add frontend

This commit is contained in:
voltsrage
2026-06-19 17:42:09 +08:00
parent 49973271e5
commit 5f69ce1a95
53 changed files with 6591 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
import { api } from './client'
export function fetchAlerts(encounterId, status) {
const params = new URLSearchParams()
if (status) params.set('status', status)
return api.get(`/api/v1/encounters/${encounterId}/alerts?${params}`)
}
export function fetchAllAlerts(status) {
const params = new URLSearchParams()
if (status) params.set('status', status)
return api.get(`/api/v1/alerts?${params}`)
}
export function acknowledgeAlert(alertId, clinicianId, note) {
return api.post(`/api/v1/alerts/${alertId}/acknowledge`, { clinicianId, note })
}
export function resolveAlert(alertId) {
return api.post(`/api/v1/alerts/${alertId}/resolve`)
}
+22
View File
@@ -0,0 +1,22 @@
const BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:5270'
async function request(path, options = {}) {
const res = await fetch(`${BASE_URL}${path}`, {
headers: { 'Content-Type': 'application/json', ...options.headers },
...options,
})
const envelope = await res.json()
if (!res.ok || !envelope.success) {
const msg = envelope.error?.message ?? `API ${res.status}: ${path}`
throw new Error(msg)
}
return envelope.data
}
export const api = {
get: (path) => request(path),
post: (path, body) => request(path, {
method: 'POST',
body: body !== undefined ? JSON.stringify(body) : undefined,
}),
}
+17
View File
@@ -0,0 +1,17 @@
import { api } from './client'
export function fetchCurrentNews2(encounterId) {
return api.get(`/api/v1/encounters/${encounterId}/news2/current`)
}
export function fetchCurrentQsofa(encounterId) {
return api.get(`/api/v1/encounters/${encounterId}/qsofa/current`)
}
export function fetchSepsisBundle(encounterId) {
return api.get(`/api/v1/encounters/${encounterId}/sepsis-bundle/current`)
}
export function fetchOrders(encounterId) {
return api.get(`/api/v1/encounters/${encounterId}/orders`)
}
+24
View File
@@ -0,0 +1,24 @@
import { api } from './client'
export function fetchActiveEncounters(department) {
const params = new URLSearchParams({ status: 'ACTIVE' })
if (department) params.set('department', department)
return api.get(`/api/v1/encounters?${params}`)
}
export function fetchEncounter(id) {
return api.get(`/api/v1/encounters/${id}`)
}
export async function fetchObservations(encounterId, { limit = 50 } = {}) {
const all = []
let cursor = null
do {
const params = new URLSearchParams({ limit: String(limit) })
if (cursor) params.set('cursor', cursor)
const page = await api.get(`/api/v1/encounters/${encounterId}/observations?${params}`)
all.push(...(page.items ?? []))
cursor = page.hasMore ? page.nextCursor : null
} while (cursor)
return all
}
+41
View File
@@ -0,0 +1,41 @@
// Map API PascalCase alert types to display labels (e.g. WarningHeartRate → WARNING_HEART_RATE)
export function alertTypeLabel(type) {
if (!type) return ''
return type.replace(/([A-Z])/g, '_$1').slice(1).toUpperCase()
}
export function alertStatusToApiFilter(status) {
const map = {
Open: 'OPEN',
Acknowledged: 'ACKNOWLEDGED',
Resolved: 'RESOLVED',
Escalated: 'ESCALATED',
}
return map[status] ?? null
}
const OBSERVATION_LABELS = {
HEART_RATE: 'Heart Rate',
RESP_RATE: 'Respiratory Rate',
SPO2: 'SpO₂',
SYSTOLIC_BP: 'Systolic BP',
TEMP_C: 'Temperature',
AVPU: 'AVPU',
SUPPLEMENTAL_O2: 'Supplemental O₂',
WBC_K_UL: 'WBC',
}
export function observationCodeLabel(code) {
return OBSERVATION_LABELS[code] ?? code?.replace(/_/g, ' ') ?? ''
}
const BUNDLE_ELEMENT_LABELS = {
BloodCultures: 'Blood cultures',
SerumLactate: 'Serum lactate',
BroadSpectrumAntibiotics: 'Broad-spectrum antibiotics',
IvFluidResuscitation: 'IV fluid resuscitation',
}
export function bundleElementLabel(elementType) {
return BUNDLE_ELEMENT_LABELS[elementType] ?? elementType
}