diff --git a/imago-admin/src/pages/admin/ClientDetail.tsx b/imago-admin/src/pages/admin/ClientDetail.tsx index 0873106..85bc655 100644 --- a/imago-admin/src/pages/admin/ClientDetail.tsx +++ b/imago-admin/src/pages/admin/ClientDetail.tsx @@ -1,18 +1,49 @@ +import { useState } from 'react'; import { useParams, Link } from 'react-router-dom'; import { PageLayout } from '@/components/layout/PageLayout'; import { PlanBadge } from '@/components/shared/PlanBadge'; import { QuotaBar } from '@/components/shared/QuotaBar'; import { StatusBadge } from '@/components/shared/StatusBadge'; import { CardSkeleton } from '@/components/shared/Skeleton'; -import { useClient } from '@/hooks/useClients'; +import { toast } from '@/components/shared/Toast'; +import { useClient, useUpdateClient } from '@/hooks/useClients'; import { useImages } from '@/hooks/useImages'; import { formatDate, formatTokens, formatMB } from '@/utils/formatters'; -import { ArrowLeft, Images, ExternalLink } from 'lucide-react'; +import { PLANS, SCOPES } from '@/utils/constants'; +import { ArrowLeft, Images, ExternalLink, Edit3, X, Loader2, Save } from 'lucide-react'; +import type { Plan, APIClientUpdate } from '@/types/api.types'; export default function ClientDetail() { const { id } = useParams<{ id: string }>(); const { data: client, isLoading } = useClient(id); const { data: clientImages } = useImages({ client_id: id, page_size: 20, sort_order: 'desc' }); + const updateClientMut = useUpdateClient(); + + const [showEdit, setShowEdit] = useState(false); + const [editScopes, setEditScopes] = useState([]); + const [editPlan, setEditPlan] = useState('free'); + + const openEdit = () => { + if (!client) return; + setEditScopes([...client.scopes]); + setEditPlan(client.plan); + setShowEdit(true); + }; + + const handleSave = async () => { + if (!client) return; + const data: APIClientUpdate = { + scopes: editScopes, + plan: editPlan, + }; + try { + await updateClientMut.mutateAsync({ id: client.id, data }); + toast('success', 'Client mis à jour'); + setShowEdit(false); + } catch { + toast('error', 'Erreur lors de la mise à jour'); + } + }; if (isLoading) { return ( @@ -46,6 +77,9 @@ export default function ClientDetail() { {client.is_active ? 'Actif' : 'Inactif'} + Retour @@ -82,7 +116,12 @@ export default function ClientDetail() { {/* Scopes */}
-

Scopes accordés

+
+

Scopes accordés

+ +
{client.scopes.map((scope) => ( @@ -137,6 +176,65 @@ export default function ClientDetail() {
)}
+ + {/* Edit modal */} + {showEdit && ( +
+
setShowEdit(false)} /> +
+
+

Éditer {client.name}

+ +
+
+
+ + +

+ {PLANS[editPlan].images === Infinity ? '∞' : PLANS[editPlan].images} images · {formatMB(PLANS[editPlan].storageMb)} · {formatTokens(PLANS[editPlan].aiTokens)} tokens +

+
+
+ +
+ {SCOPES.map((scope) => ( + + ))} +
+
+ +
+
+
+ )} ); } diff --git a/imago-admin/src/utils/constants.ts b/imago-admin/src/utils/constants.ts index ed2f29e..e03eb9f 100644 --- a/imago-admin/src/utils/constants.ts +++ b/imago-admin/src/utils/constants.ts @@ -48,6 +48,7 @@ export const PLANS = { export const SCOPES = [ 'images:read', 'images:write', + 'images:delete', 'ai:use', 'admin', ] as const;