diff --git a/src/client/components/admin-calendar.tsx b/src/client/components/admin-calendar.tsx index 4a88e5d..a5a85ab 100644 --- a/src/client/components/admin-calendar.tsx +++ b/src/client/components/admin-calendar.tsx @@ -73,7 +73,7 @@ export function AdminCalendar() { ); // Propose reschedule mutation - const { mutate: proposeReschedule, isLoading: isProposingReschedule } = useMutation( + const { mutate: proposeReschedule, isPending: isProposingReschedule } = useMutation( queryClient.bookings.proposeReschedule.mutationOptions() ); diff --git a/src/client/components/booking-status-page.tsx b/src/client/components/booking-status-page.tsx index b4ee7f7..d366c1b 100644 --- a/src/client/components/booking-status-page.tsx +++ b/src/client/components/booking-status-page.tsx @@ -71,13 +71,20 @@ export default function BookingStatusPage({ token }: BookingStatusPageProps) { ); // Try fetching reschedule proposal if booking not found or error - useQuery({ + const rescheduleQuery = useQuery({ ...queryClient.cancellation.getRescheduleProposal.queryOptions({ input: { token } }), enabled: !!token && (!!bookingError || !booking), - onSuccess: (data: any) => setRescheduleProposal(data), - onError: () => setRescheduleProposal(null), }); + // Handle reschedule proposal data + useEffect(() => { + if (rescheduleQuery.data) { + setRescheduleProposal(rescheduleQuery.data); + } else if (rescheduleQuery.error) { + setRescheduleProposal(null); + } + }, [rescheduleQuery.data, rescheduleQuery.error]); + // Cancellation mutation const cancelMutation = useMutation({ ...queryClient.cancellation.cancelByToken.mutationOptions(), @@ -387,7 +394,7 @@ export default function BookingStatusPage({ token }: BookingStatusPageProps) { ); } - const statusInfo = getStatusInfo(booking.status); + const statusInfo = getStatusInfo(booking?.status || "pending"); return (
@@ -430,22 +437,22 @@ export default function BookingStatusPage({ token }: BookingStatusPageProps) {

Status: {statusInfo.label}

- {booking.status === "pending" && ( + {booking?.status === "pending" && (

Wir haben deine Terminanfrage erhalten und werden sie in Kürze prüfen. Du erhältst eine E-Mail, sobald dein Termin bestätigt wurde.

)} - {booking.status === "confirmed" && ( + {booking?.status === "confirmed" && (

Dein Termin wurde bestätigt! Wir freuen uns auf dich. Du hast eine Bestätigungs-E-Mail mit Kalendereintrag erhalten.

)} - {booking.status === "cancelled" && ( + {booking?.status === "cancelled" && (

Dieser Termin wurde storniert. Du kannst jederzeit einen neuen Termin buchen.

)} - {booking.status === "completed" && ( + {booking?.status === "completed" && (

Dieser Termin wurde erfolgreich abgeschlossen. Vielen Dank für deinen Besuch!

@@ -465,27 +472,27 @@ export default function BookingStatusPage({ token }: BookingStatusPageProps) {
Datum: - {booking.formattedDate} + {booking?.formattedDate}
Uhrzeit: - {booking.appointmentTime} Uhr + {booking?.appointmentTime} Uhr
Behandlung: - {booking.treatmentName} + {booking?.treatmentName}
Dauer: - {booking.treatmentDuration} Minuten + {booking?.treatmentDuration} Minuten
- {booking.treatmentPrice > 0 && ( + {booking?.treatmentPrice && booking.treatmentPrice > 0 && (
Preis: {booking.treatmentPrice.toFixed(2)} €
)} - {booking.hoursUntilAppointment > 0 && booking.status !== "cancelled" && booking.status !== "completed" && ( + {booking?.hoursUntilAppointment && booking.hoursUntilAppointment > 0 && booking.status !== "cancelled" && booking.status !== "completed" && (
Verbleibende Zeit: @@ -507,18 +514,18 @@ export default function BookingStatusPage({ token }: BookingStatusPageProps) {
Name: - {booking.customerName} + {booking?.customerName}
E-Mail: - {booking.customerEmail || '—'} + {booking?.customerEmail || '—'}
Telefon: - {booking.customerPhone || '—'} + {booking?.customerPhone || '—'}
- {booking.notes && ( + {booking?.notes && (

Notizen:

{booking.notes}

@@ -527,7 +534,7 @@ export default function BookingStatusPage({ token }: BookingStatusPageProps) {
{/* Cancellation Section */} - {booking.canCancel && !cancellationResult?.success && ( + {booking?.canCancel && !cancellationResult?.success && (

@@ -587,7 +594,7 @@ export default function BookingStatusPage({ token }: BookingStatusPageProps) {

)} - {!booking.canCancel && booking.status !== "cancelled" && booking.status !== "completed" && ( + {!booking?.canCancel && booking?.status !== "cancelled" && booking?.status !== "completed" && (

ℹ️ Stornierungsfrist abgelaufen: Dieser Termin liegt weniger als {parseInt(process.env.MIN_STORNO_TIMESPAN || "24")} Stunden in der Zukunft und kann nicht mehr online storniert werden. Bitte kontaktiere uns direkt. diff --git a/src/server/rpc/bookings.ts b/src/server/rpc/bookings.ts index f3558c1..347b04d 100644 --- a/src/server/rpc/bookings.ts +++ b/src/server/rpc/bookings.ts @@ -612,13 +612,12 @@ const createManual = os cancellationUrl: bookingUrl }); - await sendEmailWithAGBAndCalendar({ - to: input.customerEmail, - subject: "Dein Termin wurde bestätigt - AGB im Anhang", - text: `Hallo ${input.customerName},\n\nwir haben deinen Termin am ${formattedDate} um ${input.appointmentTime} bestätigt.\n\nWichtiger Hinweis: Die Allgemeinen Geschäftsbedingungen (AGB) findest du im Anhang dieser E-Mail. Bitte lies sie vor deinem Termin durch.\n\nTermin-Status ansehen und verwalten: ${bookingUrl}\nFalls du den Termin stornieren möchtest, kannst du das über den obigen Link tun.\n\nRechtliche Informationen: ${generateUrl('/legal')}\nZur Website: ${homepageUrl}\n\nBis bald!\nStargirlnails Kiel`, - html, - bcc: process.env.ADMIN_EMAIL ? [process.env.ADMIN_EMAIL] : undefined, - }, { + await sendEmailWithAGBAndCalendar({ + to: input.customerEmail!, + subject: "Dein Termin wurde bestätigt - AGB im Anhang", + text: `Hallo ${input.customerName},\n\nwir haben deinen Termin am ${formattedDate} um ${input.appointmentTime} bestätigt.\n\nWichtiger Hinweis: Die Allgemeinen Geschäftsbedingungen (AGB) findest du im Anhang dieser E-Mail. Bitte lies sie vor deinem Termin durch.\n\nTermin-Status ansehen und verwalten: ${bookingUrl}\nFalls du den Termin stornieren möchtest, kannst du das über den obigen Link tun.\n\nRechtliche Informationen: ${generateUrl('/legal')}\nZur Website: ${homepageUrl}\n\nBis bald!\nStargirlnails Kiel`, + html, + }, { date: input.appointmentDate, time: input.appointmentTime, durationMinutes: treatment.duration, diff --git a/src/server/rpc/cancellation.ts b/src/server/rpc/cancellation.ts index e1c4f5f..44d2d4d 100644 --- a/src/server/rpc/cancellation.ts +++ b/src/server/rpc/cancellation.ts @@ -343,7 +343,17 @@ export const router = { } // Get booking details for each expired proposal - const expiredDetails = []; + const expiredDetails: Array<{ + customerName: string; + originalDate: string; + originalTime: string; + proposedDate: string; + proposedTime: string; + treatmentName: string; + customerEmail?: string; + customerPhone?: string; + expiredAt: string; + }> = []; for (const proposal of expiredProposals) { const booking = await bookingsKV.getItem(proposal.bookingId); if (booking) { @@ -353,8 +363,8 @@ export const router = { customerName: booking.customerName, originalDate: proposal.originalDate || booking.appointmentDate, originalTime: proposal.originalTime || booking.appointmentTime, - proposedDate: proposal.proposedDate, - proposedTime: proposal.proposedTime, + proposedDate: proposal.proposedDate!, + proposedTime: proposal.proposedTime!, treatmentName: treatment?.name || "Unbekannte Behandlung", customerEmail: booking.customerEmail, customerPhone: booking.customerPhone,