From 31499284848d61e1032fb22571f2e1c4a6f963df Mon Sep 17 00:00:00 2001 From: Arthur Belleville Date: Sun, 26 Oct 2025 10:15:10 +0100 Subject: [PATCH] Beautiful loading state --- api/src/tablo.ts | 4 ++ apps/main/src/pages/PublicBookingPage.tsx | 79 +++++++++++++++++++++-- 2 files changed, 76 insertions(+), 7 deletions(-) diff --git a/api/src/tablo.ts b/api/src/tablo.ts index 754186f..5640f17 100644 --- a/api/src/tablo.ts +++ b/api/src/tablo.ts @@ -136,6 +136,10 @@ tabloRouter.post("/create-and-invite", async (c) => { email: string; }; + if (ownerId === user.id) { + return c.json({ error: "You cannot create a tablo with yourself" }, 400); + } + // TODO: Verify that the event start and end correspond to a slot // Check if there's already a tablo between the owner and the invited user diff --git a/apps/main/src/pages/PublicBookingPage.tsx b/apps/main/src/pages/PublicBookingPage.tsx index 6d6db6e..02afc67 100644 --- a/apps/main/src/pages/PublicBookingPage.tsx +++ b/apps/main/src/pages/PublicBookingPage.tsx @@ -18,6 +18,7 @@ import { TypographyMuted, } from "@xtablo/ui/components/typography"; import { + CalendarCheck2, CalendarIcon, ChevronLeftIcon, ChevronRightIcon, @@ -28,7 +29,7 @@ import { SunIcon, UserIcon, } from "lucide-react"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import { useNavigate, useParams } from "react-router-dom"; import { twMerge } from "tailwind-merge"; import { api } from "../lib/api"; @@ -53,12 +54,13 @@ export function PublicBookingPage() { event_type_standard_name || "" ); - const { mutateAsync: createTabloWithOwner } = useCreateTabloWithOwner(api, (data) => { - queryClient.invalidateQueries({ queryKey: ["tablos"] }); - invalidatePublicSlots(); - navigate(`/chat/${data.id}`, { replace: true }); - navigate(0); - }); + const { mutateAsync: createTabloWithOwner, isPending: isCreatingTabloWithOwner } = + useCreateTabloWithOwner(api, (data) => { + queryClient.invalidateQueries({ queryKey: ["tablos"] }); + invalidatePublicSlots(); + navigate(`/chat/${data.id}`, { replace: true }); + navigate(0); + }); const userProfile = publicSlots?.user; const eventType = publicSlots?.eventType; @@ -83,6 +85,26 @@ export function PublicBookingPage() { name: "", }); + // Loading messages rotation + const loadingMessages = [ + "Nous créons votre rendez-vous, veuillez patienter", + "Préparation de votre réservation...", + "Configuration de votre appel...", + "Finalisation de votre créneau...", + ]; + const [currentMessageIndex, setCurrentMessageIndex] = useState(0); + + useEffect(() => { + setCurrentMessageIndex(0); + if (isCreatingTabloWithOwner) { + const interval = setInterval(() => { + setCurrentMessageIndex((prev) => (prev + 1) % loadingMessages.length); + }, 1000); + + return () => clearInterval(interval); + } + }, [isCreatingTabloWithOwner]); + // Theme const { theme, setTheme } = useTheme(); @@ -566,6 +588,49 @@ export function PublicBookingPage() { + {/* Loading Overlay */} + {isCreatingTabloWithOwner && ( +
+
+
+ {/* Animated Icon */} +
+
+ +
+
+
+ + {/* Text */} +
+ + Réservation en cours... + + + {loadingMessages[currentMessageIndex]} + +
+ + {/* Loading Spinner */} +
+
+
+
+
+
+
+
+ )} + {/* Booking Modal */}