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:
2025-10-05 16:28:28 +02:00
parent a8cec16d7a
commit 6cf657168b
4 changed files with 47 additions and 31 deletions

View File

@@ -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()
);

View File

@@ -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 (
<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`}>
Status: {statusInfo.label}
</h2>
{booking.status === "pending" && (
{booking?.status === "pending" && (
<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.
</p>
)}
{booking.status === "confirmed" && (
{booking?.status === "confirmed" && (
<p className={statusInfo.textColor}>
Dein Termin wurde bestätigt! Wir freuen uns auf dich. Du hast eine Bestätigungs-E-Mail mit Kalendereintrag erhalten.
</p>
)}
{booking.status === "cancelled" && (
{booking?.status === "cancelled" && (
<p className={statusInfo.textColor}>
Dieser Termin wurde storniert. Du kannst jederzeit einen neuen Termin buchen.
</p>
)}
{booking.status === "completed" && (
{booking?.status === "completed" && (
<p className={statusInfo.textColor}>
Dieser Termin wurde erfolgreich abgeschlossen. Vielen Dank für deinen Besuch!
</p>
@@ -465,27 +472,27 @@ export default function BookingStatusPage({ token }: BookingStatusPageProps) {
<div className="space-y-3">
<div className="flex justify-between py-2 border-b border-gray-100">
<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 className="flex justify-between py-2 border-b border-gray-100">
<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 className="flex justify-between py-2 border-b border-gray-100">
<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 className="flex justify-between py-2 border-b border-gray-100">
<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>
{booking.treatmentPrice > 0 && (
{booking?.treatmentPrice && booking.treatmentPrice > 0 && (
<div className="flex justify-between py-2 border-b border-gray-100">
<span className="text-gray-600">Preis:</span>
<span className="font-medium text-gray-900">{booking.treatmentPrice.toFixed(2)} </span>
</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">
<span className="text-gray-600">Verbleibende Zeit:</span>
<span className="font-medium text-pink-600">
@@ -507,18 +514,18 @@ export default function BookingStatusPage({ token }: BookingStatusPageProps) {
<div className="space-y-3">
<div className="flex justify-between py-2 border-b border-gray-100">
<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 className="flex justify-between py-2 border-b border-gray-100">
<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 className="flex justify-between py-2">
<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>
{booking.notes && (
{booking?.notes && (
<div className="mt-4 pt-4 border-t border-gray-200">
<h3 className="text-sm font-semibold text-gray-700 mb-2">Notizen:</h3>
<p className="text-gray-600 text-sm">{booking.notes}</p>
@@ -527,7 +534,7 @@ export default function BookingStatusPage({ token }: BookingStatusPageProps) {
</div>
{/* Cancellation Section */}
{booking.canCancel && !cancellationResult?.success && (
{booking?.canCancel && !cancellationResult?.success && (
<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">
<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>
)}
{!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">
<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.

View File

@@ -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,

View File

@@ -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,