fix(GRO-765): include service name in portal appointments response
- Added service JOIN to /api/portal/appointments to include name, duration,
and price fields in the service sub-object
- Changed API response shape from { upcoming, past } to { appointments: [] }
to match client expectations and simplify data flow
- Updated Appointments, ReportCards, and Dashboard components to transform
the new response format (startTime → date + time) and extract serviceName,
petName, and groomerName from nested sub-objects
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -123,7 +123,28 @@ export const AppointmentsSection: React.FC<AppointmentsSectionProps> = ({ sessio
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
const fetchedAppointments: Appointment[] = data.appointments || data || [];
|
||||
const rawAppointments: Record<string, unknown>[] = data.appointments || data || [];
|
||||
|
||||
// Transform API response (startTime) to client format (date + time)
|
||||
const fetchedAppointments: Appointment[] = rawAppointments.map((a) => {
|
||||
const start = new Date(a.startTime as string);
|
||||
const dateStr = start.toISOString().split('T')[0];
|
||||
const hours = start.getHours();
|
||||
const minutes = start.getMinutes().toString().padStart(2, '0');
|
||||
const period = hours >= 12 ? 'PM' : 'AM';
|
||||
const hour12 = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours;
|
||||
const timeStr = `${hour12}:${minutes} ${period}`;
|
||||
return {
|
||||
...a,
|
||||
date: dateStr,
|
||||
time: timeStr,
|
||||
petName: (a.pet as { name?: string })?.name,
|
||||
serviceName: (a.service as { name?: string })?.name,
|
||||
groomerName: (a.staff as { name?: string })?.name,
|
||||
duration: (a.service as { duration?: number })?.duration,
|
||||
price: (a.service as { price?: number })?.price,
|
||||
} as Appointment;
|
||||
});
|
||||
|
||||
const upcoming = fetchedAppointments.filter((appt) => isUpcoming(appt));
|
||||
const past = fetchedAppointments.filter((appt) => !isUpcoming(appt));
|
||||
|
||||
@@ -116,7 +116,24 @@ export function Dashboard({
|
||||
const invoicesData = await invoicesRes.json();
|
||||
const brandingData = await brandingRes.json();
|
||||
|
||||
setAppointments(appointmentsData.appointments || []);
|
||||
const rawAppointments: Record<string, unknown>[] = appointmentsData.appointments || [];
|
||||
const transformedAppointments = rawAppointments.map((a) => {
|
||||
const start = new Date(a.startTime as string);
|
||||
const dateStr = start.toISOString().split('T')[0];
|
||||
const hours = start.getHours();
|
||||
const minutes = start.getMinutes().toString().padStart(2, '0');
|
||||
const period = hours >= 12 ? 'PM' : 'AM';
|
||||
const hour12 = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours;
|
||||
const timeStr = `${hour12}:${minutes} ${period}`;
|
||||
return {
|
||||
...a,
|
||||
date: dateStr,
|
||||
time: timeStr,
|
||||
petName: (a.pet as { name?: string })?.name ?? '',
|
||||
serviceName: (a.service as { name?: string })?.name ?? '',
|
||||
};
|
||||
});
|
||||
setAppointments(transformedAppointments as Appointment[]);
|
||||
setPets(petsData.pets || []);
|
||||
|
||||
// Filter for pending invoices only (not "outstanding")
|
||||
|
||||
@@ -44,8 +44,25 @@ export function ReportCards({ sessionId }: Props) {
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
const allAppointments: Appointment[] = data.appointments || data || [];
|
||||
const reportCardAppointments = allAppointments.filter(
|
||||
const rawAppointments: Record<string, unknown>[] = data.appointments || data || [];
|
||||
const transformed: Appointment[] = rawAppointments.map((a) => {
|
||||
const start = new Date(a.startTime as string);
|
||||
const dateStr = start.toISOString().split('T')[0];
|
||||
const hours = start.getHours();
|
||||
const minutes = start.getMinutes().toString().padStart(2, '0');
|
||||
const period = hours >= 12 ? 'PM' : 'AM';
|
||||
const hour12 = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours;
|
||||
const timeStr = `${hour12}:${minutes} ${period}`;
|
||||
return {
|
||||
...a,
|
||||
date: dateStr,
|
||||
time: timeStr,
|
||||
petName: (a.pet as { name?: string })?.name,
|
||||
serviceName: (a.service as { name?: string })?.name,
|
||||
groomerName: (a.staff as { name?: string })?.name,
|
||||
} as Appointment;
|
||||
});
|
||||
const reportCardAppointments = transformed.filter(
|
||||
(appt) => appt.reportCardId
|
||||
);
|
||||
setAppointments(reportCardAppointments);
|
||||
|
||||
Reference in New Issue
Block a user