From affa408f3ab274583da3e379a776e5678cd0d1ee Mon Sep 17 00:00:00 2001 From: Arthur Belleville Date: Wed, 15 Oct 2025 22:57:57 +0200 Subject: [PATCH] Improve ui --- ui/package.json | 1 + ui/pnpm-lock.yaml | 36 ++ ui/src/components/AvailabilityCard.tsx | 253 ++++----- ui/src/components/EventTypeCard.tsx | 131 +++++ ui/src/components/EventTypeModal.tsx | 259 ++++----- ui/src/components/ui/card.tsx | 92 ++++ ui/src/components/ui/clipboard.tsx | 86 +++ ui/src/components/ui/dialog.tsx | 7 +- ui/src/components/ui/field.tsx | 242 +++++++++ ui/src/components/ui/textarea.tsx | 21 + ui/src/components/ui/tooltip.tsx | 30 + ui/src/components/ui/typography.tsx | 20 +- ui/src/hooks/event-types.ts | 16 +- ui/src/lib/routes.tsx | 5 - ui/src/pages/availabilities.tsx | 726 +++++++++++++------------ ui/src/pages/bookings.tsx | 81 ++- ui/src/pages/event-types-page.tsx | 177 +----- ui/src/pages/login.tsx | 55 +- ui/src/pages/planning.tsx | 184 +++---- ui/src/pages/reset-password.tsx | 49 +- ui/src/pages/signup.tsx | 113 ++-- ui/src/pages/support.tsx | 191 ------- ui/src/pages/tablo.tsx | 658 ++++++++-------------- ui/src/ui-library/clipboard.tsx | 85 --- 24 files changed, 1774 insertions(+), 1744 deletions(-) create mode 100644 ui/src/components/EventTypeCard.tsx create mode 100644 ui/src/components/ui/card.tsx create mode 100644 ui/src/components/ui/clipboard.tsx create mode 100644 ui/src/components/ui/field.tsx create mode 100644 ui/src/components/ui/textarea.tsx create mode 100644 ui/src/components/ui/tooltip.tsx delete mode 100644 ui/src/pages/support.tsx delete mode 100644 ui/src/ui-library/clipboard.tsx diff --git a/ui/package.json b/ui/package.json index da1b60e..9e79ecf 100644 --- a/ui/package.json +++ b/ui/package.json @@ -79,6 +79,7 @@ "@radix-ui/react-separator": "^1.1.7", "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-switch": "^1.2.6", + "@radix-ui/react-tooltip": "^1.2.8", "@react-stately/calendar": "^3.7.1", "@supabase/supabase-js": "^2.49.3", "@tailwindcss/vite": "^4.0.14", diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index dc05dec..f36d63d 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -51,6 +51,9 @@ importers: '@radix-ui/react-switch': specifier: ^1.2.6 version: 1.2.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-tooltip': + specifier: ^1.2.8 + version: 1.2.8(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@react-stately/calendar': specifier: ^3.7.1 version: 3.7.1(react@19.0.0) @@ -1552,6 +1555,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-tooltip@1.2.8': + resolution: {integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-use-callback-ref@1.1.1': resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} peerDependencies: @@ -7238,6 +7254,26 @@ snapshots: '@types/react': 19.0.10 '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.0.10)(react@19.0.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + optionalDependencies: + '@types/react': 19.0.10 + '@types/react-dom': 19.0.4(@types/react@19.0.10) + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.0.10)(react@19.0.0)': dependencies: react: 19.0.0 diff --git a/ui/src/components/AvailabilityCard.tsx b/ui/src/components/AvailabilityCard.tsx index 2716fff..8dfe90e 100644 --- a/ui/src/components/AvailabilityCard.tsx +++ b/ui/src/components/AvailabilityCard.tsx @@ -1,4 +1,5 @@ import { Button } from "@ui/components/ui/button"; +import { Card, CardAction, CardContent, CardHeader, CardTitle } from "@ui/components/ui/card"; import { Select, SelectContent, @@ -140,138 +141,140 @@ export function AvailabilityCard({ }; return ( -
-
-

{dayDisplay}

+ + + {dayDisplay} {onCopyToOtherDays && enabled && timeRanges.length > 0 && ( - + + + )} -
-
- - -
- - {/* Time Ranges */} -
- {timeRanges.map((range, index) => ( -
setSelectedRangeIndex(index)} - className={`flex items-center gap-1 rounded-md px-1.5 py-1 cursor-pointer transition-all duration-200 ${ - selectedRangeIndex === index - ? "bg-primary/10 dark:bg-primary/20" - : "bg-gray-50/80 dark:bg-gray-800/60 hover:bg-gray-100 dark:hover:bg-gray-700/60" + + +
+ +
- {timeRanges.length > 1 && ( - - )} -
- ))} - {timeRanges.length < 3 && ( - - )} -
-
+ ))} + {timeRanges.length < 3 && ( + + )} + + + ); } diff --git a/ui/src/components/EventTypeCard.tsx b/ui/src/components/EventTypeCard.tsx new file mode 100644 index 0000000..6ceeecb --- /dev/null +++ b/ui/src/components/EventTypeCard.tsx @@ -0,0 +1,131 @@ +import { EventType, EventTypeConfig, useEventTypes } from "@ui/hooks/event-types"; +import { Button } from "@ui/components/ui/button"; +import { + Card, + CardAction, + CardContent, + CardFooter, + CardHeader, + CardTitle, +} from "@ui/components/ui/card"; +import { CopyButton } from "@ui/components/ui/clipboard"; +import { Text } from "@ui/components/ui/typography"; +import { ExternalLinkIcon, EditIcon, TrashIcon, CheckIcon, XIcon } from "lucide-react"; +import { useUser } from "src/providers/UserStoreProvider"; + +export function EventTypeCard({ + eventType, + handleEditEventType, +}: { + eventType: EventType; + handleEditEventType: (id: string, eventType: EventTypeConfig) => void; +}) { + const { toggleEventType, deleteEventType } = useEventTypes(); + const user = useUser(); + const getPublicLink = (standardName: string | null) => { + // Sanitize user name for URL (replace spaces with hyphens, lowercase, remove special chars) + const sanitizedUserName = user.name + ?.toLowerCase() + .replace(/\s+/g, "-") + .replace(/[^a-z0-9-]/g, ""); + + const shortUserId = user.id.substring(0, 6); + // Construct the public booking URL + const baseUrl = window.location.origin; + const publicUrl = `${baseUrl}/book/${sanitizedUserName}-${shortUserId}/${standardName}`; + + return publicUrl; + }; + return ( + + + {eventType.name} + +
+ + + + +
+
+
+ + + {eventType.description} + +
+
+ Durée: + {eventType.duration} min +
+ {eventType.bufferTime && ( +
+ Temps de battement: + {eventType.bufferTime} min +
+ )} + {eventType.maxBookingsPerDay && ( +
+ Max par jour: + {eventType.maxBookingsPerDay} +
+ )} + {eventType.minAdvanceBooking && ( +
+ Réservation à l'avance: + + {eventType.minAdvanceBooking.value}{" "} + {eventType.minAdvanceBooking.unit === "minutes" + ? "min" + : eventType.minAdvanceBooking.unit === "hours" + ? "h" + : "j"} + +
+ )} +
+
+ + + Statut: + + +
+ ); +} diff --git a/ui/src/components/EventTypeModal.tsx b/ui/src/components/EventTypeModal.tsx index 6ef35f1..5dad05f 100644 --- a/ui/src/components/EventTypeModal.tsx +++ b/ui/src/components/EventTypeModal.tsx @@ -1,5 +1,12 @@ import { EventTypeConfig } from "@ui/hooks/event-types"; import { Button } from "@ui/components/ui/button"; +import { + Dialog, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@ui/components/ui/dialog"; import { Select, SelectContent, @@ -9,7 +16,6 @@ import { } from "@ui/components/ui/select"; import { Description, Input, Label, TextArea, TextField } from "@ui/ui-library/field"; import { NumberField, NumberInput } from "@ui/ui-library/number-field"; -import { CustomModal } from "./CustomModal"; export function EventTypeModal({ isModalOpen, @@ -27,132 +33,136 @@ export function EventTypeModal({ handleSaveEventType: () => void; }) { return ( - setIsModalOpen(false)} - title={editingEventType ? "Modifier le type d'événement" : "Nouveau type d'événement"} - width="xl" - > -
- {/* Basic Information Section */} -
- setFormData({ ...formData, name: value })} - isRequired - > - - - - - - -