From 85adfaab2d04d00e01aeecbe71ceb4503f15cdd5 Mon Sep 17 00:00:00 2001 From: LathanDevers Date: Tue, 4 Aug 2026 16:51:00 +0200 Subject: [PATCH 1/3] correct user settings manager (and password reset) --- src/App.tsx | 2 + src/components/ui/Badge.tsx | 4 +- src/components/widgets/AccessWidget.tsx | 93 +++++++---- src/components/widgets/InformationWidget.tsx | 70 ++++++--- src/hooks/useDocumentDownload.ts | 3 +- src/hooks/useUserInformations.ts | 30 ++++ src/pages/AccountSettings.tsx | 22 +-- .../accountsettings/UpdatePasswordPage.tsx | 145 ++++++++++++++++++ src/types/Company.ts | 8 + src/types/User.ts | 12 ++ vite.config.ts | 28 ++-- 11 files changed, 339 insertions(+), 78 deletions(-) create mode 100644 src/hooks/useUserInformations.ts create mode 100644 src/pages/accountsettings/UpdatePasswordPage.tsx create mode 100644 src/types/Company.ts create mode 100644 src/types/User.ts diff --git a/src/App.tsx b/src/App.tsx index b6d1963..a4ffb84 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -14,6 +14,7 @@ import Analytics from '@/pages/trustcenter/Analytics'; import Backups from '@/pages/trustcenter/Backups'; import NewTicket from '@/pages/servicedesk/NewTicket'; import TicketDetail from '@/pages/servicedesk/TicketDetail'; +import UpdatePasswordPage from './pages/accountsettings/UpdatePasswordPage'; function App() { const [isAuthenticated, setIsAuthenticated] = useState(pb.authStore.isValid); @@ -63,6 +64,7 @@ function App() { } /> } /> } /> + } /> {/* Sécurité : Si l'URL n'existe pas, on redirige vers le dashboard */} } /> diff --git a/src/components/ui/Badge.tsx b/src/components/ui/Badge.tsx index 00bd24e..0eff359 100644 --- a/src/components/ui/Badge.tsx +++ b/src/components/ui/Badge.tsx @@ -6,7 +6,7 @@ interface BadgeProps { name: string } -export const StatusBadge: React.FC = ({ className, name }) => { +export const Badge: React.FC = ({ className, name }) => { return (
= ({ className, name }) => { ); }; -export default StatusBadge; \ No newline at end of file +export default Badge; \ No newline at end of file diff --git a/src/components/widgets/AccessWidget.tsx b/src/components/widgets/AccessWidget.tsx index 4c54c76..9819af2 100644 --- a/src/components/widgets/AccessWidget.tsx +++ b/src/components/widgets/AccessWidget.tsx @@ -1,43 +1,74 @@ -import { Card, CardContent, CardHeader } from '@/components/ui/Card'; -import { Lock } from "lucide-react"; +import React from 'react'; +import { Loader, Lock, KeyRound } from "lucide-react"; +import { useNavigate } from "react-router-dom"; // Assurez-vous d'utiliser react-router +import { Card } from '@/components/ui/Card'; +import { Button } from '@/components/ui/Button'; +import StatusBadge from '../ui/StatusBadge'; +import type { User } from '@/types/User'; -export function AccessWidget() { +interface AccessWidgetProps { + user: User | null; + isLoading: boolean; + error: string | null; +} + +export const AccessWidget: React.FC = ({ + user, + isLoading, + error, +}) => { + // Hook de navigation + const navigate = useNavigate(); return ( - - -

Sécurité de l'accès

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

Authentification à double facteur (MFA)

-

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

+ +
+

+
+
- - - Actif - -

+ Sécurité de l'accès + +
-
- + {isLoading && !error && ( +
+
+ )} - {/* Mot de passe */} -
-

Mot de passe institutionnel

-

Dernière modification : Il y a 43 jours

- + {!isLoading && !error && ( +
+ + {/* MFA */} +
+
+

Authentification (MFA)

+

Exigée par GISE

+
+ +
+ + {/* MOT DE PASSE (Lien vers la sous-page) */} +
+

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 index ea59a71..514dbd8 100644 --- a/src/components/widgets/InformationWidget.tsx +++ b/src/components/widgets/InformationWidget.tsx @@ -1,7 +1,33 @@ import { Card, CardContent, CardHeader } from '@/components/ui/Card'; +import type { Company } from '@/types/Company'; +import type { User } from '@/types/User'; +import { Loader } from '@/components/ui/Loader'; import { Building } from "lucide-react"; -export function InformationWidget() { +interface InformationWidgetProps { + user: User | null; + company: Company | null; + isLoading: boolean; + error: string | null; +} + +function setVipBadge(vip_level: string | undefined) { + switch (vip_level?.toLowerCase()) { + case "platinum": + return {vip_level?.toUpperCase()}; + case "premium": + return {vip_level?.toUpperCase()}; + default: + return {vip_level?.toUpperCase()}; + } +} + +export const InformationWidget: React.FC = ({ + user, + company, + isLoading, + error, +}) => { return ( @@ -11,25 +37,29 @@ export function InformationWidget() { - -
-
-
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%) -
-
-
-
+ {isLoading && !error && } + {!isLoading && !error && ( + +
+
+
Entité Légale
+
{company?.name}
+
+
+
Administrateur du compte
+
{user?.role} {user?.name}
+
Email : {user?.emailVisibility && user?.email}{!user?.emailVisibility && "caché"}
+
+
+
Niveau d'Infogérance (SLA)
+
+ {setVipBadge(company?.vip_level)} + Couverture 24/7 (99.99%) +
+
+
+
+ )}
); } diff --git a/src/hooks/useDocumentDownload.ts b/src/hooks/useDocumentDownload.ts index 9ea383e..c422e43 100644 --- a/src/hooks/useDocumentDownload.ts +++ b/src/hooks/useDocumentDownload.ts @@ -33,8 +33,7 @@ export function useDocumentDownload() { link.click(); document.body.removeChild(link); - } catch (err) { - console.error("Erreur lors du téléchargement sécurisé :", err); + } catch { setError("Accès refusé au coffre-fort documentaire."); } finally { setIsDownloading(false); diff --git a/src/hooks/useUserInformations.ts b/src/hooks/useUserInformations.ts new file mode 100644 index 0000000..1d037fd --- /dev/null +++ b/src/hooks/useUserInformations.ts @@ -0,0 +1,30 @@ +import { pb } from "@/services/pocketbase"; +import type { Company } from "@/types/Company"; +import type { User } from "@/types/User"; +import { useEffect, useState } from "react"; + +export const useUserInformations = () => { + const [user, setUser] = useState(null); + const [company, setCompany] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchInformations = async () => { + setIsLoading(true); + try { + const userRecords = await pb.collection('aegis_users').getFullList(); + setUser(userRecords[0]); + const companyRecords = await pb.collection('aegis_companies').getFullList(); + setCompany(companyRecords[0]) + } catch (err) { + setError("Erreur lors de la récupération des infos utilisateur :"+err); + } finally { + setIsLoading(false); + } + }; + fetchInformations(); + }, []); + + return { user, company, isLoading, error }; +}; \ No newline at end of file diff --git a/src/pages/AccountSettings.tsx b/src/pages/AccountSettings.tsx index 3c196f3..9051e03 100644 --- a/src/pages/AccountSettings.tsx +++ b/src/pages/AccountSettings.tsx @@ -1,29 +1,33 @@ import PageHeader from "@/components/ui/PageHeader"; import AccessWidget from "@/components/widgets/AccessWidget"; import InformationWidget from "@/components/widgets/InformationWidget"; +import { useUserInformations } from "@/hooks/useUserInformations"; export default function AccountSettings() { + const { user, company, isLoading, error } = useUserInformations(); return (
- - {/* Contenu Principal */}
{/* Header */} -
- {/* Colonne 1 : Sécurité */} - - + {/* Colonne 2 : Organisation & Contrat */} - - +
-
); diff --git a/src/pages/accountsettings/UpdatePasswordPage.tsx b/src/pages/accountsettings/UpdatePasswordPage.tsx new file mode 100644 index 0000000..e86ab99 --- /dev/null +++ b/src/pages/accountsettings/UpdatePasswordPage.tsx @@ -0,0 +1,145 @@ +import React, { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { KeyRound, AlertCircle, ShieldCheck } from "lucide-react"; +import { Card } from '@/components/ui/Card'; +import { Input } from '@/components/ui/Input'; +import { Button } from '@/components/ui/Button'; +import { pb } from '@/services/pocketbase'; +import BackButton from '@/components/ui/BackButton'; +import { NEUMORPHISM } from '@/styles/Neumorphism'; + +export default function UpdatePasswordPage() { + const navigate = useNavigate(); + + const [isUpdating, setIsUpdating] = useState(false); + const [feedback, setFeedback] = useState<{ type: 'error' | 'success', message: string } | null>(null); + + const [formData, setFormData] = useState({ + oldPassword: '', + newPassword: '', + confirmPassword: '' + }); + + const handlePasswordChange = async (e: React.FormEvent) => { + e.preventDefault(); + setIsUpdating(true); + setFeedback(null); + + if (formData.newPassword !== formData.confirmPassword) { + setFeedback({ type: 'error', message: "Les nouveaux mots de passe ne correspondent pas." }); + setIsUpdating(false); + return; + } + + if (formData.newPassword.length < 8) { + setFeedback({ type: 'error', message: "Le mot de passe doit contenir au moins 8 caractères minimum." }); + setIsUpdating(false); + return; + } + + try { + const userId = pb.authStore.model?.id; + if (!userId) throw new Error("Non authentifié."); + + await pb.collection("users").update(userId, { + oldPassword: formData.oldPassword, + password: formData.newPassword, + passwordConfirm: formData.confirmPassword, + }); + + setFeedback({ type: 'success', message: "Votre mot de passe a été modifié avec succès." }); + setFormData({ oldPassword: '', newPassword: '', confirmPassword: '' }); + + // Retour automatique après 2.5 secondes + setTimeout(() => navigate(-1), 2500); + + } catch { + setFeedback({ type: 'error', message: "Ancien mot de passe incorrect ou erreur réseau." }); + } finally { + setIsUpdating(false); + } + }; + + const insetNeu = "shadow-[inset_2px_2px_4px_#c5c5c5,inset_-2px_-2px_4px_#ffffff] dark:shadow-[inset_2px_2px_4px_#121926,inset_-2px_-2px_4px_#2a3850]"; + + return ( +
+ + {/* BOUTON DE RETOUR EXTRAIT */} + + + {/* LE FORMULAIRE */} + +
+
+ +
+
+

+ Renouvellement du mot de passe +

+

+ Veuillez choisir un mot de passe fort et unique pour sécuriser votre accès AEGIS. +

+
+
+ +
+
+ setFormData({ ...formData, oldPassword: e.target.value })} + /> +
+ +
+ setFormData({ ...formData, newPassword: e.target.value })} + /> + setFormData({ ...formData, confirmPassword: e.target.value })} + /> +
+ + {/* Zone de Feedback */} + {feedback && ( +
+ {feedback.type === 'error' ? : } + {feedback.message} + {feedback.type === 'success' && Redirection...} +
+ )} + +
+ +
+
+
+
+ ); +} \ No newline at end of file diff --git a/src/types/Company.ts b/src/types/Company.ts new file mode 100644 index 0000000..addb1d4 --- /dev/null +++ b/src/types/Company.ts @@ -0,0 +1,8 @@ +export interface Company { + id: string; + name: string; + no: string; + vip_level: string; + created: string; + updated: string +} \ No newline at end of file diff --git a/src/types/User.ts b/src/types/User.ts new file mode 100644 index 0000000..fe0e146 --- /dev/null +++ b/src/types/User.ts @@ -0,0 +1,12 @@ +export interface User { + id: string; + email: string; + emailVisibility: boolean; + verified: boolean; + name: string; + role: string; + company: string; + mfa_enabled: boolean; + created: string; + updated: string +} \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index 9055c80..427f36f 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -11,20 +11,20 @@ export default defineConfig({ ], resolve: { alias: { - '@': path.resolve(__dirname, './src'), - '@components': path.resolve(__dirname, './src/components'), - '@ui': path.resolve(__dirname, './src/components/ui'), - '@common': path.resolve(__dirname, './src/components/common'), - '@pages': path.resolve(__dirname, './src/pages'), - '@hooks': path.resolve(__dirname, './src/hooks'), - '@layouts': path.resolve(__dirname, './src/layouts'), - '@features': path.resolve(__dirname, './src/features'), - '@services': path.resolve(__dirname, './src/services'), - '@utils': path.resolve(__dirname, './src/utils'), - '@assets': path.resolve(__dirname, './src/assets'), - '@data': path.resolve(__dirname, './src/data'), - '@widgets': path.resolve(__dirname, './src/components/widgets'), - '@styles': path.resolve(__dirname, './src/styles'), + '@': path.resolve(import.meta.dirname, './src'), + '@components': path.resolve(import.meta.dirname, './src/components'), + '@ui': path.resolve(import.meta.dirname, './src/components/ui'), + '@common': path.resolve(import.meta.dirname, './src/components/common'), + '@pages': path.resolve(import.meta.dirname, './src/pages'), + '@hooks': path.resolve(import.meta.dirname, './src/hooks'), + '@layouts': path.resolve(import.meta.dirname, './src/layouts'), + '@features': path.resolve(import.meta.dirname, './src/features'), + '@services': path.resolve(import.meta.dirname, './src/services'), + '@utils': path.resolve(import.meta.dirname, './src/utils'), + '@assets': path.resolve(import.meta.dirname, './src/assets'), + '@data': path.resolve(import.meta.dirname, './src/data'), + '@widgets': path.resolve(import.meta.dirname, './src/components/widgets'), + '@styles': path.resolve(import.meta.dirname, './src/styles'), } } }) -- 2.47.3 From 6a979c70a03de98a72507f141b366524f448e42e Mon Sep 17 00:00:00 2001 From: LathanDevers Date: Tue, 4 Aug 2026 16:51:15 +0200 Subject: [PATCH 2/3] correct user settings manager (and password reset) --- src/pages/accountsettings/UpdatePasswordPage.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pages/accountsettings/UpdatePasswordPage.tsx b/src/pages/accountsettings/UpdatePasswordPage.tsx index e86ab99..785ea57 100644 --- a/src/pages/accountsettings/UpdatePasswordPage.tsx +++ b/src/pages/accountsettings/UpdatePasswordPage.tsx @@ -59,9 +59,6 @@ export default function UpdatePasswordPage() { setIsUpdating(false); } }; - - const insetNeu = "shadow-[inset_2px_2px_4px_#c5c5c5,inset_-2px_-2px_4px_#ffffff] dark:shadow-[inset_2px_2px_4px_#121926,inset_-2px_-2px_4px_#2a3850]"; - return (
-- 2.47.3 From 75a15cbd54f997d07bbbb1764d3e5163b8a8c92d Mon Sep 17 00:00:00 2001 From: LathanDevers Date: Sun, 9 Aug 2026 15:01:40 +0200 Subject: [PATCH 3/3] update nanoid --- pnpm-lock.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e854109..ce6f3f0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1038,8 +1038,8 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - nanoid@3.3.16: - resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} + nanoid@3.3.18: + resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -2202,7 +2202,7 @@ snapshots: ms@2.1.3: {} - nanoid@3.3.16: {} + nanoid@3.3.18: {} natural-compare@1.4.0: {} @@ -2249,7 +2249,7 @@ snapshots: postcss@8.5.25: dependencies: - nanoid: 3.3.16 + nanoid: 3.3.18 picocolors: 1.1.1 source-map-js: 1.2.1 -- 2.47.3