fix: tighten real operations data paths
This commit is contained in:
203
modules/core/server/operations.ts
Normal file
203
modules/core/server/operations.ts
Normal file
@@ -0,0 +1,203 @@
|
||||
import { and, desc, eq, isNull, or, sql } from "drizzle-orm";
|
||||
|
||||
import { getDatabase } from "@/modules/core/server/db";
|
||||
import { admissions, beds, elders, rooms, systemIncidents } from "@/modules/core/server/schema";
|
||||
import type { Admission, FacilityBed, Room, SystemIncident } from "@/modules/core/types";
|
||||
import type { Elder } from "@/modules/elders/types";
|
||||
|
||||
function iso(value: Date): string {
|
||||
return value.toISOString();
|
||||
}
|
||||
|
||||
type ActiveBedLabel = {
|
||||
bed: string;
|
||||
bedId: string;
|
||||
room: string;
|
||||
};
|
||||
|
||||
function toElder(row: typeof elders.$inferSelect, activeBed?: ActiveBedLabel): Elder {
|
||||
return {
|
||||
id: row.id,
|
||||
organizationId: row.organizationId,
|
||||
name: row.name,
|
||||
gender: row.gender,
|
||||
age: row.age,
|
||||
careLevel: row.careLevel,
|
||||
room: activeBed?.room ?? "",
|
||||
bed: activeBed?.bed ?? "",
|
||||
bedId: activeBed?.bedId,
|
||||
status: row.status,
|
||||
primaryContact: row.primaryContact,
|
||||
phone: row.phone,
|
||||
medicalNotes: row.medicalNotes,
|
||||
createdAt: iso(row.createdAt),
|
||||
updatedAt: iso(row.updatedAt),
|
||||
};
|
||||
}
|
||||
|
||||
export async function listOperationalElders(organizationId?: string): Promise<Elder[]> {
|
||||
const database = getDatabase();
|
||||
const [elderRows, activeAdmissionRows] = await Promise.all([
|
||||
database
|
||||
.select()
|
||||
.from(elders)
|
||||
.where(organizationId ? eq(elders.organizationId, organizationId) : sql`true`)
|
||||
.orderBy(desc(elders.createdAt)),
|
||||
database
|
||||
.select({
|
||||
bedCode: beds.code,
|
||||
bedId: admissions.bedId,
|
||||
elderId: admissions.elderId,
|
||||
roomName: rooms.name,
|
||||
})
|
||||
.from(admissions)
|
||||
.innerJoin(beds, eq(beds.id, admissions.bedId))
|
||||
.innerJoin(rooms, eq(rooms.id, beds.roomId))
|
||||
.where(
|
||||
organizationId
|
||||
? and(eq(admissions.organizationId, organizationId), eq(admissions.status, "active"))
|
||||
: eq(admissions.status, "active"),
|
||||
)
|
||||
.orderBy(desc(admissions.admittedAt)),
|
||||
]);
|
||||
const bedLabelByElderId = new Map<string, ActiveBedLabel>();
|
||||
for (const row of activeAdmissionRows) {
|
||||
if (bedLabelByElderId.has(row.elderId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bedLabelByElderId.set(row.elderId, {
|
||||
room: row.roomName,
|
||||
bed: row.bedCode,
|
||||
bedId: row.bedId,
|
||||
});
|
||||
}
|
||||
|
||||
return elderRows.map((row) => toElder(row, bedLabelByElderId.get(row.id)));
|
||||
}
|
||||
|
||||
export async function listOperationalRooms(organizationId?: string): Promise<Room[]> {
|
||||
const database = getDatabase();
|
||||
const [roomRows, bedRows] = await Promise.all([
|
||||
database
|
||||
.select()
|
||||
.from(rooms)
|
||||
.where(organizationId ? eq(rooms.organizationId, organizationId) : sql`true`)
|
||||
.orderBy(desc(rooms.createdAt)),
|
||||
database
|
||||
.select()
|
||||
.from(beds)
|
||||
.where(organizationId ? eq(beds.organizationId, organizationId) : sql`true`),
|
||||
]);
|
||||
const bedCounts = new Map<string, { occupied: number; total: number }>();
|
||||
for (const bed of bedRows) {
|
||||
const current = bedCounts.get(bed.roomId) ?? { occupied: 0, total: 0 };
|
||||
current.total += 1;
|
||||
if (bed.status === "occupied") {
|
||||
current.occupied += 1;
|
||||
}
|
||||
bedCounts.set(bed.roomId, current);
|
||||
}
|
||||
|
||||
return roomRows.map((room) => {
|
||||
const counts = bedCounts.get(room.id) ?? { occupied: 0, total: 0 };
|
||||
return {
|
||||
id: room.id,
|
||||
organizationId: room.organizationId,
|
||||
name: room.name,
|
||||
code: room.code,
|
||||
capacity: room.capacity,
|
||||
status: room.status,
|
||||
bedCount: counts.total,
|
||||
occupiedBedCount: counts.occupied,
|
||||
createdAt: iso(room.createdAt),
|
||||
updatedAt: iso(room.updatedAt),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export async function listOperationalBeds(organizationId?: string): Promise<FacilityBed[]> {
|
||||
const database = getDatabase();
|
||||
const bedRows = await database
|
||||
.select({
|
||||
bed: beds,
|
||||
currentElderName: sql<string | null>`(
|
||||
select ${elders.name}
|
||||
from ${admissions}
|
||||
inner join ${elders} on ${elders.id} = ${admissions.elderId}
|
||||
where ${admissions.bedId} = ${beds.id} and ${admissions.status} = 'active'
|
||||
order by ${admissions.admittedAt} desc
|
||||
limit 1
|
||||
)`,
|
||||
roomName: rooms.name,
|
||||
})
|
||||
.from(beds)
|
||||
.innerJoin(rooms, eq(rooms.id, beds.roomId))
|
||||
.where(organizationId ? eq(beds.organizationId, organizationId) : sql`true`)
|
||||
.orderBy(desc(beds.createdAt));
|
||||
|
||||
return bedRows.map((row) => ({
|
||||
id: row.bed.id,
|
||||
organizationId: row.bed.organizationId,
|
||||
roomId: row.bed.roomId,
|
||||
roomName: row.roomName,
|
||||
code: row.bed.code,
|
||||
status: row.bed.status,
|
||||
notes: row.bed.notes,
|
||||
currentElderName: row.currentElderName ?? undefined,
|
||||
createdAt: iso(row.bed.createdAt),
|
||||
updatedAt: iso(row.bed.updatedAt),
|
||||
}));
|
||||
}
|
||||
|
||||
export async function listOperationalAdmissions(organizationId?: string): Promise<Admission[]> {
|
||||
const database = getDatabase();
|
||||
const admissionRows = await database
|
||||
.select({
|
||||
admission: admissions,
|
||||
bedCode: beds.code,
|
||||
elderName: elders.name,
|
||||
roomName: rooms.name,
|
||||
})
|
||||
.from(admissions)
|
||||
.innerJoin(elders, eq(elders.id, admissions.elderId))
|
||||
.innerJoin(beds, eq(beds.id, admissions.bedId))
|
||||
.innerJoin(rooms, eq(rooms.id, beds.roomId))
|
||||
.where(organizationId ? eq(admissions.organizationId, organizationId) : sql`true`)
|
||||
.orderBy(desc(admissions.admittedAt));
|
||||
|
||||
return admissionRows.map((row) => ({
|
||||
id: row.admission.id,
|
||||
organizationId: row.admission.organizationId,
|
||||
elderId: row.admission.elderId,
|
||||
elderName: row.elderName,
|
||||
bedId: row.admission.bedId,
|
||||
bedCode: row.bedCode,
|
||||
roomName: row.roomName,
|
||||
status: row.admission.status,
|
||||
admittedAt: iso(row.admission.admittedAt),
|
||||
dischargedAt: row.admission.dischargedAt ? iso(row.admission.dischargedAt) : undefined,
|
||||
notes: row.admission.notes,
|
||||
}));
|
||||
}
|
||||
|
||||
export async function listOperationalIncidents(organizationId?: string): Promise<SystemIncident[]> {
|
||||
const database = getDatabase();
|
||||
const incidentRows = await database
|
||||
.select()
|
||||
.from(systemIncidents)
|
||||
.where(organizationId ? or(isNull(systemIncidents.organizationId), eq(systemIncidents.organizationId, organizationId)) : sql`true`)
|
||||
.orderBy(desc(systemIncidents.createdAt));
|
||||
|
||||
return incidentRows.map((incident) => ({
|
||||
id: incident.id,
|
||||
organizationId: incident.organizationId ?? undefined,
|
||||
severity: incident.severity,
|
||||
status: incident.status,
|
||||
title: incident.title,
|
||||
description: incident.description,
|
||||
source: incident.source,
|
||||
createdAt: iso(incident.createdAt),
|
||||
updatedAt: iso(incident.updatedAt),
|
||||
}));
|
||||
}
|
||||
Reference in New Issue
Block a user