From c7389d747b3bb57b18c83aeca186880e12d76e60 Mon Sep 17 00:00:00 2001 From: LathanDevers Date: Fri, 31 Jul 2026 18:50:05 +0200 Subject: [PATCH 1/6] rebase --- src/pages/DocumentVault.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/DocumentVault.tsx b/src/pages/DocumentVault.tsx index 7d3ddd4..1e573ef 100644 --- a/src/pages/DocumentVault.tsx +++ b/src/pages/DocumentVault.tsx @@ -82,7 +82,7 @@ export default function DocumentVault() {
- +
From 6b93d21d1931b26911b9fcd7860f41160ba993f4 Mon Sep 17 00:00:00 2001 From: LathanDevers Date: Fri, 31 Jul 2026 21:37:56 +0200 Subject: [PATCH 2/6] add useDocuments and load saved documents in DocumentsVault --- src/App.css | 184 ------------------ src/components/layout/Sidebar.tsx | 14 +- src/components/ui/Card.tsx | 5 +- src/components/ui/ServiceObject.tsx | 2 +- src/components/ui/ThemeToggle.tsx | 52 +++++ src/components/ui/UptimeObject.tsx | 38 ++-- src/components/widgets/DocumentsWidget.tsx | 77 ++++++++ .../widgets/EvolutionOptionCard.tsx | 2 +- src/components/widgets/UptimeWidget.tsx | 16 +- src/hooks/useDocuments.ts | 31 +++ src/hooks/useTheme.ts | 42 ++++ src/index.css | 2 + src/pages/DocumentVault.tsx | 84 ++------ src/pages/ServiceDesk.tsx | 4 +- src/styles/Neumorphism.ts | 14 +- src/types/Document.ts | 7 + 16 files changed, 280 insertions(+), 294 deletions(-) create mode 100644 src/components/ui/ThemeToggle.tsx create mode 100644 src/components/widgets/DocumentsWidget.tsx create mode 100644 src/hooks/useDocuments.ts create mode 100644 src/hooks/useTheme.ts create mode 100644 src/types/Document.ts 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/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/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/DocumentsWidget.tsx b/src/components/widgets/DocumentsWidget.tsx new file mode 100644 index 0000000..8230f7a --- /dev/null +++ b/src/components/widgets/DocumentsWidget.tsx @@ -0,0 +1,77 @@ +import React from 'react'; +import { useDocuments } from '@/hooks/useDocuments'; +import { Button } from '../ui/Button'; + +interface DocumentsWidgetProps { + onSelectDocument: (id: string) => void; +} + +export function DocumentsWidget(){ + const { documents } = useDocuments(); + + 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 ( +
+ + + + + + + + + + + {documents.map((doc) => ( + + + + + + + ))} + +
+ Nom du fichier + + Catégorie + + Ajouté le + + Action +
+
+ + + +
+
{doc.title}
+
{doc.file.size} • PDF
+
+
+
+ + {doc.category} + + + {doc.created.split(" ")[0]+" "+doc.created.split(" ")[1].split(":")[0]+":"+doc.created.split(" ")[1].split(":")[1]} + + +
+
+ ); +}; + +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/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/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/DocumentVault.tsx b/src/pages/DocumentVault.tsx index 1e573ef..f4576aa 100644 --- a/src/pages/DocumentVault.tsx +++ b/src/pages/DocumentVault.tsx @@ -1,3 +1,6 @@ +import { Card } from '@/components/ui/Card'; +import PageHeader from '@/components/ui/PageHeader'; +import DocumentsWidget from '@/components/widgets/DocumentsWidget'; import { useState } from 'react'; // Données fictives pour le maquettage visuel @@ -20,26 +23,15 @@ export default function DocumentVault() { }; return ( -
- - {/* Header du Coffre-Fort */} -
-
-
-

Coffre-Fort Documentaire

-

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

-
-
-
+
{/* Contenu Principal */} -
- +
+ + + {/* Barre de recherche (Visuelle) */}
@@ -59,61 +51,9 @@ export default function DocumentVault() {
{/* 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/ServiceDesk.tsx b/src/pages/ServiceDesk.tsx index f5cb9a8..f02754d 100644 --- a/src/pages/ServiceDesk.tsx +++ b/src/pages/ServiceDesk.tsx @@ -7,8 +7,8 @@ export default function ServiceDesk() { const navigate = useNavigate(); return ( -
-
+
+
{/* HEADER EXTRAIT */} Date: Sat, 1 Aug 2026 22:24:49 +0200 Subject: [PATCH 3/6] add downloadable content in vault --- src/components/ui/Button.tsx | 2 +- src/components/ui/DocumentObject.tsx | 63 ++++++++++++++++++++++ src/components/ui/TicketRow.tsx | 2 +- src/components/widgets/DocumentsWidget.tsx | 49 +++-------------- src/components/widgets/TicketsWidget.tsx | 2 +- src/hooks/useDocumentDownload.ts | 45 ++++++++++++++++ src/pages/DocumentVault.tsx | 14 +---- src/styles/Neumorphism.ts | 38 ++++++------- src/types/Document.ts | 14 +++-- 9 files changed, 148 insertions(+), 81 deletions(-) create mode 100644 src/components/ui/DocumentObject.tsx create mode 100644 src/hooks/useDocumentDownload.ts diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx index 962e162..75177bf 100644 --- a/src/components/ui/Button.tsx +++ b/src/components/ui/Button.tsx @@ -64,7 +64,7 @@ export const Button = forwardRef( {isLoading && ( diff --git a/src/components/ui/DocumentObject.tsx b/src/components/ui/DocumentObject.tsx new file mode 100644 index 0000000..0403c79 --- /dev/null +++ b/src/components/ui/DocumentObject.tsx @@ -0,0 +1,63 @@ +import React from 'react'; +import { Download } from 'lucide-react'; +import { cn } from '@/utils/utils'; +import { NEUMORPHISM } from '@/styles/Neumorphism'; +import type { Document } from '@/types/Document'; +import { Button } from './Button'; +import { useDocumentDownload } from '@/hooks/useDocumentDownload'; + +interface DocumentObjectProps { + doc: Document; +} + +export const DocumentObject: React.FC = ({ doc }) => { + // 2. On initialise le Hook pour ce document spécifique + const { downloadFile, isDownloading } = useDocumentDownload(); + + 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 ( +
+ +
+ + + +
+
{doc.title}
+
{doc.file} • PDF
+
+
+ + + + {doc.category} + + + + {/* Petit conseil d'optimisation : utiliser une fonction date pour éviter les crashs si doc.created est mal formaté */} + {doc.created ? `${doc.created.split(" ")[0]} ${doc.created.split(" ")[1]?.substring(0,5)}` : 'N/A'} + + + {/* 3. On relie le bouton à l'état et à la fonction du Hook */} + + + + ); +}; \ 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/widgets/DocumentsWidget.tsx b/src/components/widgets/DocumentsWidget.tsx index 8230f7a..a684c58 100644 --- a/src/components/widgets/DocumentsWidget.tsx +++ b/src/components/widgets/DocumentsWidget.tsx @@ -1,6 +1,8 @@ import React from 'react'; import { useDocuments } from '@/hooks/useDocuments'; +import {DocumentObject} from '@ui/DocumentObject' import { Button } from '../ui/Button'; +import { Card } from '../ui/Card'; interface DocumentsWidgetProps { onSelectDocument: (id: string) => void; @@ -8,19 +10,10 @@ interface DocumentsWidgetProps { export function DocumentsWidget(){ const { documents } = useDocuments(); - - 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 ( -
+ - + - + {documents.map((doc) => ( - - - - - - + ))}
Nom du fichier @@ -36,41 +29,13 @@ export function DocumentsWidget(){
-
- - - -
-
{doc.title}
-
{doc.file.size} • PDF
-
-
-
- - {doc.category} - - - {doc.created.split(" ")[0]+" "+doc.created.split(" ")[1].split(":")[0]+":"+doc.created.split(" ")[1].split(":")[1]} - - -
-
+ ); }; 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/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/pages/DocumentVault.tsx b/src/pages/DocumentVault.tsx index f4576aa..f34cb92 100644 --- a/src/pages/DocumentVault.tsx +++ b/src/pages/DocumentVault.tsx @@ -1,4 +1,5 @@ import { Card } from '@/components/ui/Card'; +import { Input } from '@/components/ui/Input'; import PageHeader from '@/components/ui/PageHeader'; import DocumentsWidget from '@/components/widgets/DocumentsWidget'; import { useState } from 'react'; @@ -13,15 +14,6 @@ const mockDocuments = [ 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 (
@@ -40,19 +32,17 @@ export default function DocumentVault() {
- 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" />
{/* Liste des Documents (La Carte) */} -
); diff --git a/src/styles/Neumorphism.ts b/src/styles/Neumorphism.ts index d660ac8..3ab86a8 100644 --- a/src/styles/Neumorphism.ts +++ b/src/styles/Neumorphism.ts @@ -3,31 +3,31 @@ export const NEUMORPHISM = { // Reliefs au repos - lightShadow: "shadow-[6px_6px_12px_#c5c5c5,-6px_-6px_12px_#ffffff]", - darkShadow: "dark:shadow-[6px_6px_12px_#121926,-6px_-6px_12px_#2a3850]", + lightShadow: "shadow-[6px_6px_12px_#c5c5c5,-6px_-6px_12px_#ffffff]", + darkShadow: "dark:shadow-[6px_6px_12px_#121926,-6px_-6px_12px_#2a3850]", - lightShadowHover: "hover:shadow-[6px_6px_12px_#c5c5c5,-6px_-6px_12px_#ffffff]", - darkShadowHover: "dark:hover:shadow-[6px_6px_12px_#121926,-6px_-6px_12px_#2a3850]", + lightShadowHover: "hover:shadow-[6px_6px_12px_#c5c5c5,-6px_-6px_12px_#ffffff]", + darkShadowHover: "dark:hover:shadow-[6px_6px_12px_#121926,-6px_-6px_12px_#2a3850]", - // Ombres Neumorphic quand on clique (enfoncé) - lightActive: "active:shadow-[4px_4px_12px_#c5c5c5,-4px_-4px_12px_#ffffff]", - darkActive: "dark:active:shadow-[4px_4px_12px_#121926,-4px_-4px_12px_#2a3850]", + // Ombres Neumorphic quand on clique (enfoncé) + lightActive: "active:shadow-[4px_4px_12px_#c5c5c5,-4px_-4px_12px_#ffffff]", + darkActive: "dark:active:shadow-[4px_4px_12px_#121926,-4px_-4px_12px_#2a3850]", - - insetLightActive: "active:shadow-[inset_4px_4px_8px_#c5c5c5,inset_-4px_-4px_8px_#ffffff]", - insetDarkActive: "dark:active:shadow-[inset_4px_4px_8px_#121926,inset_-4px_-4px_8px_#2a3850]", - insetLightHover: "hover:shadow-[inset_4px_4px_8px_#c5c5c5,inset_-4px_-4px_8px_#ffffff]", - insetDarkHover: "dark:hover:shadow-[inset_4px_4px_8px_#121926,inset_-4px_-4px_8px_#2a3850]", + insetLightActive: "active:shadow-[inset_4px_4px_8px_#c5c5c5,inset_-4px_-4px_8px_#ffffff]", + insetDarkActive: "dark:active:shadow-[inset_4px_4px_8px_#121926,inset_-4px_-4px_8px_#2a3850]", - sizeStyles: { - sm: "px-5 py-2 text-sm rounded-md", - md: "px-[1.7em] py-[0.7em] text-[18px] rounded-[0.5em]", + insetLightHover: "hover:shadow-[inset_4px_4px_8px_#c5c5c5,inset_-4px_-4px_8px_#ffffff]", + insetDarkHover: "dark:hover:shadow-[inset_4px_4px_8px_#121926,inset_-4px_-4px_8px_#2a3850]", + + sizeStyles: { + sm: "px-5 py-2 text-sm rounded-md", + md: "px-[1.7em] py-[0.7em] text-[18px] rounded-[0.5em]", lg: "px-10 py-4 text-xl rounded-xl", icon: "p-3 rounded-lg", - }, + }, // Ombres du bouton ACTIF (effet enfoncé/creusé "inset") - insetLight: "shadow-[inset_4px_4px_8px_#c5c5c5,inset_-4px_-4px_8px_#ffffff]", - insetDark: "dark:shadow-[inset_4px_4px_8px_#121926,inset_-4px_-4px_8px_#2a3850]", + insetLight: "shadow-[inset_4px_4px_8px_#c5c5c5,inset_-4px_-4px_8px_#ffffff]", + insetDark: "dark:shadow-[inset_4px_4px_8px_#121926,inset_-4px_-4px_8px_#2a3850]", - } as const; \ No newline at end of file +} as const; \ No newline at end of file diff --git a/src/types/Document.ts b/src/types/Document.ts index a2b7f4e..df3c7ac 100644 --- a/src/types/Document.ts +++ b/src/types/Document.ts @@ -1,7 +1,11 @@ export interface Document { - id: string; - title: string; - category: string; - file: File; - created: string; + id: string; + collectionId: string; // <-- OBLIGATOIRE pour pb.files.getUrl() + collectionName: string; // (ex: 'aegis_documents_vault') + created: string; + updated: string; + title: string; + file: string; // Le nom du fichier généré par PocketBase (ex: 'rapport_7b3a.pdf') + category: string; + company?: string; // L'ID du client (Relation) } \ No newline at end of file From 25c75ba078943fb226866fac63141578b55627dc Mon Sep 17 00:00:00 2001 From: LathanDevers Date: Sat, 1 Aug 2026 22:25:39 +0200 Subject: [PATCH 4/6] add downloadable content in vault --- src/components/ui/DocumentObject.tsx | 1 - src/components/widgets/DocumentsWidget.tsx | 6 ------ src/pages/DocumentVault.tsx | 8 -------- 3 files changed, 15 deletions(-) diff --git a/src/components/ui/DocumentObject.tsx b/src/components/ui/DocumentObject.tsx index 0403c79..1638b5c 100644 --- a/src/components/ui/DocumentObject.tsx +++ b/src/components/ui/DocumentObject.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { Download } from 'lucide-react'; import { cn } from '@/utils/utils'; -import { NEUMORPHISM } from '@/styles/Neumorphism'; import type { Document } from '@/types/Document'; import { Button } from './Button'; import { useDocumentDownload } from '@/hooks/useDocumentDownload'; diff --git a/src/components/widgets/DocumentsWidget.tsx b/src/components/widgets/DocumentsWidget.tsx index a684c58..c583764 100644 --- a/src/components/widgets/DocumentsWidget.tsx +++ b/src/components/widgets/DocumentsWidget.tsx @@ -1,13 +1,7 @@ -import React from 'react'; import { useDocuments } from '@/hooks/useDocuments'; import {DocumentObject} from '@ui/DocumentObject' -import { Button } from '../ui/Button'; import { Card } from '../ui/Card'; -interface DocumentsWidgetProps { - onSelectDocument: (id: string) => void; -} - export function DocumentsWidget(){ const { documents } = useDocuments(); return ( diff --git a/src/pages/DocumentVault.tsx b/src/pages/DocumentVault.tsx index f34cb92..70cda68 100644 --- a/src/pages/DocumentVault.tsx +++ b/src/pages/DocumentVault.tsx @@ -1,16 +1,8 @@ -import { Card } from '@/components/ui/Card'; 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(''); From 80dc61b9383e1008a0687aa8d09bec739836eb7a Mon Sep 17 00:00:00 2001 From: LathanDevers Date: Sun, 2 Aug 2026 21:22:32 +0200 Subject: [PATCH 5/6] end cleanup --- src/components/auth/LocalLoginForm.tsx | 9 +- src/components/ui/Badge.tsx | 23 +++++ src/components/ui/Card.tsx | 4 +- src/components/ui/LogoGise.tsx | 20 +++++ src/components/ui/StatusBadge.tsx | 2 - src/components/widgets/AccessWidget.tsx | 43 +++++++++ src/components/widgets/InformationWidget.tsx | 37 ++++++++ src/pages/AccountSettings.tsx | 92 ++++---------------- src/pages/DocumentVault.tsx | 21 ++--- src/pages/Login.tsx | 52 ++++------- 10 files changed, 169 insertions(+), 134 deletions(-) create mode 100644 src/components/ui/Badge.tsx create mode 100644 src/components/ui/LogoGise.tsx create mode 100644 src/components/widgets/AccessWidget.tsx create mode 100644 src/components/widgets/InformationWidget.tsx 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/ui/Badge.tsx b/src/components/ui/Badge.tsx new file mode 100644 index 0000000..00bd24e --- /dev/null +++ b/src/components/ui/Badge.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { cn } from '@/utils/utils'; + +interface BadgeProps { + className?: string; + name: string +} + +export const StatusBadge: React.FC = ({ className, name }) => { + + return ( +
+ {name} +
+ ); +}; + +export default StatusBadge; \ No newline at end of file diff --git a/src/components/ui/Card.tsx b/src/components/ui/Card.tsx index df858c0..8ee89ca 100644 --- a/src/components/ui/Card.tsx +++ b/src/components/ui/Card.tsx @@ -43,7 +43,7 @@ export const CardHeader = React.forwardRef (
) 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/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/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/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/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 70cda68..6a61374 100644 --- a/src/pages/DocumentVault.tsx +++ b/src/pages/DocumentVault.tsx @@ -17,21 +17,12 @@ export default function DocumentVault() {
{/* Barre de recherche (Visuelle) */} -
-
-
- - - -
- setSearchTerm(e.target.value)} - /> -
-
+ setSearchTerm(e.target.value)} + /> {/* Liste des Documents (La Carte) */} 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 +
) : ( - Date: Sun, 2 Aug 2026 22:21:57 +0200 Subject: [PATCH 6/6] end cleaning --- src/components/widgets/BackupWidget.tsx | 29 +++++++++------------ src/components/widgets/NewTicketWidget.tsx | 12 +++------ src/components/widgets/SecurityWidget.tsx | 12 ++++----- src/components/widgets/TicketChatWidget.tsx | 16 ++++++------ src/components/widgets/TicketInfoWidget.tsx | 6 ++--- 5 files changed, 34 insertions(+), 41 deletions(-) diff --git a/src/components/widgets/BackupWidget.tsx b/src/components/widgets/BackupWidget.tsx index 65da02e..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/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 :