feat(patient-status schema updated)- applied changes

This commit is contained in:
2025-10-30 21:13:33 +05:30
parent 103142b2d3
commit 54f83ab398
9 changed files with 146 additions and 63 deletions

View File

@@ -77,7 +77,6 @@ async function createOrUpdatePatientByInsuranceId(options: {
gender: "", gender: "",
phone: "", phone: "",
userId, userId,
status: "inactive",
insuranceId, insuranceId,
}; };
@@ -219,7 +218,7 @@ router.post(
if (patient && patient.id !== undefined) { if (patient && patient.id !== undefined) {
const newStatus = const newStatus =
seleniumResult.eligibility === "Y" ? "active" : "inactive"; seleniumResult.eligibility === "Y" ? "ACTIVE" : "INACTIVE";
await storage.updatePatient(patient.id, { status: newStatus }); await storage.updatePatient(patient.id, { status: newStatus });
outputResult.patientUpdateStatus = `Patient status updated to ${newStatus}`; outputResult.patientUpdateStatus = `Patient status updated to ${newStatus}`;
@@ -647,7 +646,7 @@ router.post(
// Update patient status based on seleniumResult.eligibility // Update patient status based on seleniumResult.eligibility
const newStatus = const newStatus =
seleniumResult?.eligibility === "Y" ? "active" : "inactive"; seleniumResult?.eligibility === "Y" ? "ACTIVE" : "INACTIVE";
await storage.updatePatient(updatedPatient.id, { status: newStatus }); await storage.updatePatient(updatedPatient.id, { status: newStatus });
resultItem.patientUpdateStatus = `Patient status updated to ${newStatus}`; resultItem.patientUpdateStatus = `Patient status updated to ${newStatus}`;

View File

@@ -24,11 +24,14 @@ import {
InsertPatient, InsertPatient,
insertPatientSchema, insertPatientSchema,
Patient, Patient,
PatientStatus,
patientStatusOptions,
UpdatePatient, UpdatePatient,
updatePatientSchema, updatePatientSchema,
} from "@repo/db/types"; } from "@repo/db/types";
import { z } from "zod"; import { z } from "zod";
import { DateInputField } from "@/components/ui/dateInputField"; import { DateInputField } from "@/components/ui/dateInputField";
import { PatientStatusSchema } from "@repo/db/usedSchemas";
interface PatientFormProps { interface PatientFormProps {
patient?: Patient; patient?: Patient;
@@ -84,7 +87,7 @@ export const PatientForm = forwardRef<PatientFormRef, PatientFormProps>(
policyHolder: "", policyHolder: "",
allergies: "", allergies: "",
medicalConditions: "", medicalConditions: "",
status: "active", status: "UNKNOWN",
userId: user?.id, userId: user?.id,
}; };
}, [isEditing, patient, extractedInfo, user?.id]); }, [isEditing, patient, extractedInfo, user?.id]);
@@ -297,13 +300,21 @@ export const PatientForm = forwardRef<PatientFormRef, PatientFormProps>(
<FormField <FormField
control={form.control} control={form.control}
name="status" name="status"
render={({ field }) => ( render={({ field }) => {
const options = Object.values(
patientStatusOptions
) as PatientStatus[]; // ['ACTIVE','INACTIVE','UNKNOWN']
const toLabel = (v: PatientStatus) =>
v[0] + v.slice(1).toLowerCase(); // ACTIVE -> Active
return (
<FormItem> <FormItem>
<FormLabel>Status *</FormLabel> <FormLabel>Status *</FormLabel>
<Select <Select
onValueChange={field.onChange} value={(field.value as PatientStatus) ?? "UNKNOWN"}
value={field.value} onValueChange={(v) =>
defaultValue="active" field.onChange(v as PatientStatus)
}
> >
<FormControl> <FormControl>
<SelectTrigger> <SelectTrigger>
@@ -311,13 +322,17 @@ export const PatientForm = forwardRef<PatientFormRef, PatientFormProps>(
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent> <SelectContent>
<SelectItem value="active">Active</SelectItem> {options.map((v) => (
<SelectItem value="inactive">Inactive</SelectItem> <SelectItem key={v} value={v}>
{toLabel(v)}
</SelectItem>
))}
</SelectContent> </SelectContent>
</Select> </Select>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
)} );
}}
/> />
<FormField <FormField

View File

@@ -39,6 +39,7 @@ import { Checkbox } from "@/components/ui/checkbox";
import { formatDateToHumanReadable } from "@/utils/dateUtils"; import { formatDateToHumanReadable } from "@/utils/dateUtils";
import { Patient, UpdatePatient } from "@repo/db/types"; import { Patient, UpdatePatient } from "@repo/db/types";
import { PatientFinancialsModal } from "./patient-financial-modal"; import { PatientFinancialsModal } from "./patient-financial-modal";
import { getPageNumbers } from "@/utils/pageNumberGenerator";
interface PatientApiResponse { interface PatientApiResponse {
patients: Patient[]; patients: Patient[];
@@ -309,25 +310,6 @@ export function PatientTable({
return colorClasses[id % colorClasses.length]; return colorClasses[id % colorClasses.length];
}; };
function getPageNumbers(current: number, total: number): (number | "...")[] {
const delta = 2;
const range: (number | "...")[] = [];
const left = Math.max(2, current - delta);
const right = Math.min(total - 1, current + delta);
range.push(1);
if (left > 2) range.push("...");
for (let i = left; i <= right; i++) {
range.push(i);
}
if (right < total - 1) range.push("...");
if (total > 1) range.push(total);
return range;
}
return ( return (
<div className="bg-white shadow rounded-lg overflow-hidden"> <div className="bg-white shadow rounded-lg overflow-hidden">
<div className="overflow-x-auto"> <div className="overflow-x-auto">
@@ -440,18 +422,26 @@ export function PatientTable({
</TableCell> </TableCell>
<TableCell> <TableCell>
<div className="col-span-1"> <div className="col-span-1">
<span {patient.status === "ACTIVE" && (
className={cn( <span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
"inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium", Active
patient.status === "active"
? "bg-green-100 text-green-800"
: "bg-red-100 text-red-800"
)}
>
{patient.status === "active" ? "Active" : "Inactive"}
</span> </span>
)}
{patient.status === "INACTIVE" && (
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800">
Inactive
</span>
)}
{patient.status === "UNKNOWN" && (
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-gray-200 text-gray-700">
Unknown
</span>
)}
</div> </div>
</TableCell> </TableCell>
<TableCell className="text-right"> <TableCell className="text-right">
<div className="flex justify-end space-x-2"> <div className="flex justify-end space-x-2">
{allowDelete && ( {allowDelete && (
@@ -573,15 +563,18 @@ export function PatientTable({
<p> <p>
<span className="text-gray-500">Status:</span>{" "} <span className="text-gray-500">Status:</span>{" "}
<span <span
className={`${ className={cn(
currentPatient.status === "active" currentPatient.status === "ACTIVE"
? "text-green-600" ? "text-green-600"
: "text-red-600" : currentPatient.status === "INACTIVE"
} font-medium`} ? "text-red-600"
: "text-gray-600", // UNKNOWN or fallback
"font-medium"
)}
> >
{currentPatient.status {currentPatient.status
? currentPatient.status.charAt(0).toUpperCase() + ? currentPatient.status.charAt(0).toUpperCase() +
currentPatient.status.slice(1) currentPatient.status.slice(1).toLowerCase()
: "Unknown"} : "Unknown"}
</span> </span>
</p> </p>

View File

@@ -251,7 +251,6 @@ export default function InsuranceStatusPage() {
gender: "", gender: "",
phone: "", phone: "",
userId: user?.id ?? 1, userId: user?.id ?? 1,
status: "active",
insuranceId: memberId, insuranceId: memberId,
}; };
await addPatientMutation.mutateAsync(newPatient); await addPatientMutation.mutateAsync(newPatient);

View File

@@ -254,7 +254,6 @@ export default function PatientsPage() {
gender: "", gender: "",
phone: "", phone: "",
userId: user?.id ?? 1, userId: user?.id ?? 1,
status: "active",
insuranceId: data.memberId || "", insuranceId: data.memberId || "",
}; };

56
migratedb.txt Normal file
View File

@@ -0,0 +1,56 @@
1.
```
npx prisma migrate dev --create-only --name add_patient_status_enum
```
2.
```
-- Create the enum type (quoted name keeps exact case)
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'PatientStatus') THEN
CREATE TYPE "PatientStatus" AS ENUM ('ACTIVE', 'INACTIVE', 'UNKNOWN');
END IF;
END$$;
-- 1) Add new enum column (nullable for backfill)
ALTER TABLE "Patient"
ADD COLUMN IF NOT EXISTS "status_new" "PatientStatus";
-- 2) Backfill from old text column to enum (case-insensitive)
UPDATE "Patient"
SET "status_new" = CASE
WHEN "status" IS NULL THEN 'UNKNOWN'::"PatientStatus"
WHEN lower("status") = 'active' THEN 'ACTIVE'::"PatientStatus"
WHEN lower("status") = 'inactive' THEN 'INACTIVE'::"PatientStatus"
ELSE 'UNKNOWN'::"PatientStatus"
END
WHERE "status_new" IS NULL;
-- 3) Safety: ensure no NULLs remain
DO $$
DECLARE cnt INTEGER;
BEGIN
SELECT count(*) INTO cnt FROM "Patient" WHERE "status_new" IS NULL;
IF cnt > 0 THEN
RAISE EXCEPTION 'Migration abort: % rows have NULL status_new', cnt;
END IF;
END$$;
-- 4) Make new column NOT NULL and set DB default to UNKNOWN
ALTER TABLE "Patient"
ALTER COLUMN "status_new" SET DEFAULT 'UNKNOWN',
ALTER COLUMN "status_new" SET NOT NULL;
-- 5) Drop old column and rename new -> status
ALTER TABLE "Patient" DROP COLUMN IF EXISTS "status";
ALTER TABLE "Patient" RENAME COLUMN "status_new" TO "status";
```
3.
```
npx prisma migrate dev
```

View File

@@ -52,7 +52,7 @@ model Patient {
policyHolder String? policyHolder String?
allergies String? allergies String?
medicalConditions String? medicalConditions String?
status String @default("active") status PatientStatus @default(UNKNOWN)
userId Int userId Int
createdAt DateTime @default(now()) createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id]) user User @relation(fields: [userId], references: [id])
@@ -65,6 +65,12 @@ model Patient {
@@index([createdAt]) @@index([createdAt])
} }
enum PatientStatus {
ACTIVE
INACTIVE
UNKNOWN
}
model Appointment { model Appointment {
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
patientId Int patientId Int

View File

@@ -1,5 +1,9 @@
import { PatientUncheckedCreateInputObjectSchema } from "@repo/db/usedSchemas"; import {
PatientStatusSchema,
PatientUncheckedCreateInputObjectSchema,
} from "@repo/db/usedSchemas";
import { z } from "zod"; import { z } from "zod";
import { makeEnumOptions } from "../utils";
export type Patient = z.infer<typeof PatientUncheckedCreateInputObjectSchema>; export type Patient = z.infer<typeof PatientUncheckedCreateInputObjectSchema>;
@@ -28,6 +32,17 @@ export const insuranceIdSchema = z.preprocess(
.nullable() .nullable()
); );
//patient status
export type PatientStatus = z.infer<typeof PatientStatusSchema>;
// enum → select options
export const patientStatusOptions =
makeEnumOptions<
typeof PatientStatusSchema extends z.ZodTypeAny
? z.infer<typeof PatientStatusSchema>
: string
>(PatientStatusSchema);
export const insertPatientSchema = ( export const insertPatientSchema = (
PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any> PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
) )

View File

@@ -1,6 +1,7 @@
// using this, as the browser load only the required files , not whole db/shared/schemas/ files. // using this, as the browser load only the required files , not whole db/shared/schemas/ files.
export * from '../shared/schemas/objects/AppointmentUncheckedCreateInput.schema'; export * from '../shared/schemas/objects/AppointmentUncheckedCreateInput.schema';
export * from '../shared/schemas/objects/PatientUncheckedCreateInput.schema'; export * from '../shared/schemas/objects/PatientUncheckedCreateInput.schema';
export * from '../shared/schemas/enums/PatientStatus.schema';
export * from '../shared/schemas/objects/UserUncheckedCreateInput.schema'; export * from '../shared/schemas/objects/UserUncheckedCreateInput.schema';
export * from '../shared/schemas/objects/StaffUncheckedCreateInput.schema' export * from '../shared/schemas/objects/StaffUncheckedCreateInput.schema'
export * from '../shared/schemas/objects/ClaimUncheckedCreateInput.schema' export * from '../shared/schemas/objects/ClaimUncheckedCreateInput.schema'