mirror of
https://github.com/temetro/temetro.git
synced 2026-08-29 03:46:52 +00:00
fix: chat input clipping, AI appointments create patients, batched approval
1. Chat input toolbar was clipped in the empty state: the form's overflow-hidden made its flex min-height resolve to 0, so the vertically-centered layout squeezed it and hid the bottom toolbar (attach/add-patient/model/mic/send). Add `shrink-0` to the form; let the empty-state column scroll. 2. AI-imported appointments now create/link a patient: services.ensurePatient reuses a same-name patient or creates one (auto file number, source "ai"); appointments.createAppointment calls it when the booking has no file number, so imported people appear on the Patients page. 3. Many proposals collapse into one BatchActionPreviewCard → a review dialog with per-row remove and "Add all" (commits sequentially), instead of one Add/Discard card per record. Plus: widen the Live chart right margin so the value pill stays inside the card. Verified with `next build`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import { db } from "../db/index.js";
|
||||
import { appointments } from "../db/schema/appointments.js";
|
||||
import type { AppointmentInput } from "../lib/appointment-validation.js";
|
||||
import type { Appointment } from "../types/appointment.js";
|
||||
import * as patients from "./patients.js";
|
||||
|
||||
type AppointmentRow = typeof appointments.$inferSelect;
|
||||
|
||||
@@ -58,9 +59,16 @@ export async function createAppointment(
|
||||
userId: string,
|
||||
input: AppointmentInput,
|
||||
): Promise<Appointment> {
|
||||
// Link to a patient — creating one when the booking has no file number (e.g.
|
||||
// an AI-imported appointment), so the person shows up on the Patients page.
|
||||
const fileNumber = await patients.ensurePatient(orgId, userId, {
|
||||
fileNumber: input.fileNumber,
|
||||
name: input.name,
|
||||
initials: input.initials,
|
||||
});
|
||||
const [row] = await db
|
||||
.insert(appointments)
|
||||
.values(columns(orgId, input, userId))
|
||||
.values(columns(orgId, { ...input, fileNumber }, userId))
|
||||
.returning();
|
||||
return toAppointment(row!);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,10 @@ import {
|
||||
problems,
|
||||
} from "../db/schema/patients.js";
|
||||
import { HttpError } from "../lib/http-error.js";
|
||||
import type { PatientInput } from "../lib/patient-validation.js";
|
||||
import {
|
||||
patientInputSchema,
|
||||
type PatientInput,
|
||||
} from "../lib/patient-validation.js";
|
||||
import type {
|
||||
Allergy,
|
||||
Encounter,
|
||||
@@ -314,6 +317,40 @@ export async function generateFileNumber(orgId: string): Promise<string> {
|
||||
return String(Number(r?.max ?? 9999) + 1);
|
||||
}
|
||||
|
||||
// Resolve the file number to attach a denormalized record (e.g. an appointment)
|
||||
// to a patient. With a file number, use it as-is. Without one (AI-imported rows),
|
||||
// reuse an existing same-name patient when present — deduping repeat rows and
|
||||
// re-imports — otherwise create a minimal patient (auto file number, source "ai")
|
||||
// so they appear on the Patients page. Returns the file number to link.
|
||||
export async function ensurePatient(
|
||||
orgId: string,
|
||||
userId: string,
|
||||
patient: { fileNumber: string; name: string; initials: string },
|
||||
): Promise<string> {
|
||||
if (patient.fileNumber) return patient.fileNumber;
|
||||
const [existing] = await db
|
||||
.select({ fileNumber: patients.fileNumber })
|
||||
.from(patients)
|
||||
.where(
|
||||
and(
|
||||
eq(patients.organizationId, orgId),
|
||||
eq(patients.name, patient.name),
|
||||
),
|
||||
)
|
||||
.limit(1);
|
||||
if (existing) return existing.fileNumber;
|
||||
const created = await createPatient(
|
||||
orgId,
|
||||
userId,
|
||||
patientInputSchema.parse({
|
||||
name: patient.name,
|
||||
initials: patient.initials,
|
||||
source: "ai",
|
||||
}),
|
||||
);
|
||||
return created.fileNumber;
|
||||
}
|
||||
|
||||
export async function listPatients(
|
||||
orgId: string,
|
||||
demographicsOnly = false,
|
||||
|
||||
Reference in New Issue
Block a user