feat(staff column order in aptmpt page)

This commit is contained in:
2026-01-28 02:25:18 +05:30
parent b8df9459ca
commit 4594a264a1
7 changed files with 244 additions and 84 deletions

View File

@@ -39,6 +39,7 @@ import {
Patient,
PatientStatus,
UpdateAppointment,
Staff as DBStaff,
} from "@repo/db/types";
import {
Popover,
@@ -61,12 +62,9 @@ interface TimeSlot {
displayTime: string;
}
interface Staff {
id: string;
name: string;
role: "doctor" | "hygienist";
type StaffWithColor = DBStaff & {
color: string;
}
};
interface ScheduledAppointment {
id?: number;
@@ -99,10 +97,10 @@ export default function AppointmentsPage() {
number | null
>(null);
const [proceduresPatientId, setProceduresPatientId] = useState<number | null>(
null
null,
);
const [proceduresPatient, setProceduresPatient] = useState<Patient | null>(
null
null,
);
const [calendarOpen, setCalendarOpen] = useState(false);
@@ -117,7 +115,7 @@ export default function AppointmentsPage() {
}>({ open: false });
const dispatch = useAppDispatch();
const batchTask = useAppSelector(
(state) => state.seleniumEligibilityBatchCheckTask
(state) => state.seleniumEligibilityBatchCheckTask,
);
const [isCheckingAllElig, setIsCheckingAllElig] = useState(false);
const [processedAppointmentIds, setProcessedAppointmentIds] = useState<
@@ -153,7 +151,7 @@ export default function AppointmentsPage() {
queryFn: async () => {
const res = await apiRequest(
"GET",
`/api/appointments/day?date=${formattedSelectedDate}`
`/api/appointments/day?date=${formattedSelectedDate}`,
);
if (!res.ok) {
throw new Error("Failed to load appointments for date");
@@ -168,7 +166,7 @@ export default function AppointmentsPage() {
const patientsFromDay = dayPayload.patients ?? [];
// Staff memebers
const { data: staffMembersRaw = [] as Staff[] } = useQuery<Staff[]>({
const { data: staffMembersRaw = [] as DBStaff[] } = useQuery<DBStaff[]>({
queryKey: ["/api/staffs/"],
queryFn: async () => {
const res = await apiRequest("GET", "/api/staffs/");
@@ -186,11 +184,18 @@ export default function AppointmentsPage() {
"bg-orange-500", // softer warm orange
];
// Assign colors cycling through the list
const staffMembers = staffMembersRaw.map((staff, index) => ({
...staff,
// Assign colors cycling through the list, and order them by display order for the page column.
color: colors[index % colors.length] || "bg-gray-400",
const orderedStaff = staffMembersRaw.filter(
(s): s is DBStaff & { displayOrder: number } =>
typeof s.displayOrder === "number" && s.displayOrder > 0,
);
orderedStaff.sort((a, b) => a.displayOrder - b.displayOrder);
const staffMembers: StaffWithColor[] = orderedStaff.map((staff, index) => ({
...staff,
color: colors[index % colors.length] ?? "bg-gray-400",
}));
// Generate time slots from 8:00 AM to 6:00 PM in 15-minute increments
@@ -245,13 +250,13 @@ export default function AppointmentsPage() {
};
sessionStorage.setItem(
"newAppointmentData",
JSON.stringify(newAppointmentData)
JSON.stringify(newAppointmentData),
);
} catch (err) {
// If sessionStorage parsing fails, overwrite with a fresh object
sessionStorage.setItem(
"newAppointmentData",
JSON.stringify({ patientId: patientId })
JSON.stringify({ patientId: patientId }),
);
}
@@ -272,7 +277,7 @@ export default function AppointmentsPage() {
const res = await apiRequest(
"POST",
"/api/appointments/upsert",
appointment
appointment,
);
return await res.json();
},
@@ -308,7 +313,7 @@ export default function AppointmentsPage() {
const res = await apiRequest(
"PUT",
`/api/appointments/${id}`,
appointment
appointment,
);
return await res.json();
},
@@ -359,7 +364,7 @@ export default function AppointmentsPage() {
// Handle appointment submission (create or update)
const handleAppointmentSubmit = (
appointmentData: InsertAppointment | UpdateAppointment
appointmentData: InsertAppointment | UpdateAppointment,
) => {
// Converts local date to exact UTC date with no offset issues
const rawDate = parseLocalDate(appointmentData.date);
@@ -479,7 +484,7 @@ export default function AppointmentsPage() {
// Handle creating a new appointment at a specific time slot and for a specific staff member
const handleCreateAppointmentAtSlot = (
timeSlot: TimeSlot,
staffId: number
staffId: number,
) => {
// Calculate end time (30 minutes after start time)
const startHour = parseInt(timeSlot.time.split(":")[0] as string);
@@ -532,7 +537,7 @@ export default function AppointmentsPage() {
try {
sessionStorage.setItem(
"newAppointmentData",
JSON.stringify(mergedAppointment)
JSON.stringify(mergedAppointment),
);
} catch (e) {
// ignore sessionStorage write failures
@@ -550,7 +555,7 @@ export default function AppointmentsPage() {
const handleMoveAppointment = (
appointmentId: number,
newTimeSlot: TimeSlot,
newStaffId: number
newStaffId: number,
) => {
const appointment = appointments.find((a) => a.id === appointmentId);
if (!appointment) return;
@@ -603,7 +608,7 @@ export default function AppointmentsPage() {
staff,
}: {
appointment: ScheduledAppointment;
staff: Staff;
staff: StaffWithColor;
}) {
const [{ isDragging }, drag] = useDrag(() => ({
type: ItemTypes.APPOINTMENT,
@@ -624,7 +629,7 @@ export default function AppointmentsPage() {
// Only allow edit on click if we're not dragging
if (!isDragging) {
const fullAppointment = appointments.find(
(a) => a.id === appointment.id
(a) => a.id === appointment.id,
);
if (fullAppointment) {
e.stopPropagation();
@@ -659,7 +664,7 @@ export default function AppointmentsPage() {
timeSlot: TimeSlot;
staffId: number;
appointment: ScheduledAppointment | undefined;
staff: Staff;
staff: StaffWithColor;
}) {
const [{ isOver, canDrop }, drop] = useDrop(() => ({
accept: ItemTypes.APPOINTMENT,
@@ -700,13 +705,13 @@ export default function AppointmentsPage() {
// appointment page — update these handlers
const handleCheckEligibility = (appointmentId: number) => {
setLocation(
`/insurance-status?appointmentId=${appointmentId}&action=eligibility`
`/insurance-status?appointmentId=${appointmentId}&action=eligibility`,
);
};
const handleCheckClaimStatus = (appointmentId: number) => {
setLocation(
`/insurance-status?appointmentId=${appointmentId}&action=claim`
`/insurance-status?appointmentId=${appointmentId}&action=claim`,
);
};
@@ -720,7 +725,7 @@ export default function AppointmentsPage() {
const handleChartPlan = (appointmentId: number) => {
console.log(
`Viewing chart/treatment plan for appointment: ${appointmentId}`
`Viewing chart/treatment plan for appointment: ${appointmentId}`,
);
};
@@ -745,7 +750,7 @@ export default function AppointmentsPage() {
setTaskStatus({
status: "pending",
message: `Checking eligibility for appointments on ${dateParam}...`,
})
}),
);
setIsCheckingAllElig(true);
@@ -755,7 +760,7 @@ export default function AppointmentsPage() {
const res = await apiRequest(
"POST",
`/api/insurance-status/appointments/check-all-eligibilities?date=${dateParam}`,
{}
{},
);
// read body for all cases so we can show per-appointment info
@@ -773,7 +778,7 @@ export default function AppointmentsPage() {
setTaskStatus({
status: "error",
message: `Batch eligibility failed: ${errMsg}`,
})
}),
);
toast({
title: "Batch check failed",
@@ -851,7 +856,7 @@ export default function AppointmentsPage() {
setTaskStatus({
status: skippedCount > 0 ? "error" : "success",
message: `Batch processed ${results.length} appointments — success: ${successCount}, warnings: ${warningCount}, skipped: ${skippedCount}.`,
})
}),
);
// also show final toast summary
@@ -866,7 +871,7 @@ export default function AppointmentsPage() {
setTaskStatus({
status: "error",
message: `Batch eligibility error: ${err?.message ?? String(err)}`,
})
}),
);
toast({
title: "Batch check failed",
@@ -970,7 +975,7 @@ export default function AppointmentsPage() {
<Item
onClick={({ props }) => {
const fullAppointment = appointments.find(
(a) => a.id === props.appointmentId
(a) => a.id === props.appointmentId,
);
if (fullAppointment) {
handleEditAppointment(fullAppointment);
@@ -1148,7 +1153,7 @@ export default function AppointmentsPage() {
staffId={Number(staff.id)}
appointment={getAppointmentAtSlot(
timeSlot,
Number(staff.id)
Number(staff.id),
)}
staff={staff}
/>