Fix: Slot reservation only after successful email validation
- Move email validation before slot reservation in backend - Remove duplicate frontend email validation - Slots are no longer blocked by failed booking attempts - Clean up unused email error UI components - Ensure slots remain available if email validation fails
This commit is contained in:
@@ -96,12 +96,15 @@ const create = os
|
||||
);
|
||||
}
|
||||
|
||||
// Deep email validation using Rapid Email Validator API
|
||||
// Email validation before slot reservation
|
||||
console.log(`Validating email: ${input.customerEmail}`);
|
||||
const emailValidation = await validateEmail(input.customerEmail);
|
||||
console.log(`Email validation result:`, emailValidation);
|
||||
if (!emailValidation.valid) {
|
||||
console.log(`Email validation failed: ${emailValidation.reason}`);
|
||||
throw new Error(emailValidation.reason || "Ungültige E-Mail-Adresse");
|
||||
}
|
||||
|
||||
|
||||
// Validate that the booking is not in the past
|
||||
const today = new Date().toISOString().split("T")[0]; // YYYY-MM-DD
|
||||
if (input.appointmentDate < today) {
|
||||
@@ -118,8 +121,8 @@ const create = os
|
||||
}
|
||||
|
||||
// Prevent double booking: same customer email with pending/confirmed on same date
|
||||
// Skip duplicate check in development mode
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
// Skip duplicate check when DISABLE_DUPLICATE_CHECK is set
|
||||
if (!process.env.DISABLE_DUPLICATE_CHECK) {
|
||||
const existing = await kv.getAllItems();
|
||||
const hasConflict = existing.some(b =>
|
||||
b.customerEmail.toLowerCase() === input.customerEmail.toLowerCase() &&
|
||||
@@ -137,7 +140,11 @@ const create = os
|
||||
status: "pending" as const,
|
||||
createdAt: new Date().toISOString()
|
||||
};
|
||||
// If a slotId is provided, tentatively reserve the slot (mark reserved but pending)
|
||||
|
||||
// First save the booking
|
||||
await kv.setItem(id, booking);
|
||||
|
||||
// Then reserve the slot only after successful booking creation
|
||||
if (booking.slotId) {
|
||||
const slot = await availabilityKV.getItem(booking.slotId);
|
||||
if (!slot) throw new Error("Availability slot not found");
|
||||
@@ -149,12 +156,11 @@ const create = os
|
||||
};
|
||||
await availabilityKV.setItem(slot.id, updatedSlot);
|
||||
}
|
||||
await kv.setItem(id, booking);
|
||||
|
||||
// Notify customer: request received (pending)
|
||||
void (async () => {
|
||||
// Create booking access token for status viewing
|
||||
const bookingAccessToken = await queryClient.cancellation.createToken({ input: { bookingId: id } });
|
||||
const bookingAccessToken = await queryClient.cancellation.createToken({ bookingId: id });
|
||||
const bookingUrl = generateUrl(`/booking/${bookingAccessToken.token}`);
|
||||
|
||||
const formattedDate = formatDateGerman(input.appointmentDate);
|
||||
@@ -222,8 +228,10 @@ const create = os
|
||||
}
|
||||
})();
|
||||
return booking;
|
||||
} catch (error) {
|
||||
} catch (error) {
|
||||
console.error("Booking creation error:", error);
|
||||
|
||||
// Re-throw the error for oRPC to handle
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
@@ -297,7 +305,7 @@ const updateStatus = os
|
||||
try {
|
||||
if (input.status === "confirmed") {
|
||||
// Create booking access token for this booking (status + cancellation)
|
||||
const bookingAccessToken = await queryClient.cancellation.createToken({ input: { bookingId: booking.id } });
|
||||
const bookingAccessToken = await queryClient.cancellation.createToken({ bookingId: booking.id });
|
||||
|
||||
const formattedDate = formatDateGerman(booking.appointmentDate);
|
||||
const bookingUrl = generateUrl(`/booking/${bookingAccessToken.token}`);
|
||||
|
Reference in New Issue
Block a user