diff --git a/src/client/components/admin-calendar.tsx b/src/client/components/admin-calendar.tsx index e6bc88d..148fb70 100644 --- a/src/client/components/admin-calendar.tsx +++ b/src/client/components/admin-calendar.tsx @@ -47,7 +47,7 @@ export function AdminCalendar() { ...queryClient.recurringAvailability.getAvailableTimes.queryOptions({ input: { date: createFormData.appointmentDate, - treatmentId: createFormData.treatmentId + treatmentIds: createFormData.treatmentId ? [createFormData.treatmentId] : [] } }), enabled: !!createFormData.appointmentDate && !!createFormData.treatmentId @@ -58,7 +58,16 @@ export function AdminCalendar() { ...queryClient.recurringAvailability.getAvailableTimes.queryOptions({ input: { date: rescheduleFormData.appointmentDate, - treatmentId: (showRescheduleModal ? bookings?.find(b => b.id === showRescheduleModal)?.treatmentId : '') || '' + treatmentIds: (() => { + const booking = showRescheduleModal ? bookings?.find(b => b.id === showRescheduleModal) : null; + if (!booking) return []; + // Use new treatments array if available + if (booking.treatments && Array.isArray(booking.treatments) && booking.treatments.length > 0) { + return booking.treatments.map((t: any) => t.id); + } + // Fallback to deprecated treatmentId for backward compatibility + return booking.treatmentId ? [booking.treatmentId] : []; + })() } }), enabled: !!showRescheduleModal && !!rescheduleFormData.appointmentDate @@ -86,8 +95,16 @@ export function AdminCalendar() { queryClient.bookings.generateCalDAVToken.mutationOptions() ); - const getTreatmentName = (treatmentId: string) => { - return treatments?.find(t => t.id === treatmentId)?.name || "Unbekannte Behandlung"; + const getTreatmentNames = (booking: any) => { + // Handle new treatments array structure + if (booking.treatments && Array.isArray(booking.treatments) && booking.treatments.length > 0) { + return booking.treatments.map((t: any) => t.name).join(", "); + } + // Fallback to deprecated treatmentId for backward compatibility + if (booking.treatmentId) { + return treatments?.find(t => t.id === booking.treatmentId)?.name || "Unbekannte Behandlung"; + } + return "Keine Behandlung"; }; const getStatusColor = (status: string) => { @@ -219,9 +236,29 @@ export function AdminCalendar() { const sessionId = localStorage.getItem('sessionId'); if (!sessionId) return; + // Convert treatmentId to treatments array + const selectedTreatment = treatments?.find(t => t.id === createFormData.treatmentId); + if (!selectedTreatment) { + setCreateError('Bitte wähle eine Behandlung aus.'); + return; + } + + const treatmentsArray = [{ + id: selectedTreatment.id, + name: selectedTreatment.name, + duration: selectedTreatment.duration, + price: selectedTreatment.price + }]; + createManualBooking({ sessionId, - ...createFormData + treatments: treatmentsArray, + customerName: createFormData.customerName, + appointmentDate: createFormData.appointmentDate, + appointmentTime: createFormData.appointmentTime, + customerEmail: createFormData.customerEmail, + customerPhone: createFormData.customerPhone, + notes: createFormData.notes }, { onSuccess: () => { setShowCreateModal(false); @@ -469,7 +506,7 @@ export function AdminCalendar() {
{booking.appointmentTime}
{booking.customerName}
@@ -526,7 +563,7 @@ export function AdminCalendar() {
- Behandlung: {getTreatmentName(booking.treatmentId)} + Behandlung: {getTreatmentNames(booking)}
Uhrzeit: {booking.appointmentTime} @@ -842,7 +879,7 @@ export function AdminCalendar() { {(() => { const booking = bookings?.find(b => b.id === showRescheduleModal); - const treatmentName = booking ? getTreatmentName(booking.treatmentId) : ''; + const treatmentName = booking ? getTreatmentNames(booking) : ''; return booking ? (
Kunde: {booking.customerName}
diff --git a/src/client/components/booking-status-page.tsx b/src/client/components/booking-status-page.tsx index 5fe4189..5c7f9c1 100644 --- a/src/client/components/booking-status-page.tsx +++ b/src/client/components/booking-status-page.tsx @@ -45,7 +45,7 @@ interface RescheduleProposalDetails { totalPrice: number; }; original: { date: string; time: string }; - proposed: { date: string; time: string }; + proposed: { date?: string; time?: string }; expiresAt: string; hoursUntilExpiry: number; isExpired: boolean; @@ -203,7 +203,7 @@ export default function BookingStatusPage({ token }: BookingStatusPageProps) { if (oneClickAction === 'accept') { const confirmAccept = window.confirm( - `Möchtest du den neuen Termin am ${rescheduleProposal.proposed.date} um ${rescheduleProposal.proposed.time} Uhr akzeptieren?` + `Möchtest du den neuen Termin am ${rescheduleProposal.proposed.date || 'TBD'} um ${rescheduleProposal.proposed.time || 'TBD'} Uhr akzeptieren?` ); if (confirmAccept) { acceptMutation.mutate({ token }); @@ -381,7 +381,7 @@ export default function BookingStatusPage({ token }: BookingStatusPageProps) {
Neuer Vorschlag
-
{rescheduleProposal.proposed.date} um {rescheduleProposal.proposed.time} Uhr
+
{rescheduleProposal.proposed.date || 'TBD'} um {rescheduleProposal.proposed.time || 'TBD'} Uhr
{rescheduleProposal.booking.treatments && rescheduleProposal.booking.treatments.length > 0 ? ( <> diff --git a/src/client/components/review-submission-page.tsx b/src/client/components/review-submission-page.tsx index 5fdab93..60814f8 100644 --- a/src/client/components/review-submission-page.tsx +++ b/src/client/components/review-submission-page.tsx @@ -139,7 +139,11 @@ export default function ReviewSubmissionPage({ token }: ReviewSubmissionPageProp
Behandlung: - {booking.treatmentName} + + {booking.treatments && booking.treatments.length > 0 + ? booking.treatments.map((t: any) => t.name).join(", ") + : "Keine Behandlung"} +
Name: diff --git a/src/server/rpc/cancellation.ts b/src/server/rpc/cancellation.ts index 44d2d4d..c518c30 100644 --- a/src/server/rpc/cancellation.ts +++ b/src/server/rpc/cancellation.ts @@ -28,7 +28,15 @@ const cancellationKV = createKV("cancellation_tokens"); // Types for booking and availability type Booking = { id: string; - treatmentId: string; + treatments: Array<{ + id: string; + name: string; + duration: number; + price: number; + }>; + // Deprecated fields for backward compatibility + treatmentId?: string; + bookedDurationMinutes?: number; customerName: string; customerEmail?: string; customerPhone?: string; @@ -120,9 +128,42 @@ const getBookingByToken = os throw new Error("Booking not found"); } - // Get treatment details - const treatmentsKV = createKV("treatments"); - const treatment = await treatmentsKV.getItem(booking.treatmentId); + // Handle treatments array + let treatments: Array<{id: string; name: string; duration: number; price: number}>; + let totalDuration: number; + let totalPrice: number; + + if (booking.treatments && booking.treatments.length > 0) { + // New bookings with treatments array + treatments = booking.treatments; + totalDuration = treatments.reduce((sum, t) => sum + t.duration, 0); + totalPrice = treatments.reduce((sum, t) => sum + t.price, 0); + } else if (booking.treatmentId) { + // Old bookings with single treatmentId (backward compatibility) + const treatmentsKV = createKV("treatments"); + const treatment = await treatmentsKV.getItem(booking.treatmentId); + + if (treatment) { + treatments = [{ + id: treatment.id, + name: treatment.name, + duration: treatment.duration, + price: treatment.price, + }]; + totalDuration = treatment.duration; + totalPrice = treatment.price; + } else { + // Fallback if treatment not found + treatments = []; + totalDuration = booking.bookedDurationMinutes || 60; + totalPrice = 0; + } + } else { + // Edge case: no treatments and no treatmentId + treatments = []; + totalDuration = 0; + totalPrice = 0; + } // Calculate if cancellation is still possible const minStornoTimespan = parseInt(process.env.MIN_STORNO_TIMESPAN || "24"); @@ -140,10 +181,9 @@ const getBookingByToken = os customerPhone: booking.customerPhone, appointmentDate: booking.appointmentDate, appointmentTime: booking.appointmentTime, - treatmentId: booking.treatmentId, - treatmentName: treatment?.name || "Unbekannte Behandlung", - treatmentDuration: treatment?.duration || 60, - treatmentPrice: treatment?.price || 0, + treatments, + totalDuration, + totalPrice, status: booking.status, notes: booking.notes, formattedDate: formatDateGerman(booking.appointmentDate), @@ -284,8 +324,42 @@ export const router = { throw new Error("Booking not found"); } - const treatmentsKV = createKV("treatments"); - const treatment = await treatmentsKV.getItem(booking.treatmentId); + // Handle treatments array + let treatments: Array<{id: string; name: string; duration: number; price: number}>; + let totalDuration: number; + let totalPrice: number; + + if (booking.treatments && booking.treatments.length > 0) { + // New bookings with treatments array + treatments = booking.treatments; + totalDuration = treatments.reduce((sum, t) => sum + t.duration, 0); + totalPrice = treatments.reduce((sum, t) => sum + t.price, 0); + } else if (booking.treatmentId) { + // Old bookings with single treatmentId (backward compatibility) + const treatmentsKV = createKV("treatments"); + const treatment = await treatmentsKV.getItem(booking.treatmentId); + + if (treatment) { + treatments = [{ + id: treatment.id, + name: treatment.name, + duration: treatment.duration, + price: treatment.price, + }]; + totalDuration = treatment.duration; + totalPrice = treatment.price; + } else { + // Fallback if treatment not found + treatments = []; + totalDuration = booking.bookedDurationMinutes || 60; + totalPrice = 0; + } + } else { + // Edge case: no treatments and no treatmentId + treatments = []; + totalDuration = 0; + totalPrice = 0; + } const now = new Date(); const isExpired = new Date(proposal.expiresAt) <= now; @@ -298,8 +372,9 @@ export const router = { customerEmail: booking.customerEmail, customerPhone: booking.customerPhone, status: booking.status, - treatmentId: booking.treatmentId, - treatmentName: treatment?.name || "Unbekannte Behandlung", + treatments, + totalDuration, + totalPrice, }, original: { date: proposal.originalDate || booking.appointmentDate, @@ -358,14 +433,22 @@ export const router = { const booking = await bookingsKV.getItem(proposal.bookingId); if (booking) { const treatmentsKV = createKV("treatments"); - const treatment = await treatmentsKV.getItem(booking.treatmentId); + // Get treatment name(s) from new treatments array or fallback to deprecated treatmentId + let treatmentName = "Unbekannte Behandlung"; + if (booking.treatments && Array.isArray(booking.treatments) && booking.treatments.length > 0) { + treatmentName = booking.treatments.map((t: any) => t.name).join(", "); + } else if (booking.treatmentId) { + const treatment = await treatmentsKV.getItem(booking.treatmentId); + treatmentName = treatment?.name || "Unbekannte Behandlung"; + } + expiredDetails.push({ customerName: booking.customerName, originalDate: proposal.originalDate || booking.appointmentDate, originalTime: proposal.originalTime || booking.appointmentTime, proposedDate: proposal.proposedDate!, proposedTime: proposal.proposedTime!, - treatmentName: treatment?.name || "Unbekannte Behandlung", + treatmentName: treatmentName, customerEmail: booking.customerEmail, customerPhone: booking.customerPhone, expiredAt: proposal.expiresAt,