From e5c0cab7e9e25fe5da085234374e9f11bbe4b59e Mon Sep 17 00:00:00 2001 From: Arthur Belleville Date: Sun, 13 Apr 2025 11:22:50 +0200 Subject: [PATCH] Add empty state + fix logout issue --- ui/src/components/CustomLoadingOverlay.tsx | 35 +++ ui/src/components/RowActionMenu.tsx | 48 ++++ ui/src/hooks/auth.ts | 2 + ui/src/pages/devis.tsx | 270 +++++++++++++-------- 4 files changed, 258 insertions(+), 97 deletions(-) create mode 100644 ui/src/components/CustomLoadingOverlay.tsx create mode 100644 ui/src/components/RowActionMenu.tsx diff --git a/ui/src/components/CustomLoadingOverlay.tsx b/ui/src/components/CustomLoadingOverlay.tsx new file mode 100644 index 0000000..ba05716 --- /dev/null +++ b/ui/src/components/CustomLoadingOverlay.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import icon from "../assets/icon.jpg"; // Import the image + +// Define props type (adjust if needed) +interface CustomLoadingOverlayProps { + loadingMessage?: string; +} + +export const CustomLoadingOverlay: React.FC = ({ + loadingMessage = "Loading...", +}) => { + return ( +
+
+ {/* Use the imported image */} + Loading icon + {/* Display the loading message passed via props */} +

+ {loadingMessage} +

+
+
+ ); +}; diff --git a/ui/src/components/RowActionMenu.tsx b/ui/src/components/RowActionMenu.tsx new file mode 100644 index 0000000..d754233 --- /dev/null +++ b/ui/src/components/RowActionMenu.tsx @@ -0,0 +1,48 @@ +import React from "react"; +import { Button } from "@ui/ui-library/button"; +import { PencilIcon, TrashIcon } from "lucide-react"; +import { Database } from "@ui/types/db"; + +type Devis = Database["public"]["Tables"]["devis"]["Row"]; + +interface RowActionMenuProps { + devis: Devis; + onEdit: (devis: Devis) => void; + onDelete: (devis: Devis) => void; +} + +export const RowActionMenu: React.FC = ({ + devis, + onEdit, + onDelete, +}) => { + return ( +
e.stopPropagation()} + onDoubleClick={(e) => e.stopPropagation()} + > + + +
+ ); +}; diff --git a/ui/src/hooks/auth.ts b/ui/src/hooks/auth.ts index 468a24a..172d658 100644 --- a/ui/src/hooks/auth.ts +++ b/ui/src/hooks/auth.ts @@ -8,6 +8,7 @@ import { Session, createClient, } from "@supabase/supabase-js"; +import { queryClient } from "@ui/lib/api"; export type User = SupabaseUser & { user_metadata: { @@ -153,6 +154,7 @@ export function useLogout() { mutationFn: async () => { const { error } = await supabase.auth.signOut(); if (error) throw error; + queryClient.removeQueries(); }, }); } diff --git a/ui/src/pages/devis.tsx b/ui/src/pages/devis.tsx index f6cd2f8..1522bfd 100644 --- a/ui/src/pages/devis.tsx +++ b/ui/src/pages/devis.tsx @@ -1,7 +1,8 @@ import { Button } from "@ui/ui-library/button"; -import { PlusIcon, TrashIcon } from "lucide-react"; +import { NotebookPenIcon, PlusIcon } from "lucide-react"; import { AllCommunityModule, + ColDef, ModuleRegistry, themeQuartz, } from "ag-grid-community"; @@ -28,6 +29,15 @@ import { import { CalendarDate, DateValue } from "@internationalized/date"; import { Form } from "@ui/ui-library/form"; import { DateField, DateInput } from "@ui/ui-library/date-field"; +import { RowActionMenu } from "@ui/components/RowActionMenu"; +import { CustomLoadingOverlay } from "@ui/components/CustomLoadingOverlay"; +import { + EmptyState, + EmptyStateActions, + EmptyStateDescription, + EmptyStateHeading, + EmptyStateIcon, +} from "@ui/ui-library/empty-state"; ModuleRegistry.registerModules([AllCommunityModule]); @@ -55,6 +65,9 @@ export const DevisPage = () => { const [dueDateError, setDueDateError] = useState(""); const [selectedDevis, setSelectedDevis] = useState(null); const [formData, setFormData] = useState(defaultFormData); + const [devisToDelete, setDevisToDelete] = useState(null); + const [isConfirmDeleteModalOpen, setIsConfirmDeleteModalOpen] = + useState(false); const validateDueDate = (date: DateValue, dueDate: DateValue) => { if (dueDate.compare(date) < 0) { @@ -123,26 +136,89 @@ export const DevisPage = () => { setFormData(defaultFormData); }; - const confirmDelete = (devisId: string) => { - deleteDevis.mutate(devisId); + const handleEdit = (devis: Devis) => { + console.log("Edit devis:", devis); }; - if (isLoading) { - return
Loading...
; - } + const handleDelete = (devis: Devis) => { + setDevisToDelete(devis); + setIsConfirmDeleteModalOpen(true); + }; + + const confirmDeleteAction = () => { + if (devisToDelete) { + deleteDevis.mutate(devisToDelete.id); + setDevisToDelete(null); + setIsConfirmDeleteModalOpen(false); + } + }; + + const columnDefs: ColDef[] = [ + { + field: "date", + headerName: "Date", + valueFormatter: (params) => { + if (!params.value) return ""; + return new Date(params.value).toLocaleDateString("fr-FR"); + }, + }, + { field: "client_email", headerName: "Client" }, + { + field: "tax", + headerName: "TVA", + valueFormatter: (params) => { + if (params.value == null) return ""; + return params.value.toFixed(2) + " €"; + }, + flex: 1, + }, + { + field: "total", + headerName: "Montant", + valueFormatter: (params) => { + return params.value.toFixed(2) + " €"; + }, + }, + { field: "status", headerName: "Status" }, + { + headerName: "Actions", + pinned: "right", + width: 80, + cellStyle: { padding: 0 }, + colId: "actions-column", + cellRenderer: (params: { data: Devis; node: { id: string | null } }) => { + if (!params.data) return null; + return ( +
+ +
+ ); + }, + lockPosition: true, + suppressNavigable: true, + suppressMovable: true, + filter: false, + sortable: false, + resizable: false, + suppressSizeToFit: true, + }, + ]; return (

- Devis + Vos devis

-
+
-
- - rowData={devisData} - gridOptions={{ - theme: themeQuartz, - onRowDoubleClicked: (event) => { - if (event.data) { - setSelectedDevis(event.data); - } - }, - domLayout: "autoHeight", - suppressHorizontalScroll: true, - defaultColDef: { - resizable: false, - sortable: true, - filter: false, - flex: 1, - minWidth: 100, - }, - }} - columnDefs={[ - { - field: "date", - headerName: "Date", - valueFormatter: (params) => { - if (!params.value) return ""; - return new Date(params.value).toLocaleDateString("fr-FR"); + {devisData?.length === 0 ? ( + + + + + Aucun devis + + Créez un nouveau devis pour commencer. + + + + + + + + ) : ( +
+ + rowData={devisData} + loading={isLoading} + gridOptions={{ + theme: themeQuartz, + onRowDoubleClicked: (event) => { + if (event.data) { + setSelectedDevis(event.data); + } }, - }, - { field: "client_email", headerName: "Client" }, - { - field: "total", - headerName: "Montant", - valueFormatter: (params) => { - return params.value.toFixed(2) + " €"; + suppressHorizontalScroll: true, + domLayout: "autoHeight", + loadingOverlayComponent: CustomLoadingOverlay, + loadingOverlayComponentParams: { + loadingMessage: "Chargement des devis...", }, - }, - { field: "status", headerName: "Status" }, - { - headerName: "Actions", - cellRenderer: (params: { data: Devis }) => ( - - - - - -

- Supprimer le devis -

- -
- -

- Êtes-vous sûr de vouloir supprimer ce devis ? - Cette action est irréversible. -

-
- - - Annuler - - - -
-
-
- ), - }, - ]} - /> -
+ defaultColDef: { + sortable: true, + filter: false, + flex: 1, + minWidth: 100, + resizable: false, + }, + }} + columnDefs={columnDefs} + /> +
+ )}
- + !isOpen && setSelectedDevis(null)} + isDismissable + >

@@ -490,6 +528,44 @@ export const DevisPage = () => {

+ + + +

+ Supprimer le devis +

+ setIsConfirmDeleteModalOpen(false)} + /> +
+ +

+ Êtes-vous sûr de vouloir supprimer ce devis ? Cette action est + irréversible. +

+
+ + + + +
+
); };