diff --git a/src/App.css b/src/App.css index f90339d..e69de29 100644 --- a/src/App.css +++ b/src/App.css @@ -1,184 +0,0 @@ -.counter { - font-size: 16px; - padding: 5px 10px; - border-radius: 5px; - color: var(--accent); - background: var(--accent-bg); - border: 2px solid transparent; - transition: border-color 0.3s; - margin-bottom: 24px; - - &:hover { - border-color: var(--accent-border); - } - &:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; - } -} - -.hero { - position: relative; - - .base, - .framework, - .vite { - inset-inline: 0; - margin: 0 auto; - } - - .base { - width: 170px; - position: relative; - z-index: 0; - } - - .framework, - .vite { - position: absolute; - } - - .framework { - z-index: 1; - top: 34px; - height: 28px; - transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg) - scale(1.4); - } - - .vite { - z-index: 0; - top: 107px; - height: 26px; - width: auto; - transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg) - scale(0.8); - } -} - -#center { - display: flex; - flex-direction: column; - gap: 25px; - place-content: center; - place-items: center; - flex-grow: 1; - - @media (max-width: 1024px) { - padding: 32px 20px 24px; - gap: 18px; - } -} - -#next-steps { - display: flex; - border-top: 1px solid var(--border); - text-align: left; - - & > div { - flex: 1 1 0; - padding: 32px; - @media (max-width: 1024px) { - padding: 24px 20px; - } - } - - .icon { - margin-bottom: 16px; - width: 22px; - height: 22px; - } - - @media (max-width: 1024px) { - flex-direction: column; - text-align: center; - } -} - -#docs { - border-right: 1px solid var(--border); - - @media (max-width: 1024px) { - border-right: none; - border-bottom: 1px solid var(--border); - } -} - -#next-steps ul { - list-style: none; - padding: 0; - display: flex; - gap: 8px; - margin: 32px 0 0; - - .logo { - height: 18px; - } - - a { - color: var(--text-h); - font-size: 16px; - border-radius: 6px; - background: var(--social-bg); - display: flex; - padding: 6px 12px; - align-items: center; - gap: 8px; - text-decoration: none; - transition: box-shadow 0.3s; - - &:hover { - box-shadow: var(--shadow); - } - .button-icon { - height: 18px; - width: 18px; - } - } - - @media (max-width: 1024px) { - margin-top: 20px; - flex-wrap: wrap; - justify-content: center; - - li { - flex: 1 1 calc(50% - 8px); - } - - a { - width: 100%; - justify-content: center; - box-sizing: border-box; - } - } -} - -#spacer { - height: 88px; - border-top: 1px solid var(--border); - @media (max-width: 1024px) { - height: 48px; - } -} - -.ticks { - position: relative; - width: 100%; - - &::before, - &::after { - content: ''; - position: absolute; - top: -4.5px; - border: 5px solid transparent; - } - - &::before { - left: 0; - border-left-color: var(--border); - } - &::after { - right: 0; - border-right-color: var(--border); - } -} diff --git a/src/components/auth/LocalLoginForm.tsx b/src/components/auth/LocalLoginForm.tsx index c2403b3..430f076 100644 --- a/src/components/auth/LocalLoginForm.tsx +++ b/src/components/auth/LocalLoginForm.tsx @@ -1,5 +1,6 @@ import React, { useState } from 'react'; import { Button } from '@/components/ui/Button'; +import { Input } from '../ui/Input'; interface LocalLoginFormProps { onSubmit: (email: string, pass: string) => void; @@ -20,10 +21,9 @@ export const LocalLoginForm: React.FC = ({ onSubmit, isLoad
- setEmail(e.target.value)} - className="block w-full rounded-md border border-slate-300 px-3 py-2 shadow-sm focus:border-blue-900 focus:ring-blue-900 sm:text-sm" />
@@ -31,10 +31,9 @@ export const LocalLoginForm: React.FC = ({ onSubmit, isLoad
- setPassword(e.target.value)} - className="block w-full rounded-md border border-slate-300 px-3 py-2 shadow-sm focus:border-blue-900 focus:ring-blue-900 sm:text-sm" + value={password} onChange={(e) => setPassword(e.target.value)} />
diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx index 4475348..86f3a35 100644 --- a/src/components/layout/Sidebar.tsx +++ b/src/components/layout/Sidebar.tsx @@ -4,6 +4,8 @@ import { Shield, ShieldCheck, HelpCircle, Lock, Settings, LogOut } from 'lucide- import { cn } from '@/utils/utils'; import { Card } from '@/components/ui/Card'; import { NEUMORPHISM } from '@/styles/Neumorphism'; +import { useTheme } from '@/hooks/useTheme'; +import { ThemeToggle } from '@ui/ThemeToggle'; interface SidebarProps { onLogout: () => void; @@ -21,20 +23,21 @@ const NAVIGATION_ITEMS = [ export const Sidebar: React.FC = ({ onLogout, className }) => { + const { isDark, toggleTheme } = useTheme(); return ( {/* 1. EN-TÊTE / LOGO */}
{/* Petit badge logo avec effet creusé */}
- +
-

AEGIS

- by GISE +

AEGIS

+ by GISE
@@ -52,7 +55,7 @@ export const Sidebar: React.FC = ({ onLogout, className }) => { "w-full flex items-center gap-4 px-5 py-4 rounded-2xl transition-all duration-300 ease-in-out cursor-pointer", "bg-[#e8e8e8] dark:bg-[#1e293b]", isActive - ? cn(NEUMORPHISM.insetLight, NEUMORPHISM.insetDark, "!text-blue-600 dark:!text-blue-400 font-black") + ? cn(NEUMORPHISM.insetLight, NEUMORPHISM.insetDark, "text-blue-600! dark:text-blue-400! font-black") : cn(NEUMORPHISM.lightShadow, NEUMORPHISM.darkShadow, "text-slate-500 dark:text-slate-400 font-bold hover:text-slate-700 dark:hover:text-slate-200") ) } @@ -74,6 +77,7 @@ export const Sidebar: React.FC = ({ onLogout, className }) => { })} + {/* 3. ZONE DE DÉCONNEXION */}
+ + + ); +}; \ No newline at end of file diff --git a/src/components/ui/LogoGise.tsx b/src/components/ui/LogoGise.tsx new file mode 100644 index 0000000..b40f817 --- /dev/null +++ b/src/components/ui/LogoGise.tsx @@ -0,0 +1,20 @@ +export function LogoGise(){ + return( +
+
+
+ + + +
+
+

+ AEGIS by GISE +

+

+ Accès restreint +

+
+ ); +}; +export default LogoGise; \ No newline at end of file diff --git a/src/components/ui/ServiceObject.tsx b/src/components/ui/ServiceObject.tsx index 6ece131..539a5b7 100644 --- a/src/components/ui/ServiceObject.tsx +++ b/src/components/ui/ServiceObject.tsx @@ -9,7 +9,7 @@ interface ServiceObjectProps { export const ServiceObject: React.FC = ({ contract }) => { return ( - +

{contract.service_name}

{/* Fonctionne instantanément avec 'Actif', 'En déploiement', 'Suspendu' */} diff --git a/src/components/ui/StatusBadge.tsx b/src/components/ui/StatusBadge.tsx index 010be32..347ce94 100644 --- a/src/components/ui/StatusBadge.tsx +++ b/src/components/ui/StatusBadge.tsx @@ -1,5 +1,3 @@ -// Fichier : src/components/ui/StatusBadge.tsx - import React from 'react'; import { cn } from '@/utils/utils'; import type { AegisStatus } from '@/types/Status'; diff --git a/src/components/ui/ThemeToggle.tsx b/src/components/ui/ThemeToggle.tsx new file mode 100644 index 0000000..6e5dfb4 --- /dev/null +++ b/src/components/ui/ThemeToggle.tsx @@ -0,0 +1,52 @@ +import { Sun, Moon } from "lucide-react"; +import { cn } from "@utils/utils"; + +interface ThemeToggleProps { + isDark: boolean; + toggleTheme: () => void; +} + +export function ThemeToggle({ isDark, toggleTheme }: ThemeToggleProps) { + + const lightInset = "shadow-[inset_4px_4px_8px_#c5c5c5,inset_-4px_-4px_8px_#ffffff]"; + const darkInset = "dark:shadow-[inset_4px_4px_8px_#121926,inset_-4px_-4px_8px_#2a3850]"; + + const lightExtrude = "shadow-[3px_3px_6px_#c5c5c5,-3px_-3px_6px_#ffffff]"; + const darkExtrude = "dark:shadow-[3px_3px_6px_#121926,-3px_-3px_6px_#2a3850]"; + + return ( + + ); +} \ No newline at end of file diff --git a/src/components/ui/TicketRow.tsx b/src/components/ui/TicketRow.tsx index 681f8ad..75c8dc8 100644 --- a/src/components/ui/TicketRow.tsx +++ b/src/components/ui/TicketRow.tsx @@ -11,7 +11,7 @@ export const TicketRow: React.FC = ({ ticket, onClick }) => { return (
onClick(ticket.id)} - className="p-6 flex items-center justify-between hover:bg-slate-50 transition-colors cursor-pointer group" + className="p-6 flex items-center justify-between hover:bg-amber-50 transition-colors" >
diff --git a/src/components/ui/UptimeObject.tsx b/src/components/ui/UptimeObject.tsx index 12cccaf..4955564 100644 --- a/src/components/ui/UptimeObject.tsx +++ b/src/components/ui/UptimeObject.tsx @@ -1,6 +1,7 @@ import React from 'react'; import type { InfrastructureNode } from '@/types/InfrastructureNode'; import { StatusBadge } from '@ui/StatusBadge'; +import { cn } from '@/utils/utils'; interface UptimeObjectProps { node: InfrastructureNode; @@ -9,24 +10,25 @@ interface UptimeObjectProps { export const UptimeObject: React.FC = ({ node }) => { return ( // La "key" a été retirée d'ici, elle sera gérée par le parent -
- - {/* Infos Serveur */} -
-

{node.label}

-

{node.type}

-
- - {/* Métriques & Badge */} -
-
-

SLA

-

{node.uptime_sla}

-
- - -
+ -
+ {/* Infos Serveur */} + +

{node.label}

+

{node.type}

+ + + {/* Métriques & Badge */} + +

SLA

+

{node.uptime_sla}

+ + + + + + ); }; \ No newline at end of file diff --git a/src/components/widgets/AccessWidget.tsx b/src/components/widgets/AccessWidget.tsx new file mode 100644 index 0000000..4c54c76 --- /dev/null +++ b/src/components/widgets/AccessWidget.tsx @@ -0,0 +1,43 @@ +import { Card, CardContent, CardHeader } from '@/components/ui/Card'; +import { Lock } from "lucide-react"; + +export function AccessWidget() { + + return ( + + +

Sécurité de l'accès

+
+ + + {/* MFA Status */} +
+
+

Authentification à double facteur (MFA)

+

Exigée par les politiques de sécurité GISE

+
+ + + Actif + +
+ +
+ +
+ + {/* Mot de passe */} +
+

Mot de passe institutionnel

+

Dernière modification : Il y a 43 jours

+ +
+
+
+ ); +} +export default AccessWidget; \ No newline at end of file diff --git a/src/components/widgets/BackupWidget.tsx b/src/components/widgets/BackupWidget.tsx index a7e8239..974c686 100644 --- a/src/components/widgets/BackupWidget.tsx +++ b/src/components/widgets/BackupWidget.tsx @@ -1,4 +1,6 @@ -import { Card, CardContent } from '@/components/ui/Card'; +import { Card, CardContent, CardHeader } from '@/components/ui/Card'; +import { Check } from 'lucide-react'; +import Badge from '@ui/Badge'; interface BackupWidgetProps { onClick?: () => void; @@ -6,27 +8,22 @@ interface BackupWidgetProps { export default function BackupWidget({ onClick }: BackupWidgetProps) { return ( - + +

Sauvegardes

+ + +
-
-

Sauvegardes

- - PRA Immuable - -
- -
+

Dernier snapshot système

Aujourd'hui à 03:00 AM

-
- +
- - - + Intégrité validée avec succès
diff --git a/src/components/widgets/DocumentsWidget.tsx b/src/components/widgets/DocumentsWidget.tsx new file mode 100644 index 0000000..c583764 --- /dev/null +++ b/src/components/widgets/DocumentsWidget.tsx @@ -0,0 +1,36 @@ +import { useDocuments } from '@/hooks/useDocuments'; +import {DocumentObject} from '@ui/DocumentObject' +import { Card } from '../ui/Card'; + +export function DocumentsWidget(){ + const { documents } = useDocuments(); + return ( + + + + + + + + + + + + {documents.map((doc) => ( + + ))} + +
+ Nom du fichier + + Catégorie + + Ajouté le + + Action +
+
+ ); +}; + +export default DocumentsWidget; \ No newline at end of file diff --git a/src/components/widgets/EvolutionOptionCard.tsx b/src/components/widgets/EvolutionOptionCard.tsx index 09530cb..cfb343d 100644 --- a/src/components/widgets/EvolutionOptionCard.tsx +++ b/src/components/widgets/EvolutionOptionCard.tsx @@ -19,7 +19,7 @@ export const EvolutionOptionCard: React.FC = ({ return (
diff --git a/src/components/widgets/InformationWidget.tsx b/src/components/widgets/InformationWidget.tsx new file mode 100644 index 0000000..ea59a71 --- /dev/null +++ b/src/components/widgets/InformationWidget.tsx @@ -0,0 +1,37 @@ +import { Card, CardContent, CardHeader } from '@/components/ui/Card'; +import { Building } from "lucide-react"; + +export function InformationWidget() { + return ( + + +

+ + Profil de l'Organisation +

+
+ + +
+
+
Entité Légale
+
Cabinet Juridique Example & Associés
+
+
+
Administrateur du compte
+
Direction Générale (direction@example.com)
+
+
+
Niveau d'Infogérance (SLA)
+
+ VIP PLATINUM + Couverture 24/7 (99.99%) +
+
+
+
+
+ ); +} + +export default InformationWidget; \ No newline at end of file diff --git a/src/components/widgets/NewTicketWidget.tsx b/src/components/widgets/NewTicketWidget.tsx index d07904e..cc73dcc 100644 --- a/src/components/widgets/NewTicketWidget.tsx +++ b/src/components/widgets/NewTicketWidget.tsx @@ -5,7 +5,6 @@ import { Button } from '@/components/ui/Button'; import { Input } from '@/components/ui/Input'; import { Select } from '@/components/ui/Select'; import { Textarea } from '@/components/ui/Textarea'; -import { cn } from '@/utils/utils'; interface NewTicketWidgetProps { onCancel: () => void; @@ -22,6 +21,7 @@ export const NewTicketWidget: React.FC = ({ onCancel, onSu const [affectedAsset, setAffectedAsset] = useState(''); const [incidentTime, setIncidentTime] = useState(''); const [description, setDescription] = useState(''); + const [buttonVariant, setButtonVariant]=useState('primary'); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); @@ -39,7 +39,7 @@ export const NewTicketWidget: React.FC = ({ onCancel, onSu return ( - +

Ouvrir un nouveau ticket

Qualification de votre requête ITSM

@@ -62,7 +62,7 @@ export const NewTicketWidget: React.FC = ({ onCancel, onSu = ({ ticket }) => { return ( - +
@@ -24,10 +24,10 @@ export const TicketInfoWidget: React.FC = ({ ticket }) =>

- + - +

Description du besoin :

diff --git a/src/components/widgets/TicketsWidget.tsx b/src/components/widgets/TicketsWidget.tsx index 7845a72..0f83517 100644 --- a/src/components/widgets/TicketsWidget.tsx +++ b/src/components/widgets/TicketsWidget.tsx @@ -13,7 +13,7 @@ export const TicketsWidget: React.FC = ({ onSelectTicket }) const { tickets, isLoading, error } = useTickets(); return ( - + {/* En-tête du Widget */}
diff --git a/src/components/widgets/UptimeWidget.tsx b/src/components/widgets/UptimeWidget.tsx index af5ba56..f682c0f 100644 --- a/src/components/widgets/UptimeWidget.tsx +++ b/src/components/widgets/UptimeWidget.tsx @@ -10,8 +10,8 @@ export default function UptimeWidget() { return ( -

État des Services & SLA

- +

État des Services & SLA

+ {/* 1. Gestion de l'erreur */} {error && ( @@ -26,13 +26,15 @@ export default function UptimeWidget() { )} {/* 4. Affichage des données */} + {!isLoading && !error && nodes.length > 0 && ( -
- {nodes.map((node) => ( - - ))} -
+ + {nodes.map((node) => ( + + ))} + )} +
); diff --git a/src/hooks/useDocumentDownload.ts b/src/hooks/useDocumentDownload.ts new file mode 100644 index 0000000..9ea383e --- /dev/null +++ b/src/hooks/useDocumentDownload.ts @@ -0,0 +1,45 @@ +import { useState } from 'react'; +// Importez votre instance PocketBase +import {pb} from '@/services/pocketbase'; +import type { Document } from '@/types/Document'; + +export function useDocumentDownload() { + const [isDownloading, setIsDownloading] = useState(false); + const [error, setError] = useState(null); + + const downloadFile = async (doc: Document) => { + try { + setIsDownloading(true); + setError(null); + + // 1. Demander un jeton éphémère de téléchargement (Sécurité Max) + // Ce jeton autorise le navigateur à télécharger un fichier protégé sans exposer votre session + const fileToken = await pb.files.getToken(); + + // 2. Générer l'URL du fichier + // ATTENTION : Pour que getUrl fonctionne, 'doc' DOIT contenir doc.id ET doc.collectionId (ou doc.collectionName) + const fileUrl = pb.files.getUrl(doc, doc.file); + + // 3. Ajouter le jeton sécurisé et forcer le téléchargement (?download=1) + const downloadUrl = `${fileUrl}?token=${fileToken}&download=1`; + + // 4. Créer un lien invisible pour forcer le navigateur à télécharger + const link = document.createElement("a"); + link.href = downloadUrl; + // On s'assure d'avoir un nom de fichier par défaut si doc.title est vide + link.setAttribute("download", doc.title || "document_aegis.pdf"); + + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + + } catch (err) { + console.error("Erreur lors du téléchargement sécurisé :", err); + setError("Accès refusé au coffre-fort documentaire."); + } finally { + setIsDownloading(false); + } + }; + + return { downloadFile, isDownloading, error }; +} \ No newline at end of file diff --git a/src/hooks/useDocuments.ts b/src/hooks/useDocuments.ts new file mode 100644 index 0000000..c12c858 --- /dev/null +++ b/src/hooks/useDocuments.ts @@ -0,0 +1,31 @@ +import { useState, useEffect, useCallback } from 'react'; +import { pb } from '@/services/pocketbase'; +import type { Document } from '@/types/Document'; + +export const useDocuments = () => { + const [documents, setDocuments] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + + const fetchDocuments = useCallback(async () => { + setIsLoading(true); + setError(null); + try { + const records = await pb.collection('aegis_documents_vault').getFullList({ + sort: '-created', // Du plus récent au plus ancien + }); + setDocuments(records); + } catch (err) { + console.error("Erreur lors de la récupération des documents :", err); + setError("Impossible de charger les documents."); + } finally { + setIsLoading(false); + } + }, []); + + useEffect(() => { + fetchDocuments(); + }, [fetchDocuments]); + + return { documents, isLoading, error, refetch: fetchDocuments }; +}; \ No newline at end of file diff --git a/src/hooks/useTheme.ts b/src/hooks/useTheme.ts new file mode 100644 index 0000000..83093a6 --- /dev/null +++ b/src/hooks/useTheme.ts @@ -0,0 +1,42 @@ +import { useState, useEffect } from 'react'; + +type Theme = 'light' | 'dark'; + +export function useTheme() { + // Initialisation de l'état avec une fonction pour ne lire le localStorage qu'une seule fois + const [theme, setTheme] = useState(() => { + // 1. On vérifie si un choix a déjà été sauvegardé + const storedTheme = localStorage.getItem('theme'); + if (storedTheme === 'dark' || storedTheme === 'light') { + return storedTheme; + } + + // 2. Sinon, on vérifie la préférence système de l'utilisateur + if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { + return 'dark'; + } + + // 3. Par défaut, mode clair + return 'light'; + }); + + // À chaque fois que 'theme' change, on met à jour le HTML et on sauvegarde + useEffect(() => { + const root = window.document.documentElement; + + if (theme === 'dark') { + root.classList.add('dark'); + } else { + root.classList.remove('dark'); + } + + localStorage.setItem('theme', theme); + }, [theme]); + + // Fonction utilitaire pour basculer facilement + const toggleTheme = () => { + setTheme((prevTheme) => (prevTheme === 'dark' ? 'light' : 'dark')); + }; + + return { theme, isDark: theme === 'dark', toggleTheme, setTheme }; +} \ No newline at end of file diff --git a/src/index.css b/src/index.css index 6ba9b65..788fe36 100644 --- a/src/index.css +++ b/src/index.css @@ -4,6 +4,8 @@ @tailwind components; @tailwind utilities; +@custom-variant dark (&:where(.dark, .dark *)); + /* --- VOS UTILITAIRES PERSONNALISÉS --- */ @layer utilities { .scrollbar-hide { diff --git a/src/pages/AccountSettings.tsx b/src/pages/AccountSettings.tsx index 2b893cd..3c196f3 100644 --- a/src/pages/AccountSettings.tsx +++ b/src/pages/AccountSettings.tsx @@ -1,88 +1,26 @@ +import PageHeader from "@/components/ui/PageHeader"; +import AccessWidget from "@/components/widgets/AccessWidget"; +import InformationWidget from "@/components/widgets/InformationWidget"; + export default function AccountSettings() { return ( -
- - {/* Header */} -
-
-
-

Paramètres du compte

-

Gestion de la sécurité et informations contractuelles

-
-
-
+
+ {/* Contenu Principal */} -
- +
+ {/* Header */} + +
- + {/* Colonne 1 : Sécurité */} -
-
-

- - Sécurité de l'accès -

- -
- {/* MFA Status */} -
-
-

Authentification à double facteur (MFA)

-

Exigée par les politiques de sécurité GISE

-
- - - Actif - -
- -
- -
- - {/* Mot de passe */} -
-

Mot de passe institutionnel

-

Dernière modification : Il y a 43 jours

- -
-
-
-
+ {/* Colonne 2 : Organisation & Contrat */} -
-
-

- - Profil de l'Organisation -

- -
-
-
Entité Légale
-
Cabinet Juridique Example & Associés
-
-
-
Administrateur du compte
-
Direction Générale (direction@example.com)
-
-
-
Niveau d'Infogérance (SLA)
-
- VIP PLATINUM - Couverture 24/7 (99.99%) -
-
-
-
-
+
diff --git a/src/pages/DocumentVault.tsx b/src/pages/DocumentVault.tsx index 7d3ddd4..6a61374 100644 --- a/src/pages/DocumentVault.tsx +++ b/src/pages/DocumentVault.tsx @@ -1,119 +1,32 @@ +import { Input } from '@/components/ui/Input'; +import PageHeader from '@/components/ui/PageHeader'; +import DocumentsWidget from '@/components/widgets/DocumentsWidget'; import { useState } from 'react'; -// Données fictives pour le maquettage visuel -const mockDocuments = [ - { id: 'DOC-102', title: 'Rapport d\'Audit de Vulnérabilité Q2', category: 'Audit', date: '12 Juil. 2026', size: '2.4 MB', encrypted: true }, - { id: 'DOC-101', title: 'Facture Infogérance - Juin 2026', category: 'Facture', date: '01 Juil. 2026', size: '1.1 MB', encrypted: true }, - { id: 'DOC-095', title: 'Certificat de Conformité ISO 27001', category: 'Rapport de conformité', date: '15 Jan. 2026', size: '4.8 MB', encrypted: true } -]; - export default function DocumentVault() { const [searchTerm, setSearchTerm] = useState(''); - const getCategoryBadge = (category: string) => { - switch (category) { - case 'Audit': return 'bg-purple-50 text-purple-700 border-purple-200'; - case 'Facture': return 'bg-slate-100 text-slate-700 border-slate-200'; - case 'Rapport de conformité': return 'bg-emerald-50 text-emerald-700 border-emerald-200'; - default: return 'bg-blue-50 text-blue-700 border-blue-200'; - } - }; - return ( -
- - {/* Header du Coffre-Fort */} -
-
-
-

Coffre-Fort Documentaire

-

- - - - Espace de stockage chiffré de bout en bout -

-
-
-
+
{/* Contenu Principal */} -
- +
+ + + {/* Barre de recherche (Visuelle) */} -
-
-
- - - -
- setSearchTerm(e.target.value)} - className="block w-full pl-10 pr-3 py-2 border border-slate-300 rounded-lg leading-5 bg-white placeholder-slate-400 focus:outline-none focus:ring-1 focus:ring-blue-900 focus:border-blue-900 sm:text-sm shadow-sm" - /> -
-
+ setSearchTerm(e.target.value)} + /> {/* Liste des Documents (La Carte) */} -
- - - - - - - - - - - {mockDocuments.map((doc) => ( - - - - - - - ))} - -
- Nom du fichier - - Catégorie - - Ajouté le - - Action -
-
- - - -
-
{doc.title}
-
{doc.size} • PDF
-
-
-
- - {doc.category} - - - {doc.date} - - -
-
- +
-
+
); } \ No newline at end of file diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx index 6e932bf..0eaa153 100644 --- a/src/pages/Login.tsx +++ b/src/pages/Login.tsx @@ -2,42 +2,32 @@ import { useLoginFlow } from '@/hooks/useLoginFlow'; import { LocalLoginForm } from '@/components/auth/LocalLoginForm'; import { MfaForm } from '@/components/auth/MfaForm'; import { Card, CardContent } from '@/components/ui/Card'; +import LogoGise from '@/components/ui/LogoGise'; +import { Button } from '@/components/ui/Button'; +import { GlobeLock } from 'lucide-react'; +import Badge from '@/components/ui/Badge'; interface LoginProps { onLoginSuccess: () => void; } export default function Login({ onLoginSuccess }: LoginProps) { - const { - step, isLoading, error, mfaConfig, - loginWithZitadel, loginWithLocal, verifyMfa, cancelMfa + const { + step, isLoading, error, mfaConfig, + loginWithZitadel, loginWithLocal, verifyMfa, cancelMfa } = useLoginFlow(onLoginSuccess); return ( -
- +
+ {/* HEADER LOGO */} -
-
-
- - - -
-
-

- AEGIS by GISE -

-

- Accès restreint -

-
+ {/* BOÎTE CENTRALE DÉCLINÉE AVEC NOTRE OBJET CARD */} -
- +
+ - + {/* AFFICHAGE DES ERREURS GLOBALES */} {error && (
@@ -47,32 +37,28 @@ export default function Login({ onLoginSuccess }: LoginProps) { {/* ROUTAGE INTERNE DES ÉTAPES */} {step === 1 ? ( -
- +
- Ou via vos identifiants locaux +
) : ( - -
+
+
{/* HEADER EXTRAIT */}