Fix TypeScript errors for Docker build
- Fix optional chaining for booking properties - Fix useMutation isLoading to isPending - Fix email parameter types - Fix expiredDetails array typing
This commit is contained in:
@@ -73,7 +73,7 @@ export function AdminCalendar() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Propose reschedule mutation
|
// Propose reschedule mutation
|
||||||
const { mutate: proposeReschedule, isLoading: isProposingReschedule } = useMutation(
|
const { mutate: proposeReschedule, isPending: isProposingReschedule } = useMutation(
|
||||||
queryClient.bookings.proposeReschedule.mutationOptions()
|
queryClient.bookings.proposeReschedule.mutationOptions()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@@ -71,13 +71,20 @@ export default function BookingStatusPage({ token }: BookingStatusPageProps) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Try fetching reschedule proposal if booking not found or error
|
// Try fetching reschedule proposal if booking not found or error
|
||||||
useQuery({
|
const rescheduleQuery = useQuery({
|
||||||
...queryClient.cancellation.getRescheduleProposal.queryOptions({ input: { token } }),
|
...queryClient.cancellation.getRescheduleProposal.queryOptions({ input: { token } }),
|
||||||
enabled: !!token && (!!bookingError || !booking),
|
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
|
// Cancellation mutation
|
||||||
const cancelMutation = useMutation({
|
const cancelMutation = useMutation({
|
||||||
...queryClient.cancellation.cancelByToken.mutationOptions(),
|
...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 (
|
return (
|
||||||
<div className="min-h-screen bg-gradient-to-br from-pink-50 to-purple-50 py-8 px-4">
|
<div className="min-h-screen bg-gradient-to-br from-pink-50 to-purple-50 py-8 px-4">
|
||||||
@@ -430,22 +437,22 @@ export default function BookingStatusPage({ token }: BookingStatusPageProps) {
|
|||||||
<h2 className={`text-xl font-bold ${statusInfo.textColor} mb-2`}>
|
<h2 className={`text-xl font-bold ${statusInfo.textColor} mb-2`}>
|
||||||
Status: {statusInfo.label}
|
Status: {statusInfo.label}
|
||||||
</h2>
|
</h2>
|
||||||
{booking.status === "pending" && (
|
{booking?.status === "pending" && (
|
||||||
<p className={statusInfo.textColor}>
|
<p className={statusInfo.textColor}>
|
||||||
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.
|
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.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{booking.status === "confirmed" && (
|
{booking?.status === "confirmed" && (
|
||||||
<p className={statusInfo.textColor}>
|
<p className={statusInfo.textColor}>
|
||||||
Dein Termin wurde bestätigt! Wir freuen uns auf dich. Du hast eine Bestätigungs-E-Mail mit Kalendereintrag erhalten.
|
Dein Termin wurde bestätigt! Wir freuen uns auf dich. Du hast eine Bestätigungs-E-Mail mit Kalendereintrag erhalten.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{booking.status === "cancelled" && (
|
{booking?.status === "cancelled" && (
|
||||||
<p className={statusInfo.textColor}>
|
<p className={statusInfo.textColor}>
|
||||||
Dieser Termin wurde storniert. Du kannst jederzeit einen neuen Termin buchen.
|
Dieser Termin wurde storniert. Du kannst jederzeit einen neuen Termin buchen.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{booking.status === "completed" && (
|
{booking?.status === "completed" && (
|
||||||
<p className={statusInfo.textColor}>
|
<p className={statusInfo.textColor}>
|
||||||
Dieser Termin wurde erfolgreich abgeschlossen. Vielen Dank für deinen Besuch!
|
Dieser Termin wurde erfolgreich abgeschlossen. Vielen Dank für deinen Besuch!
|
||||||
</p>
|
</p>
|
||||||
@@ -465,27 +472,27 @@ export default function BookingStatusPage({ token }: BookingStatusPageProps) {
|
|||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="flex justify-between py-2 border-b border-gray-100">
|
<div className="flex justify-between py-2 border-b border-gray-100">
|
||||||
<span className="text-gray-600">Datum:</span>
|
<span className="text-gray-600">Datum:</span>
|
||||||
<span className="font-medium text-gray-900">{booking.formattedDate}</span>
|
<span className="font-medium text-gray-900">{booking?.formattedDate}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between py-2 border-b border-gray-100">
|
<div className="flex justify-between py-2 border-b border-gray-100">
|
||||||
<span className="text-gray-600">Uhrzeit:</span>
|
<span className="text-gray-600">Uhrzeit:</span>
|
||||||
<span className="font-medium text-gray-900">{booking.appointmentTime} Uhr</span>
|
<span className="font-medium text-gray-900">{booking?.appointmentTime} Uhr</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between py-2 border-b border-gray-100">
|
<div className="flex justify-between py-2 border-b border-gray-100">
|
||||||
<span className="text-gray-600">Behandlung:</span>
|
<span className="text-gray-600">Behandlung:</span>
|
||||||
<span className="font-medium text-gray-900">{booking.treatmentName}</span>
|
<span className="font-medium text-gray-900">{booking?.treatmentName}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between py-2 border-b border-gray-100">
|
<div className="flex justify-between py-2 border-b border-gray-100">
|
||||||
<span className="text-gray-600">Dauer:</span>
|
<span className="text-gray-600">Dauer:</span>
|
||||||
<span className="font-medium text-gray-900">{booking.treatmentDuration} Minuten</span>
|
<span className="font-medium text-gray-900">{booking?.treatmentDuration} Minuten</span>
|
||||||
</div>
|
</div>
|
||||||
{booking.treatmentPrice > 0 && (
|
{booking?.treatmentPrice && booking.treatmentPrice > 0 && (
|
||||||
<div className="flex justify-between py-2 border-b border-gray-100">
|
<div className="flex justify-between py-2 border-b border-gray-100">
|
||||||
<span className="text-gray-600">Preis:</span>
|
<span className="text-gray-600">Preis:</span>
|
||||||
<span className="font-medium text-gray-900">{booking.treatmentPrice.toFixed(2)} €</span>
|
<span className="font-medium text-gray-900">{booking.treatmentPrice.toFixed(2)} €</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{booking.hoursUntilAppointment > 0 && booking.status !== "cancelled" && booking.status !== "completed" && (
|
{booking?.hoursUntilAppointment && booking.hoursUntilAppointment > 0 && booking.status !== "cancelled" && booking.status !== "completed" && (
|
||||||
<div className="flex justify-between py-2">
|
<div className="flex justify-between py-2">
|
||||||
<span className="text-gray-600">Verbleibende Zeit:</span>
|
<span className="text-gray-600">Verbleibende Zeit:</span>
|
||||||
<span className="font-medium text-pink-600">
|
<span className="font-medium text-pink-600">
|
||||||
@@ -507,18 +514,18 @@ export default function BookingStatusPage({ token }: BookingStatusPageProps) {
|
|||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="flex justify-between py-2 border-b border-gray-100">
|
<div className="flex justify-between py-2 border-b border-gray-100">
|
||||||
<span className="text-gray-600">Name:</span>
|
<span className="text-gray-600">Name:</span>
|
||||||
<span className="font-medium text-gray-900">{booking.customerName}</span>
|
<span className="font-medium text-gray-900">{booking?.customerName}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between py-2 border-b border-gray-100">
|
<div className="flex justify-between py-2 border-b border-gray-100">
|
||||||
<span className="text-gray-600">E-Mail:</span>
|
<span className="text-gray-600">E-Mail:</span>
|
||||||
<span className="font-medium text-gray-900">{booking.customerEmail || '—'}</span>
|
<span className="font-medium text-gray-900">{booking?.customerEmail || '—'}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between py-2">
|
<div className="flex justify-between py-2">
|
||||||
<span className="text-gray-600">Telefon:</span>
|
<span className="text-gray-600">Telefon:</span>
|
||||||
<span className="font-medium text-gray-900">{booking.customerPhone || '—'}</span>
|
<span className="font-medium text-gray-900">{booking?.customerPhone || '—'}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{booking.notes && (
|
{booking?.notes && (
|
||||||
<div className="mt-4 pt-4 border-t border-gray-200">
|
<div className="mt-4 pt-4 border-t border-gray-200">
|
||||||
<h3 className="text-sm font-semibold text-gray-700 mb-2">Notizen:</h3>
|
<h3 className="text-sm font-semibold text-gray-700 mb-2">Notizen:</h3>
|
||||||
<p className="text-gray-600 text-sm">{booking.notes}</p>
|
<p className="text-gray-600 text-sm">{booking.notes}</p>
|
||||||
@@ -527,7 +534,7 @@ export default function BookingStatusPage({ token }: BookingStatusPageProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Cancellation Section */}
|
{/* Cancellation Section */}
|
||||||
{booking.canCancel && !cancellationResult?.success && (
|
{booking?.canCancel && !cancellationResult?.success && (
|
||||||
<div className="bg-white rounded-lg shadow-lg p-6 mb-6">
|
<div className="bg-white rounded-lg shadow-lg p-6 mb-6">
|
||||||
<h2 className="text-lg font-semibold text-gray-900 mb-4 flex items-center">
|
<h2 className="text-lg font-semibold text-gray-900 mb-4 flex items-center">
|
||||||
<svg className="w-5 h-5 mr-2 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5 mr-2 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
@@ -587,7 +594,7 @@ export default function BookingStatusPage({ token }: BookingStatusPageProps) {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!booking.canCancel && booking.status !== "cancelled" && booking.status !== "completed" && (
|
{!booking?.canCancel && booking?.status !== "cancelled" && booking?.status !== "completed" && (
|
||||||
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4 mb-6">
|
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4 mb-6">
|
||||||
<p className="text-yellow-800 text-sm">
|
<p className="text-yellow-800 text-sm">
|
||||||
<strong>ℹ️ Stornierungsfrist abgelaufen:</strong> 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.
|
<strong>ℹ️ Stornierungsfrist abgelaufen:</strong> 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.
|
||||||
|
@@ -612,13 +612,12 @@ const createManual = os
|
|||||||
cancellationUrl: bookingUrl
|
cancellationUrl: bookingUrl
|
||||||
});
|
});
|
||||||
|
|
||||||
await sendEmailWithAGBAndCalendar({
|
await sendEmailWithAGBAndCalendar({
|
||||||
to: input.customerEmail,
|
to: input.customerEmail!,
|
||||||
subject: "Dein Termin wurde bestätigt - AGB im Anhang",
|
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`,
|
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,
|
html,
|
||||||
bcc: process.env.ADMIN_EMAIL ? [process.env.ADMIN_EMAIL] : undefined,
|
}, {
|
||||||
}, {
|
|
||||||
date: input.appointmentDate,
|
date: input.appointmentDate,
|
||||||
time: input.appointmentTime,
|
time: input.appointmentTime,
|
||||||
durationMinutes: treatment.duration,
|
durationMinutes: treatment.duration,
|
||||||
|
@@ -343,7 +343,17 @@ export const router = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get booking details for each expired proposal
|
// 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) {
|
for (const proposal of expiredProposals) {
|
||||||
const booking = await bookingsKV.getItem(proposal.bookingId);
|
const booking = await bookingsKV.getItem(proposal.bookingId);
|
||||||
if (booking) {
|
if (booking) {
|
||||||
@@ -353,8 +363,8 @@ export const router = {
|
|||||||
customerName: booking.customerName,
|
customerName: booking.customerName,
|
||||||
originalDate: proposal.originalDate || booking.appointmentDate,
|
originalDate: proposal.originalDate || booking.appointmentDate,
|
||||||
originalTime: proposal.originalTime || booking.appointmentTime,
|
originalTime: proposal.originalTime || booking.appointmentTime,
|
||||||
proposedDate: proposal.proposedDate,
|
proposedDate: proposal.proposedDate!,
|
||||||
proposedTime: proposal.proposedTime,
|
proposedTime: proposal.proposedTime!,
|
||||||
treatmentName: treatment?.name || "Unbekannte Behandlung",
|
treatmentName: treatment?.name || "Unbekannte Behandlung",
|
||||||
customerEmail: booking.customerEmail,
|
customerEmail: booking.customerEmail,
|
||||||
customerPhone: booking.customerPhone,
|
customerPhone: booking.customerPhone,
|
||||||
|
Reference in New Issue
Block a user