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,