From d0bafacaba9bead3005ce26b3c10ebe76a5e90a8 Mon Sep 17 00:00:00 2001 From: Arthur Dodin Date: Sun, 12 Apr 2026 18:27:20 +0200 Subject: [PATCH 01/10] feat: conventions git --- CONVENTIONS.md | 106 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 CONVENTIONS.md diff --git a/CONVENTIONS.md b/CONVENTIONS.md new file mode 100644 index 0000000..1758476 --- /dev/null +++ b/CONVENTIONS.md @@ -0,0 +1,106 @@ +# Git Convention Guide + +## Commits (Conventional Commits) + +### Format + +``` +type(scope?): description +``` + +### Types principaux + +* `feat`: nouvelle fonctionnalité +* `fix`: correction de bug +* `refactor`: modification interne sans changement fonctionnel +* `style`: formatage / UI mineur (pas de logique) +* `docs`: documentation +* `test`: ajout/modification de tests +* `chore`: maintenance (deps, config…) +* `perf`: amélioration de performance +* `build`: build / dépendances +* `ci`: intégration continue + +### Exemples + +``` +feat(auth): add login with CAS +fix(navbar): fix mobile display issue +refactor(api): simplify error handling +style(ui): adjust spacing in header +``` + +### Bonnes pratiques + +* écrire à l’impératif → `add`, `fix`, `update` +* description courte (-50 caractères) et explicite +* un commit = une seule intention + +### Breaking changes + +L'ajour d'un point d'exclamation est possible pour signifier un breaking change. +``` +feat!: remove old API +``` + +--- + +## Branches + +### Format + +``` +type/short-description +``` + +### Types + +* `feat/` +* `fix/` +* `refactor/` +* `chore/` +* `docs/` +* `perf/` +* `test/` + +### Exemples + +``` +feat/login-page +fix/navbar-mobile +refactor/auth-hooks +``` + +### Bonnes pratiques + +* utiliser kebab-case +* rester court et clair +* une branche = un objectif + +--- + +## Choisir le bon type (exemple avec le frontend) + +| Situation | Type | +| ------------------------ | -------- | +| Nouvelle feature visible | feat | +| Bug | fix | +| Refacto interne | refactor | +| Amélioration performance | perf | +| Ajustement visuel mineur | style | + +--- + +## Exemple workflow + +``` +prod + └── feat/login-page + |── fix(auth): login-error + └── style(auth): color for 2026 edition +``` + +1. créer une branche depuis prod +2. faire des commits propres +3. ouvrir une pull request +4. merge après review From f3331a5029f47f15dd9989a7c4f8e45dd0c6c7fb Mon Sep 17 00:00:00 2001 From: Arthur Dodin Date: Sun, 12 Apr 2026 18:32:06 +0200 Subject: [PATCH 02/10] update: footer & navbar standardization --- frontend/src/components/footer.tsx | 19 ++ frontend/src/components/navbar.tsx | 234 ++++++++++-------- frontend/src/pages/challenge.tsx | 19 +- frontend/src/pages/food.tsx | 23 +- frontend/src/pages/games.tsx | 19 +- frontend/src/pages/home.tsx | 7 +- frontend/src/pages/news.tsx | 19 +- frontend/src/pages/parrainage.tsx | 27 +- frontend/src/pages/perm.tsx | 21 +- frontend/src/pages/plannings.tsx | 7 +- frontend/src/pages/profil.tsx | 19 +- frontend/src/pages/sdi.tsx | 24 +- frontend/src/pages/shotgun.tsx | 5 +- frontend/src/pages/wei.tsx | 19 +- .../src/services/requests/user.service.ts | 79 +++--- 15 files changed, 290 insertions(+), 251 deletions(-) create mode 100644 frontend/src/components/footer.tsx diff --git a/frontend/src/components/footer.tsx b/frontend/src/components/footer.tsx new file mode 100644 index 0000000..9ef9bfd --- /dev/null +++ b/frontend/src/components/footer.tsx @@ -0,0 +1,19 @@ +export const Footer = () => { + const currentYear = new Date().getFullYear(); + + return ( + + ); +} \ No newline at end of file diff --git a/frontend/src/components/navbar.tsx b/frontend/src/components/navbar.tsx index 0b4d9c3..2693945 100644 --- a/frontend/src/components/navbar.tsx +++ b/frontend/src/components/navbar.tsx @@ -12,6 +12,10 @@ interface NavItem { to: string; icon?: React.ComponentType>; rolesAllowed?: string[]; // ["Admin", "Respo CE", ...] + public?: boolean; + showWhen?: "always" | "auth" | "guest"; + kind?: "link" | "action"; + onClick?: () => void; children?: NavItem[]; // pour dropdown } @@ -19,85 +23,106 @@ export const Navbar = () => { const { pathname } = useLocation(); const token = getToken(); const [menuOpen, setMenuOpen] = useState(false); + const isAuthenticated = Boolean(token); + const { userPermission, userRoles = [] } = token ? decodeToken(token) : { userPermission: undefined, userRoles: [] }; + const roles = [userPermission, ...userRoles.map(r => r.roleName)].filter(Boolean) as string[]; + + const handleLogout = () => { + localStorage.removeItem("authToken"); + window.location.href = "/"; + }; useEffect(() => { setMenuOpen(false); }, [pathname]); - if (!token) return null; - const { userPermission, userRoles = [] } = decodeToken(token); - const roles = [userPermission, ...userRoles.map(r => r.roleName)]; - const navItems: NavItem[] = [ - { label: "Home", to: "/Home", icon: HomeIcon }, - { label: "Plannings", to: "/Plannings" }, - { label: "Parrainage", to: "/Parrainage" }, - { label: "Challenges", to: "/Challenges" }, - { label: "Mes Actus", to: "/News" }, - { - label: "Permanences", - to: "#", - children: [ - { label: "Listes des permanences", to: "/PermanencesList", rolesAllowed: ["Admin", "Student"] }, - { label: "Mes permanences", to: "/MyPermanences", rolesAllowed: ["Admin", "Student"] }, - { label: "Faire l'appel", to: "/PermanencesAppeal", rolesAllowed: ["Admin", "Student"] }, - ], - }, - { - label: "Events", - to: "#", - children: [ - { label: "Shotgun", to: "/Shotgun", rolesAllowed: ["Admin", "Student"] }, - { label: "WEI", to: "/Wei" }, - { label: "SDI", to: "/SDI" }, - { label: "Repas", to: "/Food" }, - { label: "Defis Commissions", to: "/Games", rolesAllowed: ["Admin", "Student"] }, - ], - }, - { label: "Mon compte", to: "/Profil", icon: UsersIcon }, - { - label: "Admin", - to: "#", - icon: CogIcon, - children: [ - { label: "Users", to: "/admin/users", rolesAllowed: ["Admin"] }, - { label: "Roles", to: "/admin/roles", rolesAllowed: ["Admin"] }, - { label: "Teams", to: "/admin/teams", rolesAllowed: ["Admin", "Respo CE"] }, - { label: "Factions", to: "/admin/factions", rolesAllowed: ["Admin", "Respo CE"] }, - { label: "Events", to: "/admin/events", rolesAllowed: ["Admin"] }, - { label: "Permanences", to: "/admin/permanences", rolesAllowed: ["Admin", "Respo CE"] }, - { label: "Challenge", to: "/admin/challenge", rolesAllowed: ["Admin", "Arbitre"] }, - { label: "Export / Import", to: "/admin/export-import", rolesAllowed: ["Admin"] }, - { label: "Email", to: "/admin/email", rolesAllowed: ["Admin"] }, - { label: "News", to: "/admin/news", rolesAllowed: ["Admin", "Communication"] }, - { label: "Tentes", to: "/admin/tent", rolesAllowed: ["Admin"] }, - { label: "Bus", to: "/admin/bus", rolesAllowed: ["Admin"] }, - { label: "Games", to: "/admin/games", rolesAllowed: ["Admin"] }, - ], - }, - -]; + { label: "Home", to: "/Home", icon: HomeIcon }, + { label: "Plannings", to: "/Plannings" }, + { label: "Parrainage", to: "/Parrainage" }, + { label: "Challenges", to: "/Challenges" }, + { label: "Mes Actus", to: "/News" }, + { + label: "Permanences", + to: "#", + children: [ + { label: "Listes des permanences", to: "/PermanencesList", rolesAllowed: ["Admin", "Student"] }, + { label: "Mes permanences", to: "/MyPermanences", rolesAllowed: ["Admin", "Student"] }, + { label: "Faire l'appel", to: "/PermanencesAppeal", rolesAllowed: ["Admin", "Student"] }, + ], + }, + { + label: "Events", + to: "#", + children: [ + { label: "Shotgun", to: "/Shotgun", rolesAllowed: ["Admin", "Student"] }, + { label: "WEI", to: "/Wei" }, + { label: "SDI", to: "/SDI" }, + { label: "Repas", to: "/Food" }, + { label: "Defis Commissions", to: "/Games", rolesAllowed: ["Admin", "Student"] }, + ], + }, + { label: "Mon compte", to: "/Profil", icon: UsersIcon }, + { + label: "Admin", + to: "#", + icon: CogIcon, + children: [ + { label: "Users", to: "/admin/users", rolesAllowed: ["Admin"] }, + { label: "Roles", to: "/admin/roles", rolesAllowed: ["Admin"] }, + { label: "Teams", to: "/admin/teams", rolesAllowed: ["Admin", "Respo CE"] }, + { label: "Factions", to: "/admin/factions", rolesAllowed: ["Admin", "Respo CE"] }, + { label: "Events", to: "/admin/events", rolesAllowed: ["Admin"] }, + { label: "Permanences", to: "/admin/permanences", rolesAllowed: ["Admin", "Respo CE"] }, + { label: "Challenge", to: "/admin/challenge", rolesAllowed: ["Admin", "Arbitre"] }, + { label: "Export / Import", to: "/admin/export-import", rolesAllowed: ["Admin"] }, + { label: "Email", to: "/admin/email", rolesAllowed: ["Admin"] }, + { label: "News", to: "/admin/news", rolesAllowed: ["Admin", "Communication"] }, + { label: "Tentes", to: "/admin/tent", rolesAllowed: ["Admin"] }, + { label: "Bus", to: "/admin/bus", rolesAllowed: ["Admin"] }, + { label: "Games", to: "/admin/games", rolesAllowed: ["Admin"] }, + ], + }, + { + label: "Déconnexion", + to: "/", + kind: "action", + showWhen: "auth", + onClick: handleLogout, + }, + { + label: "Se connecter", + to: "/", + public: true, + showWhen: "guest", + }, + ]; - // helper d’autorisation - const isAllowed = (item: NavItem): boolean => { - // Si l'item a une restriction directe - if (item.rolesAllowed) { - return item.rolesAllowed.some(r => roles.includes(r)); - } - // Si l'item a des enfants, on vérifie au moins un enfant - if (item.children && item.children.length > 0) { - return item.children.some(child => isAllowed(child)); - } + const canShowItem = (item: NavItem): boolean => { + if (item.showWhen === "auth") return isAuthenticated; + if (item.showWhen === "guest") return !isAuthenticated; - // Sinon accessible par défaut - return true; -}; + if (!isAuthenticated) { + if (item.public) return true; - const handleLogout = () => { - localStorage.removeItem("authToken"); - window.location.href = "/"; + if (item.children && item.children.length > 0) { + return item.children.some(child => canShowItem(child)); + } + + return false; + } + + if (item.rolesAllowed) { + return item.rolesAllowed.some(r => roles.includes(r)); + } + + if (item.children && item.children.length > 0) { + return item.children.some(child => canShowItem(child)); + } + + return true; }; return ( @@ -124,24 +149,18 @@ export const Navbar = () => { {/* Menu desktop */}
    {navItems.map(item => - isAllowed(item) ? ( + canShowItem(item) ? (
  • {item.children ? ( - + + ) : item.kind === "action" ? ( + ) : ( )}
  • ) : null )} -
  • - -
@@ -155,24 +174,20 @@ export const Navbar = () => { className="lg:hidden bg-blue-700 overflow-hidden" > {navItems.map(item => - isAllowed(item) ? ( + canShowItem(item) ? ( {!item.children ? ( - + item.kind === "action" ? ( + + ) : ( + + ) ) : ( - + )} ) : null )} -
  • - -
  • )} @@ -180,6 +195,29 @@ export const Navbar = () => { ); }; +const NavActionItem = ({ + item, + mobile = false, +}: { + item: NavItem; + mobile?: boolean; +}) => { + const base = mobile ? "block py-2 px-4 text-left w-full" : "inline-flex items-center py-2"; + + return ( + + ); +}; + // Composant MenuItem const MenuItem = ({ item, @@ -207,26 +245,19 @@ const MenuItem = ({ ); }; -// Composant Dropdown (desktop & mobile) // Composant Dropdown (desktop & mobile) const Dropdown = ({ item, mobile = false, + canShowItem, }: { item: NavItem; mobile?: boolean; + canShowItem: (item: NavItem) => boolean; }) => { const [open, setOpen] = useState(false); const trigger = mobile ? "p-4" : "py-2 cursor-pointer"; - // helper pour roles - const token = getToken(); - const { userPermission, userRoles = [] } = token ? decodeToken(token) : {}; - const roles = [userPermission, ...(userRoles?.map((r: any) => r.roleName) || [])]; - - const isAllowed = (child: NavItem) => - !child.rolesAllowed || child.rolesAllowed.some(r => roles.includes(r)); - return (
    {item.children! - .filter(child => isAllowed(child)) // ✅ filtre selon les rôles + .filter(child => canShowItem(child)) .map(child => (
  • ( - -
    - -
    -
    - + +
    + +
    +
    + +
    +
    -
    -

    © 2025 Semaine d'Intégration UTT

    -
    -
    ); diff --git a/frontend/src/pages/food.tsx b/frontend/src/pages/food.tsx index 5fc9180..e5aaae3 100644 --- a/frontend/src/pages/food.tsx +++ b/frontend/src/pages/food.tsx @@ -1,20 +1,19 @@ import { Navbar } from "../components/navbar"; -import { FoodSection } from "../components/WEI_SDI_Food/foodSection"; +import { FoodSection } from "../components/WEI_SDI_Food/foodSection" +import { Footer } from "../components/footer"; export const FoodPage = () => { - - return( + + return (
    - -
    -
    - -
    + +
    +
    +
    -
    -

    © 2025 Semaine d'Intégration UTT

    -
    +
    +
    - ); + ); } \ No newline at end of file diff --git a/frontend/src/pages/games.tsx b/frontend/src/pages/games.tsx index 36e46b7..08aa247 100644 --- a/frontend/src/pages/games.tsx +++ b/frontend/src/pages/games.tsx @@ -1,18 +1,17 @@ import { RoleLeaderboard } from "../components/Games/roleLeaderboard"; import { Navbar } from "../components/navbar"; +import { Footer } from "../components/footer"; export const GamesPage = () => ( - -
    - -
    -
    - + +
    + +
    +
    + +
    +
    -
    -

    © 2025 Semaine d'Intégration UTT

    -
    -
    ); diff --git a/frontend/src/pages/home.tsx b/frontend/src/pages/home.tsx index 9508ce1..d0d9b19 100644 --- a/frontend/src/pages/home.tsx +++ b/frontend/src/pages/home.tsx @@ -1,7 +1,8 @@ // src/pages/index.tsx import { Navbar } from "../components/navbar"; import { Infos } from "../components/home/infosSection"; -import {SocialLinks} from "../components/home/socialSection"; +import { SocialLinks } from "../components/home/socialSection"; +import { Footer } from "../components/footer"; export const HomePage = () => { @@ -11,9 +12,7 @@ export const HomePage = () => { -
    -

    © 2025 Semaine d'Intégration UTT

    -
    +
    ); } diff --git a/frontend/src/pages/news.tsx b/frontend/src/pages/news.tsx index 73bee95..32b03bf 100644 --- a/frontend/src/pages/news.tsx +++ b/frontend/src/pages/news.tsx @@ -1,17 +1,16 @@ import { Navbar } from "../components/navbar"; import { MyNews } from "../components/news/newsSection"; +import { Footer } from "../components/footer"; export const NewsPage = () => ( - -
    - -
    -
    - + +
    + +
    +
    + +
    +
    -
    -

    © 2025 Semaine d'Intégration UTT

    -
    -
    ); diff --git a/frontend/src/pages/parrainage.tsx b/frontend/src/pages/parrainage.tsx index 38191aa..82bf5d1 100644 --- a/frontend/src/pages/parrainage.tsx +++ b/frontend/src/pages/parrainage.tsx @@ -2,6 +2,7 @@ import { Navbar } from "../components/navbar"; import { useNavigate } from "react-router-dom"; import { ParrainageNewStudent, ParrainageStudent } from "../components/Parrainnage/parrainageForm"; import { getPermission } from "../services/requests/user.service"; +import { Footer } from "../components/footer"; export const ParrainagePage = () => { @@ -12,23 +13,21 @@ export const ParrainagePage = () => { if (!permission) { navigate("/"); return null; -} - return( -
    - -
    -
    + } + return ( +
    + +
    +
    - {(permission === "Nouveau" || permission === "Admin") && ( - )} + {(permission === "Nouveau" || permission === "Admin") && ( + )} - {(permission === "Student" || permission === "Admin") && ( - )} + {(permission === "Student" || permission === "Admin") && ( + )} +
    +
    -
    -

    © 2025 Semaine d'Intégration UTT

    -
    -
    ); } \ No newline at end of file diff --git a/frontend/src/pages/perm.tsx b/frontend/src/pages/perm.tsx index 9e35979..991cf3f 100644 --- a/frontend/src/pages/perm.tsx +++ b/frontend/src/pages/perm.tsx @@ -16,6 +16,7 @@ import { DecodedToken } from "../interfaces/token.interfaces"; import { Navbar } from "../components/navbar"; import { RespoPresenceManagement } from "../components/permanence/appealPerm"; import { MyPermanencesList } from "../components/permanence/permUser"; +import { Footer } from "../components/footer"; export const AvailablePermanencesPage: React.FC = () => { const [permanences, setPermanences] = useState([]); @@ -59,16 +60,16 @@ export const AvailablePermanencesPage: React.FC = () => { return (
    -
    +
    - -
    + />
    +
    ); }; @@ -133,14 +134,14 @@ export const MyPermanencesPage: React.FC = () => { return (
    -
    +
    - -
    + />
    +
    ); }; @@ -192,9 +193,7 @@ export const RespoCallPage = () => { )}
    -
    -

    © 2025 Semaine d'Intégration UTT

    -
    +
    ); }; diff --git a/frontend/src/pages/plannings.tsx b/frontend/src/pages/plannings.tsx index ca8f0bb..2a5c2d5 100644 --- a/frontend/src/pages/plannings.tsx +++ b/frontend/src/pages/plannings.tsx @@ -1,5 +1,6 @@ import { Navbar } from "../components/navbar"; import { PlanningSection } from "../components/Plannings/planningSection"; +import { Footer } from "../components/footer"; export const PlanningsPage = () => ( @@ -7,11 +8,9 @@ export const PlanningsPage = () => (
    - +
    -
    -

    © 2025 Semaine d'Intégration UTT

    -
    +
    ); \ No newline at end of file diff --git a/frontend/src/pages/profil.tsx b/frontend/src/pages/profil.tsx index 43afa08..da84fca 100644 --- a/frontend/src/pages/profil.tsx +++ b/frontend/src/pages/profil.tsx @@ -3,26 +3,25 @@ import { Navbar } from "../components/navbar"; import { UserPreferences } from "../components/profil/roleForm"; import { getPermission } from "../services/requests/user.service"; import { ProfilForm } from "../components/profil/profilForm"; +import { Footer } from "../components/footer"; export const ProfilPage = () => { - const navigate = useNavigate(); - const permission = getPermission(); + const navigate = useNavigate(); + const permission = getPermission(); - if (!permission) { - navigate("/"); - return null; - } + if (!permission) { + navigate("/"); + return null; + } return (
    - < ProfilForm/> + {(permission === "Student" || permission === "Admin") && ()} -
    -

    © 2025 Semaine d'Intégration UTT

    -
    +
    ); } \ No newline at end of file diff --git a/frontend/src/pages/sdi.tsx b/frontend/src/pages/sdi.tsx index 016af1b..9e80517 100644 --- a/frontend/src/pages/sdi.tsx +++ b/frontend/src/pages/sdi.tsx @@ -1,20 +1,18 @@ import { Navbar } from "../components/navbar"; -import { SdiSection } from "../components/WEI_SDI_Food/sdiSection"; - +import { SdiSection } from "../components/WEI_SDI_Food/sdiSection"; +import { Footer } from "../components/footer"; export const SdiPage = () => { - - return( + + return (
    - -
    -
    - -
    + +
    +
    +
    -
    -

    © 2025 Semaine d'Intégration UTT

    -
    +
    +
    - ); + ); } \ No newline at end of file diff --git a/frontend/src/pages/shotgun.tsx b/frontend/src/pages/shotgun.tsx index a8ef5e6..0a8bb3b 100644 --- a/frontend/src/pages/shotgun.tsx +++ b/frontend/src/pages/shotgun.tsx @@ -2,6 +2,7 @@ import { Navbar } from "../components/navbar"; import { PreregisterCESection } from "../components/shotgun/preregisterCESection"; import { PreregisterTeamSection } from "../components/shotgun/preregisterTeamSection"; import { Shotgun } from "../components/shotgun/shotgunSection"; +import { Footer } from "../components/footer"; export const ShotgunPage = () => ( @@ -15,8 +16,6 @@ export const ShotgunPage = () => (
    -
    -

    © 2025 Semaine d'Intégration UTT

    -
    +
    ); diff --git a/frontend/src/pages/wei.tsx b/frontend/src/pages/wei.tsx index 88377e5..e7fdf5e 100644 --- a/frontend/src/pages/wei.tsx +++ b/frontend/src/pages/wei.tsx @@ -3,17 +3,18 @@ import { WeiSection } from "../components/WEI_SDI_Food/weiSection"; import { TentPublic } from "../components/tent/tentSection"; import { useNavigate } from "react-router-dom"; import { getPermission } from "../services/requests/user.service"; +import { Footer } from "../components/footer"; export const WeiPage = () => { - const navigate = useNavigate(); - const permission = getPermission(); + const navigate = useNavigate(); + const permission = getPermission(); + + if (!permission) { + navigate("/"); + return null; + } - if (!permission) { - navigate("/"); - return null; - } - return (
    @@ -23,9 +24,7 @@ export const WeiPage = () => { {(permission === "Nouveau" || permission === "Admin") && }
    -
    -

    © 2025 Semaine d'Intégration UTT

    -
    +
    ); }; \ No newline at end of file diff --git a/frontend/src/services/requests/user.service.ts b/frontend/src/services/requests/user.service.ts index 9f5d316..f7e5cf1 100644 --- a/frontend/src/services/requests/user.service.ts +++ b/frontend/src/services/requests/user.service.ts @@ -3,90 +3,93 @@ import { User } from '../../interfaces/user.interface'; import api from '../api'; export const getPermission = (): string | null => { - const token = localStorage.getItem('authToken'); - if (token) { - try { - // Suppose que le token est un JWT et qu'il contient un payload avec un rôle - const decodedToken = JSON.parse(atob(token.split('.')[1])); // Décodage du token JWT - return decodedToken?.userPermission || null; // Retourne le rôle ou null - } catch (error) { - console.error('Erreur lors du décodage du token:', error); - return null; - } + const token = localStorage.getItem('authToken'); + if (token) { + try { + // Suppose que le token est un JWT et qu'il contient un payload avec un rôle + const decodedToken = JSON.parse(atob(token.split('.')[1])); // Décodage du token JWT + return decodedToken?.userPermission || null; // Retourne le rôle ou null + } catch (error) { + console.error('Erreur lors du décodage du token:', error); + return null; } - return null; - }; + } + return null; +}; export const isAdmin = (): boolean => { return getPermission() === 'Admin'; }; +export const isConnected = (): boolean => { + return getPermission() !== null; +}; export const getUsers = async () => { - const response = await api.get("/user/user/getusers"); - const users = response.data.data; - - return users; + const response = await api.get("/user/user/getusers"); + const users = response.data.data; + + return users; } export const getUsersAdmin = async () => { - const response = await api.get("/user/admin/getusers"); - const users = response.data.data; - - return users; - + const response = await api.get("/user/admin/getusers"); + const users = response.data.data; + + return users; + } export const getUsersByPermission = async () => { - const response = await api.get("/user/admin/getusersbypermission"); - const users = response.data.data; - - return users; + const response = await api.get("/user/admin/getusersbypermission"); + const users = response.data.data; + + return users; } export const getCurrentUser = async () => { - const res = await api.get("/user/user/me"); + const res = await api.get("/user/user/me"); return res.data.data; }; export const updateCurrentUser = async (data: Partial) => { - const response = await api.patch("/user/user/me", data); - return response.data + const response = await api.patch("/user/user/me", data); + return response.data }; export const updateUserByAdmin = async (id: number, data: Partial) => { - - const response = await api.patch(`/user/admin/user/${id}`, data); - return response.data + + const response = await api.patch(`/user/admin/user/${id}`, data); + return response.data }; export const deleteUserByAdmin = async (id: number) => { - const response = await api.delete(`/user/admin/user/${id}`); - return response.data + const response = await api.delete(`/user/admin/user/${id}`); + return response.data }; export const syncnewStudent = async (date: string) => { - const response = await api.post(`/user/admin/syncnewstudent/`,{date}); - return response.data + const response = await api.post(`/user/admin/syncnewstudent/`, { date }); + return response.data }; -export const syncDiscordUser = async(code : string) =>{ +export const syncDiscordUser = async (code: string) => { - const response = await api.post(`/discord/user/callback/`,{code}); - return response.data + const response = await api.post(`/discord/user/callback/`, { code }); + return response.data } From 5e94362b778fa40206f540fa1d46953439378f2c Mon Sep 17 00:00:00 2001 From: Arthur Dodin Date: Sun, 12 Apr 2026 18:34:54 +0200 Subject: [PATCH 03/10] update: "close" message standardization --- .../components/Parrainnage/parrainageForm.tsx | 4 +- .../components/Plannings/planningSection.tsx | 2 +- .../components/WEI_SDI_Food/sdiSection.tsx | 4 +- .../components/challenge/challengeList.tsx | 42 +++++++++---------- .../shotgun/preregisterCESection.tsx | 2 +- .../shotgun/preregisterTeamSection.tsx | 2 +- .../src/components/shotgun/shotgunSection.tsx | 11 +++-- 7 files changed, 33 insertions(+), 34 deletions(-) diff --git a/frontend/src/components/Parrainnage/parrainageForm.tsx b/frontend/src/components/Parrainnage/parrainageForm.tsx index 78eecf5..39e1837 100644 --- a/frontend/src/components/Parrainnage/parrainageForm.tsx +++ b/frontend/src/components/Parrainnage/parrainageForm.tsx @@ -20,7 +20,7 @@ export const ParrainageNewStudent = () => {
  • */}

    - 🚫 Ce formulaire n’est pas encore disponible. + 🚫 Ce formulaire n'est pas encore disponible.

    @@ -49,7 +49,7 @@ export const ParrainageStudent = () => { */}

    - 🚫 Ce formulaire n’est pas encore disponible. + 🚫 Ce formulaire n'est pas encore disponible.

    diff --git a/frontend/src/components/Plannings/planningSection.tsx b/frontend/src/components/Plannings/planningSection.tsx index 87dab9c..9c9f725 100644 --- a/frontend/src/components/Plannings/planningSection.tsx +++ b/frontend/src/components/Plannings/planningSection.tsx @@ -99,7 +99,7 @@ export const PlanningSection = () => { ) : (

    - 🚫 Ce planning n’est pas encore disponible. + 🚫 Ce planning n'est pas encore disponible.

    )} diff --git a/frontend/src/components/WEI_SDI_Food/sdiSection.tsx b/frontend/src/components/WEI_SDI_Food/sdiSection.tsx index 977e4b9..6d1d0df 100644 --- a/frontend/src/components/WEI_SDI_Food/sdiSection.tsx +++ b/frontend/src/components/WEI_SDI_Food/sdiSection.tsx @@ -30,14 +30,14 @@ export const SdiSection = () => { 🎉 Participe à la Soirée d'Intégration (SDI) !

    - Un événement incroyable t’attend… Inscris-toi dès maintenant pour ne rien rater de cette Soirée d’Intégration 2025 ! + Un événement incroyable t'attend… Inscris-toi dès maintenant pour ne rien rater de cette Soirée d'Intégration 2025 !

    {!isSDIOpen ? (

    - 🚫 La billetterie de la Soirée d'intégration(SDI) n’est pas encore disponible. + 🚫 La billetterie de la Soirée d'intégration(SDI) n'est pas encore disponible.

    Reste connecté, elle ouvrira bientôt ! diff --git a/frontend/src/components/challenge/challengeList.tsx b/frontend/src/components/challenge/challengeList.tsx index 9c5a35d..1f99736 100644 --- a/frontend/src/components/challenge/challengeList.tsx +++ b/frontend/src/components/challenge/challengeList.tsx @@ -16,25 +16,25 @@ export const UserChallengeList = () => { const [isChallOpen, setIsChallOpen] = useState(false); const [loading, setLoading] = useState(true); - useEffect(() => { - const init = async () => { - try { - await fetchInitialData(); - const status = await checkChallengeStatus(); - setIsChallOpen(status); - } catch (error) { - console.error("Erreur lors de la récupération des données :", error); - await Swal.fire({ - icon: "error", - title: "Oups...", - text: "Une erreur est survenue lors de la récupération des données.", - }); - } finally { - setLoading(false); - } - }; - init(); -}, []); + useEffect(() => { + const init = async () => { + try { + await fetchInitialData(); + const status = await checkChallengeStatus(); + setIsChallOpen(status); + } catch (error) { + console.error("Erreur lors de la récupération des données :", error); + await Swal.fire({ + icon: "error", + title: "Oups...", + text: "Une erreur est survenue lors de la récupération des données.", + }); + } finally { + setLoading(false); + } + }; + init(); + }, []); const fetchInitialData = async () => { await Promise.all([fetchChallenges(), fetchFactions()]); @@ -44,7 +44,7 @@ export const UserChallengeList = () => { const fetchChallenges = async () => { try { const challenges = await getAllChallenges(); - const challengesFiltered = challenges.filter((c : Challenge) => c.category != "Free") + const challengesFiltered = challenges.filter((c: Challenge) => c.category != "Free") setAvailableChallenges(challengesFiltered); } catch (err) { console.error("Erreur lors du chargement des challenges", err); @@ -122,7 +122,7 @@ export const UserChallengeList = () => { {loading ? (

    Chargement en cours...

    ) : !isChallOpen ? ( -

    +

    🚫 Les challenges ne sont pas encore ouverts.

    ) : availableChallenges.length === 0 ? ( diff --git a/frontend/src/components/shotgun/preregisterCESection.tsx b/frontend/src/components/shotgun/preregisterCESection.tsx index 9f29082..edc6de7 100644 --- a/frontend/src/components/shotgun/preregisterCESection.tsx +++ b/frontend/src/components/shotgun/preregisterCESection.tsx @@ -40,7 +40,7 @@ export const PreregisterCESection = () => { ) : (

    - La pré-inscription est actuellement fermée. + 🚫 La pré-inscription est actuellement fermée.

    )}
    diff --git a/frontend/src/components/shotgun/preregisterTeamSection.tsx b/frontend/src/components/shotgun/preregisterTeamSection.tsx index 84c057d..e4b665d 100644 --- a/frontend/src/components/shotgun/preregisterTeamSection.tsx +++ b/frontend/src/components/shotgun/preregisterTeamSection.tsx @@ -152,7 +152,7 @@ export const PreregisterTeamSection = () => { ) : (

    - La pré-inscription est actuellement fermée. + 🚫 La pré-inscription est actuellement fermée.

    )} diff --git a/frontend/src/components/shotgun/shotgunSection.tsx b/frontend/src/components/shotgun/shotgunSection.tsx index 0da54c8..5d84bcf 100644 --- a/frontend/src/components/shotgun/shotgunSection.tsx +++ b/frontend/src/components/shotgun/shotgunSection.tsx @@ -75,11 +75,10 @@ export const Shotgun = () => { {message && (

    {message}

    @@ -87,7 +86,7 @@ export const Shotgun = () => { ) : (

    - Le shotgun n'est pas encore ouvert. + 🚫 Le shotgun n'est pas encore ouvert.

    )} From bb3ed5517b8edc950dafb6159d302a7e608a266b Mon Sep 17 00:00:00 2001 From: Arthur Dodin Date: Sun, 12 Apr 2026 18:35:25 +0200 Subject: [PATCH 04/10] feat: legals & privacy pages --- frontend/src/App.tsx | 4 + .../src/components/legals/legalsSection.tsx | 98 +++++++++++++ frontend/src/pages/legals.tsx | 15 ++ frontend/src/pages/privacy.tsx | 15 ++ frontend/src/privacy/privacySection.tsx | 130 ++++++++++++++++++ 5 files changed, 262 insertions(+) create mode 100644 frontend/src/components/legals/legalsSection.tsx create mode 100644 frontend/src/pages/legals.tsx create mode 100644 frontend/src/pages/privacy.tsx create mode 100644 frontend/src/privacy/privacySection.tsx diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 07d0cab..d462ac1 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -38,6 +38,8 @@ import { GamesPage } from './pages/games'; import { FoodPage } from './pages/food'; import { PlanningsPage } from './pages/plannings'; import { Roadbook } from './pages/roadbook'; +import { PrivacyPage } from './pages/privacy'; +import { LegalsPage } from './pages/legals'; const App: React.FC = () => { @@ -64,6 +66,8 @@ const App: React.FC = () => { } /> } /> } /> + } /> + } /> {/* Utilisateurs connectés */} } /> diff --git a/frontend/src/components/legals/legalsSection.tsx b/frontend/src/components/legals/legalsSection.tsx new file mode 100644 index 0000000..c8c03df --- /dev/null +++ b/frontend/src/components/legals/legalsSection.tsx @@ -0,0 +1,98 @@ +import { Link } from "react-router-dom"; + +export const LegalsSection = () => { + return ( +
    +
    +
    +

    Mentions Légales

    +

    Informations légales et conformité RGPD

    +
    + +
    +
    +

    Editeur du Site

    +
    +
    +

    BDE UTT

    +

    Association loi 1901

    +

    N° RNA : W103000735

    +

    N° SIRET : 44838667200019

    +

    N° SIREN : 448386672

    +
    +
    +

    + 12 rue Marie Curie +
    + 10000 TROYES +

    +

    + 03 25 71 76 00 +

    +

    + bde@utt.fr +

    +
    +
    +

    + Le projet Intégration UTT est porté par l'association BDE UTT. +

    +
    + +
    +

    Propriétaire et Hébergeur

    +
    +
    +

    UTT Net Group

    +

    Association loi 1901

    +

    N° RNA : W103000699

    +

    N° RCS : 500164249

    +
    +
    +

    + 12 rue Marie Curie +
    + 10000 TROYES +

    +

    + 03 25 71 85 50 +

    +

    + ung@utt.fr +

    +
    +
    +
    + +
    +

    Protection des Données Personnelles

    +

    + Le site collecte et traite des données personnelles conformément a la loi Informatique et Libertés du 6 janvier 1978 modifiée et au RGPD EU-2016/679. +

    +

    + Pour plus d'informations, consultez notre{" "} + + Politique de confidentialite + + . +

    +
    + +
    +

    Droits d'Auteur

    +

    + L'ensemble du contenu de ce site est protegé par le droit d'auteur. Sauf mention contraire, les contenus relatifs au projet Integration UTT sont diffusés sous la responsabilité du BDE UTT. Toute reproduction, distribution ou modification est interdite sans autorisation écrite préalable. +

    +
    + +
    +

    Credits

    +

    + Ce site est developpé pour le projet Integration UTT du BDE UTT, avec le support technique de l'association UTT Net Group. +

    +
    +
    +
    +
    + ); +}; \ No newline at end of file diff --git a/frontend/src/pages/legals.tsx b/frontend/src/pages/legals.tsx new file mode 100644 index 0000000..697bb32 --- /dev/null +++ b/frontend/src/pages/legals.tsx @@ -0,0 +1,15 @@ +// src/pages/legals.tsx +import { Navbar } from "../components/navbar"; +import { Footer } from "../components/footer"; +import { LegalsSection } from "../components/legals/legalsSection"; + +export const LegalsPage = () => { + + return ( +
    + + +
    +
    + ); +} diff --git a/frontend/src/pages/privacy.tsx b/frontend/src/pages/privacy.tsx new file mode 100644 index 0000000..21789a9 --- /dev/null +++ b/frontend/src/pages/privacy.tsx @@ -0,0 +1,15 @@ +// src/pages/privacy.tsx +import { Navbar } from "../components/navbar"; +import { Footer } from "../components/footer"; +import { PrivacySection } from "../privacy/privacySection"; + +export const PrivacyPage = () => { + + return ( +
    + + +
    +
    + ); +} diff --git a/frontend/src/privacy/privacySection.tsx b/frontend/src/privacy/privacySection.tsx new file mode 100644 index 0000000..dd21fdd --- /dev/null +++ b/frontend/src/privacy/privacySection.tsx @@ -0,0 +1,130 @@ +import { Link } from "react-router-dom"; + +export const PrivacySection = () => { + return ( +
    +
    +
    +

    Politique de Confidentialité

    +

    Vos données personnelles et votre vie privée

    +
    + +
    +
    +

    Vie Privée et Données à Caractère Personnel

    +

    + A l'Université de Technologie de Troyes et au sein des associations BDE UTT et UTT Net Group, nous respectons votre vie privée. Les données collectées et utilisées par la plateforme Integration UTT sont nécessaires pour la gestion des membres, des inscriptions aux évènements et des services propoés pendant l'intégration. +

    +
    + +
    +

    Données Personnelles Collectées

    +

    + Les données suivantes sont effectivement stockées et traitées par la plateforme Intégration UTT. +

    +
      +
    • Identité: nom, prenom
    • +
    • Coordonnées: adresse email UTT, contact saisi dans le profil (optionnel)
    • +
    • Données de compte: mot de passe de connexion chiffré, date de création
    • +
    • Informations profil: branche/niveau suivie à l'UTT, majorité
    • +
    • Liaison externe optionnelle: identifiant Discord en cas de connexion Discord (optionnel)
    • +
    • Organisation intégration: éuipe, faction, rôles et préférences de rôles/commissions (optionnel, pour les organisateurs uniquement)
    • +
    • Participation aux services: inscriptions permanences, attribution bus, binôme tente, validations de challenges
    • +
    +
    + +
    +

    Comment Ces Informations Sont-Elles Utilisées ?

    +

    + Les données à caractère personnel sont des informations qui permettent sous quelque forme que ce soit, directement ou indirectement, l'identification des personnes physiques auxquelles elles s'appliquent. +

    +

    Ces informations sont utilisées pour :

    +
      +
    • Permettre l'authentification (mot de passe ou CAS), la gestion de session et la sécurisation des accès
    • +
    • Gérer les comptes utilisateurs, les droits d'accès et les rôles d'organisation
    • +
    • Permettre les fonctionnalités du site: profil, affectations d'equipes/factions, permanences, bus, tentes, challenges et évènements
    • +
    • Afficher certaines informations aux organisateurs pour la coordination opérationnelle (ex: contact, équipe, rôle)
    • +
    • Lier un compte Discord lorsque l'utilisateur active cette option
    • +
    • Executer des opérations d'administration (support, import/export, modération et gestion interne)
    • +
    +
    + +
    +

    Durée de Conservation des Données

    +

    + Les données personnelles sont conservées pendant un (1) an maximum, puis supprimées. +
    + Des données anonymisées peuvent être conservées à des fins statistiques et d'amélioration du service, mais ne permettent pas l'identification des individus. +

    +

    Les cookies de session sont détruits à la déconnexion ou à leur expiration.

    +
    + +
    +

    Vos Droits sur Vos Données

    +

    Conformément à la réglementation sur les données à caractère personnel, vous disposez des droits suivants :

    +
      +
    • Droit d'accès : obtenir une copie de vos données
    • +
    • Droit de rectification : corriger des données inexactes vous concernant
    • +
    • Droit à l'effacement : demander la suppression de vos données
    • +
    • Droit d'opposition : vous opposer au traitement de vos données
    • +
    • Droit à la portabilité : récupérer vos données dans un format structuré
    • +
    +
    + +
    +

    Comment Exercer Vos Droits ?

    +

    Si vous avez des questions ou que vous souhaitez exercer vos droits (accès, rectification, suppression), vous pouvez :

    +
      +
    • + Envoyer un courriel à {" "} + + integration@utt.fr + +
    • +
    • + Contacter le délégué à la protection des données :{" "} + + ung+dpo@utt.fr + +
    • +
    • + Par courrier : UTT Net Group, 12 rue Marie Curie, CS 42060, 10004 TROYES CEDEX +
    • +
    +

    + Si vous estimez, après nous avoir contacté, que vos droits ne sont pas respectés, vous pouvez adresser une réclamation en ligne à la CNIL ou par voie postale. +

    +
    + +
    +

    Responsable du Traitement

    +

    + Le responsable du traitement des données pour la plateforme Intégration UTT est Arthur Dodin, Président de l'association UTT Net Group. +

    +

    + L'équipe technique et les administrateurs du site pourront accéder aux données dans le cadre de la gestion de la plateforme et du support technique. +

    +
    + +
    +

    Sécurité des Données

    +

    + Nous mettons en œuvre toutes les mesures techniques et organisationnelles appropriées afin de garantir un niveau de sécurité adapté au risque, conformément aux exigences du RGPD. +

    +

    Ces données ne seront en aucun cas échangées, distribuées ou vendues à un tiers.

    +
    + +
    +

    Cookies

    +

    + Nous utilisons des cookies afin d'obtenir des statistiques sur notre site web. Ces informations ne seront en aucun cas vendues, échangées ou données. Ces cookies sont anonymisés. +

    +

    + Afin d'assurer le fonctionnement du service à l'utilisateur authentifié, des cookies de session sont inscrits sur le navigateur lors de l'authentification sur le site. Ceux-ci ont pour seule fonction d'assurer la persistance de la session authentifiée de l'utilisateur. Ils sont détruits lors de la déconnexion ou à leur expiration. +

    +
    +
    +
    +
    + ); +}; \ No newline at end of file From fd503934215a8afdf5fb0d59bad9e3a682676faa Mon Sep 17 00:00:00 2001 From: Arthur Dodin Date: Sun, 12 Apr 2026 18:40:37 +0200 Subject: [PATCH 05/10] update(home): vertical centered image --- frontend/src/components/home/infosSection.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/home/infosSection.tsx b/frontend/src/components/home/infosSection.tsx index 1688e5c..1d34cdc 100644 --- a/frontend/src/components/home/infosSection.tsx +++ b/frontend/src/components/home/infosSection.tsx @@ -8,23 +8,22 @@ export const Infos = () => { return (
    {/* Hero Carousel */} -
    +
    {["Home1", "Home2", "Home3", "Home4"].map((img, i) => ( - + {`Photo {/* Overlay sombre */}
    From 3935d6982bf256155b77319172191bdcd6f6bf6c Mon Sep 17 00:00:00 2001 From: Arthur Dodin Date: Sun, 12 Apr 2026 18:41:01 +0200 Subject: [PATCH 06/10] update(home): 2026 factions --- frontend/src/components/home/infosSection.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/home/infosSection.tsx b/frontend/src/components/home/infosSection.tsx index 1d34cdc..4e23515 100644 --- a/frontend/src/components/home/infosSection.tsx +++ b/frontend/src/components/home/infosSection.tsx @@ -68,7 +68,7 @@ export const Infos = () => { La petite histoire

    - Chaque année, deux factions composées d'une multitude d'équipes s'affrontent. Le thème de cette année : Vilains vs Justiciers. + Chaque année, deux factions composées d'une multitude d'équipes s'affrontent. Le thème de cette année : Divinités vs Monstres.

    From 2b8493e8ff7ebc23d2412dffce4d281449bbb499 Mon Sep 17 00:00:00 2001 From: Arthur Dodin Date: Sun, 12 Apr 2026 18:41:47 +0200 Subject: [PATCH 07/10] feat(profil): highlight contact input when empty --- frontend/src/components/profil/profilForm.tsx | 52 ++++++++++--------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/frontend/src/components/profil/profilForm.tsx b/frontend/src/components/profil/profilForm.tsx index 55eb0ef..b22662a 100644 --- a/frontend/src/components/profil/profilForm.tsx +++ b/frontend/src/components/profil/profilForm.tsx @@ -20,7 +20,7 @@ const branchOptions = [ { value: "SN_APPR", label: "Systeme Numérique en Apprentissage" }, { value: "Branch", label: "Branche" }, { value: "MM", label: "Mécanique et Matériaux" }, - { value : "Master", label: "Master"}, + { value: "Master", label: "Master" }, { value: "RI", label: "Ressources International" }, ]; @@ -46,7 +46,7 @@ export const ProfilForm = () => { const handleSubmit = async () => { setLoading(true); - const response = await updateCurrentUser({ branch : branch, contact : contact }); + const response = await updateCurrentUser({ branch: branch, contact: contact }); alert(response.message); setLoading(false); }; @@ -92,33 +92,37 @@ export const ProfilForm = () => { isClearable />
    -
    - - setContact(e.target.value)} /> +
    + {user.permission === "Student" || user.permission === "Admin" ? ( + + ) : ( + + )} + setContact(e.target.value)} className={user.contact === null || user.contact === "" ? "bg-white" : ""} />
    - <> - {user.discord_id ? ( -
    - ✅ Ton compte Discord est bien lié ! -
    - ) : ( - - )} - + <> + {user.discord_id ? ( +
    + ✅ Ton compte Discord est bien lié ! +
    + ) : ( + + )} + -
    +
    ); }; From b3dc0ea9c222a579a55162974d3e0023ba454e61 Mon Sep 17 00:00:00 2001 From: Arthur Dodin Date: Sun, 12 Apr 2026 19:53:10 +0200 Subject: [PATCH 08/10] lint: fix --- .../{ => components}/privacy/privacySection.tsx | 2 -- frontend/src/pages/privacy.tsx | 2 +- frontend/tsconfig.json | 14 ++++++++++---- 3 files changed, 11 insertions(+), 7 deletions(-) rename frontend/src/{ => components}/privacy/privacySection.tsx (99%) diff --git a/frontend/src/privacy/privacySection.tsx b/frontend/src/components/privacy/privacySection.tsx similarity index 99% rename from frontend/src/privacy/privacySection.tsx rename to frontend/src/components/privacy/privacySection.tsx index dd21fdd..b82a8b8 100644 --- a/frontend/src/privacy/privacySection.tsx +++ b/frontend/src/components/privacy/privacySection.tsx @@ -1,5 +1,3 @@ -import { Link } from "react-router-dom"; - export const PrivacySection = () => { return (
    diff --git a/frontend/src/pages/privacy.tsx b/frontend/src/pages/privacy.tsx index 21789a9..77b9090 100644 --- a/frontend/src/pages/privacy.tsx +++ b/frontend/src/pages/privacy.tsx @@ -1,7 +1,7 @@ // src/pages/privacy.tsx import { Navbar } from "../components/navbar"; import { Footer } from "../components/footer"; -import { PrivacySection } from "../privacy/privacySection"; +import { PrivacySection } from "../components/privacy/privacySection"; export const PrivacyPage = () => { diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index fec8c8e..20ff94b 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -1,13 +1,19 @@ { "files": [], "references": [ - { "path": "./tsconfig.app.json" }, - { "path": "./tsconfig.node.json" } + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.node.json" + } ], "compilerOptions": { "baseUrl": ".", "paths": { - "@/*": ["./src/*"] + "@/*": [ + "./src/*" + ] } } -} +} \ No newline at end of file From 72cef2705b669b5ccff3f3825b9ea2caa7536718 Mon Sep 17 00:00:00 2001 From: Arthur Dodin Date: Sun, 12 Apr 2026 20:06:48 +0200 Subject: [PATCH 09/10] ci: fix with npm 10 --- backend/package-lock.json | 389 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 389 insertions(+) diff --git a/backend/package-lock.json b/backend/package-lock.json index 42f6889..f252eba 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -219,6 +219,395 @@ "source-map-support": "^0.5.21" } }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild-kit/core-utils/node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, "node_modules/@esbuild-kit/esm-loader": { "version": "2.6.5", "resolved": "https://registry.npmjs.org/@esbuild-kit/esm-loader/-/esm-loader-2.6.5.tgz", From 70ad3d96e8996275f2f1e12972963e586620f6fa Mon Sep 17 00:00:00 2001 From: Arthur Dodin Date: Thu, 16 Apr 2026 01:56:44 +0200 Subject: [PATCH 10/10] refactor: components to Card & RevealSection --- CONVENTIONS.md | 2 +- README.md | 6 +- backend/src/controllers/role.controller.ts | 26 +- backend/src/controllers/tent.controller.ts | 2 +- backend/src/database/initdb/initrole.ts | 100 ++--- backend/src/services/news.service.ts | 10 +- backend/src/services/role.service.ts | 52 +-- backend/src/services/team.service.ts | 69 +-- backend/src/services/tent.service.ts | 6 +- backend/src/utils/emailtemplates.ts | 30 +- .../AdminChallenge/adminChalengeList.tsx | 250 ++++++----- .../adminChallengeAddPointsForm.tsx | 53 ++- .../AdminChallenge/adminChallengeEditor.tsx | 86 ++-- .../adminChallengeValidatedList.tsx | 113 ++--- .../Admin/AdminPerm/adminPermAction.tsx | 28 +- .../Admin/AdminPerm/adminPermForm.tsx | 96 ++-- .../Admin/AdminPerm/adminPermImport.tsx | 99 ++-- .../Admin/AdminPerm/adminPermList.tsx | 157 ++++--- .../Admin/AdminPerm/adminPermMembers.tsx | 35 +- frontend/src/components/Admin/adminBus.tsx | 159 +++---- frontend/src/components/Admin/adminEmail.tsx | 136 +++--- frontend/src/components/Admin/adminEvent.tsx | 29 +- .../components/Admin/adminExportImport.tsx | 421 +++++++++--------- .../src/components/Admin/adminFaction.tsx | 91 ++-- frontend/src/components/Admin/adminGames.tsx | 157 ++++--- frontend/src/components/Admin/adminNews.tsx | 271 +++++------ frontend/src/components/Admin/adminRole.tsx | 288 ++++++------ frontend/src/components/Admin/adminTeam.tsx | 129 +++--- frontend/src/components/Admin/adminTent.tsx | 17 +- frontend/src/components/Admin/adminUser.tsx | 14 +- .../src/components/Games/roleLeaderboard.tsx | 70 +-- .../components/Parrainnage/parrainageForm.tsx | 36 +- .../components/Plannings/planningSection.tsx | 107 ++--- .../components/WEI_SDI_Food/foodSection.tsx | 104 +++-- .../components/WEI_SDI_Food/sdiSection.tsx | 34 +- .../components/WEI_SDI_Food/weiSection.tsx | 32 +- frontend/src/components/auth/authForm.tsx | 134 +++--- .../components/challenge/challengeList.tsx | 157 ++++--- frontend/src/components/home/infosSection.tsx | 85 ++-- .../src/components/legals/legalsSection.tsx | 10 +- frontend/src/components/news/newsSection.tsx | 4 +- .../src/components/permanence/appealPerm.tsx | 192 ++++---- .../src/components/permanence/permList.tsx | 7 +- .../src/components/permanence/permUser.tsx | 4 +- .../src/components/privacy/privacySection.tsx | 18 +- frontend/src/components/profil/profilForm.tsx | 136 +++--- frontend/src/components/profil/roleForm.tsx | 72 ++- .../shotgun/preregisterCESection.tsx | 38 +- .../shotgun/preregisterTeamSection.tsx | 200 +++++---- .../src/components/shotgun/shotgunSection.tsx | 105 ++--- frontend/src/components/tent/tentSection.tsx | 194 ++++---- frontend/src/components/ui/card.tsx | 2 +- frontend/src/components/ui/revealSection.tsx | 43 ++ frontend/src/index.css | 26 ++ frontend/src/pages/admin.tsx | 234 ++++------ frontend/src/pages/perm.tsx | 2 +- frontend/src/pages/profil.tsx | 8 +- frontend/src/pages/roadbook.tsx | 8 +- .../src/services/requests/tent.service.ts | 6 +- 59 files changed, 2602 insertions(+), 2398 deletions(-) create mode 100644 frontend/src/components/ui/revealSection.tsx diff --git a/CONVENTIONS.md b/CONVENTIONS.md index 1758476..867421a 100644 --- a/CONVENTIONS.md +++ b/CONVENTIONS.md @@ -32,7 +32,7 @@ style(ui): adjust spacing in header ### Bonnes pratiques -* écrire à l’impératif → `add`, `fix`, `update` +* écrire à l'impératif → `add`, `fix`, `update` * description courte (-50 caractères) et explicite * un commit = une seule intention diff --git a/README.md b/README.md index 65e3eea..2211397 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Projet d'Intégration UTT – Environnement de Développement +# Projet d'Intégration UTT - Environnement de Développement Ce projet est une plateforme pour le site d'intégration de l'Université de Technologie de Troyes. Il utilise une architecture conteneurisée via Docker pour gérer le frontend, le backend et la base de données PostgreSQL. @@ -165,7 +165,7 @@ server { } ``` -Puis l’activer : +Puis l'activer : ```bash sudo ln -s /etc/nginx/sites-available/integration.utt.fr /etc/nginx/sites-enabled/ @@ -202,7 +202,7 @@ Ouvrir : ## 🔚 Nettoyage quand le dev est terminé -### 1. Supprimer l’entrée dans `/etc/hosts` +### 1. Supprimer l'entrée dans `/etc/hosts` ```bash sudo nano /etc/hosts diff --git a/backend/src/controllers/role.controller.ts b/backend/src/controllers/role.controller.ts index e53382d..e653377 100644 --- a/backend/src/controllers/role.controller.ts +++ b/backend/src/controllers/role.controller.ts @@ -9,7 +9,7 @@ export const updateUserPreferences = async (req: Request, res: Response) => { const { roleIds } = req.body; if (!userId || !Array.isArray(roleIds)) { - Error(res, { msg: "Données invalides" }); + Error(res, { msg: "Données invalides" }); } await role_service.updateUserPreferences(userId, roleIds); @@ -24,7 +24,7 @@ export const getUserPreferences = async (req: Request, res: Response) => { try { const userId = req.user?.userId; - if (!userId) Error(res, { msg: "Utilisateur non authentifié" }); + if (!userId) Error(res, { msg: "Utilisateur non authentifié" }); const preferences = await role_service.getUserPreferences(userId); const roleIds = preferences.map((pref) => pref.roleId); @@ -39,7 +39,7 @@ export const getUserPreferences = async (req: Request, res: Response) => { export const getUsersByRoleHandler = async (req: Request, res: Response) => { try { const { roleName } = req.params; - if (!roleName) Error(res, { msg: "Nom du rôle requis" }); + if (!roleName) Error(res, { msg: "Nom du rôle requis" }); const users = await role_service.getUsersByRoleName(roleName); Ok(res, { msg: "Utilisateurs récupérés", data: users }); @@ -55,7 +55,7 @@ export const addRoleToUser = async (req: Request, res: Response) => { const { userId, roleIds } = req.body; if (!userId || !Array.isArray(roleIds)) { - Error(res, { msg: "userId et roleIds requis" }); + Error(res, { msg: "userId et roleIds requis" }); } for (const roleId of roleIds) { @@ -72,13 +72,13 @@ export const addRoleToUser = async (req: Request, res: Response) => { } }; -// ❌ Supprimer rôle d’un utilisateur +// ❌ Supprimer rôle d'un utilisateur export const deleteRoleToUser = async (req: Request, res: Response) => { try { const { userId, roleId } = req.body; if (!userId || !roleId) { - Error(res, { msg: "userId et roleId requis" }); + Error(res, { msg: "userId et roleId requis" }); } await role_service.removeRoleFromUser(userId, roleId); @@ -111,11 +111,11 @@ export const getRoles = async (req: Request, res: Response) => { } }; -// 🔍 Rôles d’un utilisateur +// 🔍 Rôles d'un utilisateur export const getUserRoles = async (req: Request, res: Response) => { try { const { userId } = req.query; - if (!userId) Error(res, { msg: "userId requis" }); + if (!userId) Error(res, { msg: "userId requis" }); const roles = await role_service.getUserRoles(Number(userId)); Ok(res, { data: roles }); @@ -135,7 +135,7 @@ export const addPointsToRole = async (req: Request, res: Response) => { const { roleId, points } = req.body; if (!roleId || typeof points !== "number") { - Error(res, { msg: "roleId et points requis" }); + Error(res, { msg: "roleId et points requis" }); } await role_service.addPointsToRole(roleId, points); @@ -152,7 +152,7 @@ export const removePointsFromRole = async (req: Request, res: Response) => { const { roleId, points } = req.body; if (!roleId || typeof points !== "number") { - Error(res, { msg: "roleId et points requis" }); + Error(res, { msg: "roleId et points requis" }); } await role_service.removePointsFromRole(roleId, points); @@ -174,14 +174,14 @@ export const getAllRolePoints = async (_req: Request, res: Response) => { } }; -// 🔍 Points d’un rôle spécifique +// 🔍 Points d'un rôle spécifique export const getRolePoints = async (req: Request, res: Response) => { try { const { roleId } = req.params; - if (!roleId) Error(res, { msg: "roleId requis" }); + if (!roleId) Error(res, { msg: "roleId requis" }); const role = await role_service.getRolePoints(Number(roleId)); - if (!role) Error(res, { msg: "Rôle introuvable" }); + if (!role) Error(res, { msg: "Rôle introuvable" }); Ok(res, { data: role }); } catch (error) { diff --git a/backend/src/controllers/tent.controller.ts b/backend/src/controllers/tent.controller.ts index 9b9a55a..9fe0b39 100644 --- a/backend/src/controllers/tent.controller.ts +++ b/backend/src/controllers/tent.controller.ts @@ -85,7 +85,7 @@ export const toggleTentConfirmation = async (req: Request, res: Response) => { confirmed, }); - // Options d’email + // Options d'email const emailOptions = { from: "integration@utt.fr", to: [user1.email, user2.email], diff --git a/backend/src/database/initdb/initrole.ts b/backend/src/database/initdb/initrole.ts index 8c92ec4..c387959 100644 --- a/backend/src/database/initdb/initrole.ts +++ b/backend/src/database/initdb/initrole.ts @@ -6,57 +6,57 @@ import { eq } from "drizzle-orm"; // Liste des rôles à ajouter // Liste des rôles avec leurs descriptions const roles = [ - { name: "Animation", description: "Animer, divertir et motiver les CE et les nouveaux étudiants tout au long de la pré-inté et de l’inté." }, - { name: "Arbitre", description: "Arbitrer les différents défis pendant le semaine d'intégration." }, - { name: "Argentique", description: "Couvrir les événements de l’intégration, prendre des photos" }, - { name: "Bouffe", description: "Prévoir, organiser et coordonner tous les repas de l’inté. La bouffe c’est sacré !" }, - { name: "Bar", description: "Prévoir, organiser et coordonner toutes les boissons de l’inté !" }, - { name: "Bénévole", description: "Deviens bénévole et participe à différentes activités de l’inté !" }, - { name: "Cahier de vacances", description: "Élaborer le futur cahier de vacances des nouveaux avec des petits exercices et blagues." }, - { name: "Chasse au trésor", description: "Elaborer une chasse au trésor dans toute la capitale Troyenne." }, - { name: "Communication", description: "Préparer et gérer toute la communication de l’intégration" }, - { name: "Graphisme", description: "Créer une charte graphique incroyable pour les rendre l'inté encore plus belle !" }, - { name: "Déco", description: "Être créatif et fabriquer de quoi décorer l’UTT au thème de l’inté." }, - { name: "Défis TC", description: "Préparer un défi où les nouveaux TC doivent faire preuve d’ingéniosité pour fabriquer quelque chose." }, - { name: "Dev / Info", description: "Maintenir le site et l’application de l’inté et développer de nouveaux outils informatiques." }, - { name: "Faux amphi", description: "Créer un faux premier cours compliqué pour les TC avec des professeurs et des faux élèves." }, - { name: "Faux discours de rentrée", description: "Préparer et faire un discours de rentrée pour faire une petite frayeur aux nouveaux." }, - { name: "Logistique", description: "Préparer, organiser et mettre en place tout le matériel nécessaire pour l’intégration." }, - { name: "Lac d'Orient Express", description: "Emmener nos nouveaux voir le magnifique lac de Mesnil" }, - { name: "Média", description: "Couvrir les événements de l’intégration, prendre des photos et monter des films souvenirs." }, - { name: "Mascotte", description: "Etre juste le GOAT de l'intégration !" }, - { name: "Parrainage", description: "Attribuer des parrains/marraines aux nouveaux étudiants de manière personnalisée." }, - { name: "Partenariat", description: "Rechercher et établir des partenariats utiles pour l'intégration et les nouveaux étudiants." }, - { name: "Prévention", description: "Évaluer les risques et mettre en place des mesures préventives avant et pendant l’intégration." }, - { name: "Rallye", description: "Organiser une après-midi de jeux et d’activités sportives pour les nouveaux étudiants." }, - { name: "Respo CE", description: "Gérer le planning des CE et les guider pour qu’ils accueillent au mieux les nouveaux." }, - { name: "Respo Question", description: "Répondre au mieux aux questions des nouveaux !" }, - { name: "Sécu", description: "Gérer la sécurité des événements, notamment durant le WEI et la soirée d’intégration." }, - { name: "Soirée d'intégration", description: "Organiser une soirée sur le campus UTT durant la semaine d’inté." }, - { name: "Son et lumière", description: "Installer et gérer le son et la lumière durant les événements nécessitant une ambiance spéciale." }, - { name: "Soutenabilité", description: "Mettre en place des actions pour réduire l’impact environnemental de l’intégration." }, - { name: "SVE", description: "Mettre en place le Salon de la Vie Etudiante (SVE) lors de la deuxième semaine" }, - { name: "Traduction en anglais", description: "Traduire les contenus de l’intégration pour les étudiants étrangers." }, - { name: "Village Asso", description: "Organiser un événement pour présenter les associations UTTiennes aux nouveaux étudiants." }, - { name: "Visites", description: "Organiser les visites de l’UTT et de la ville pour les nouveaux étudiants." }, - { name: "WEI", description: "Organiser le Week-end d’intégration (transport, animation, logistique, soirée, etc.)." } - ]; - -export const initRoles = async () => { - for (const role of roles) { - await db.insert(roleSchema).values(role).onConflictDoNothing(); // Évite les doublons + { name: "Animation", description: "Animer, divertir et motiver les CE et les nouveaux étudiants tout au long de la pré-inté et de l'inté." }, + { name: "Arbitre", description: "Arbitrer les différents défis pendant le semaine d'intégration." }, + { name: "Argentique", description: "Couvrir les événements de l'intégration, prendre des photos" }, + { name: "Bouffe", description: "Prévoir, organiser et coordonner tous les repas de l'inté. La bouffe c'est sacré !" }, + { name: "Bar", description: "Prévoir, organiser et coordonner toutes les boissons de l'inté !" }, + { name: "Bénévole", description: "Deviens bénévole et participe à différentes activités de l'inté !" }, + { name: "Cahier de vacances", description: "Élaborer le futur cahier de vacances des nouveaux avec des petits exercices et blagues." }, + { name: "Chasse au trésor", description: "Elaborer une chasse au trésor dans toute la capitale Troyenne." }, + { name: "Communication", description: "Préparer et gérer toute la communication de l'intégration" }, + { name: "Graphisme", description: "Créer une charte graphique incroyable pour les rendre l'inté encore plus belle !" }, + { name: "Déco", description: "Être créatif et fabriquer de quoi décorer l'UTT au thème de l'inté." }, + { name: "Défis TC", description: "Préparer un défi où les nouveaux TC doivent faire preuve d'ingéniosité pour fabriquer quelque chose." }, + { name: "Dev / Info", description: "Maintenir le site et l'application de l'inté et développer de nouveaux outils informatiques." }, + { name: "Faux amphi", description: "Créer un faux premier cours compliqué pour les TC avec des professeurs et des faux élèves." }, + { name: "Faux discours de rentrée", description: "Préparer et faire un discours de rentrée pour faire une petite frayeur aux nouveaux." }, + { name: "Logistique", description: "Préparer, organiser et mettre en place tout le matériel nécessaire pour l'intégration." }, + { name: "Lac d'Orient Express", description: "Emmener nos nouveaux voir le magnifique lac de Mesnil" }, + { name: "Média", description: "Couvrir les événements de l'intégration, prendre des photos et monter des films souvenirs." }, + { name: "Mascotte", description: "Etre juste le GOAT de l'intégration !" }, + { name: "Parrainage", description: "Attribuer des parrains/marraines aux nouveaux étudiants de manière personnalisée." }, + { name: "Partenariat", description: "Rechercher et établir des partenariats utiles pour l'intégration et les nouveaux étudiants." }, + { name: "Prévention", description: "Évaluer les risques et mettre en place des mesures préventives avant et pendant l'intégration." }, + { name: "Rallye", description: "Organiser une après-midi de jeux et d'activités sportives pour les nouveaux étudiants." }, + { name: "Respo CE", description: "Gérer le planning des CE et les guider pour qu'ils accueillent au mieux les nouveaux." }, + { name: "Respo Question", description: "Répondre au mieux aux questions des nouveaux !" }, + { name: "Sécu", description: "Gérer la sécurité des événements, notamment durant le WEI et la soirée d'intégration." }, + { name: "Soirée d'intégration", description: "Organiser une soirée sur le campus UTT durant la semaine d'inté." }, + { name: "Son et lumière", description: "Installer et gérer le son et la lumière durant les événements nécessitant une ambiance spéciale." }, + { name: "Soutenabilité", description: "Mettre en place des actions pour réduire l'impact environnemental de l'intégration." }, + { name: "SVE", description: "Mettre en place le Salon de la Vie Etudiante (SVE) lors de la deuxième semaine" }, + { name: "Traduction en anglais", description: "Traduire les contenus de l'intégration pour les étudiants étrangers." }, + { name: "Village Asso", description: "Organiser un événement pour présenter les associations UTTiennes aux nouveaux étudiants." }, + { name: "Visites", description: "Organiser les visites de l'UTT et de la ville pour les nouveaux étudiants." }, + { name: "WEI", description: "Organiser le Week-end d'intégration (transport, animation, logistique, soirée, etc.)." } +]; - //Initier le table pour le jeux des orga - const [currentRole] = await db - .select() - .from(roleSchema) - .where(eq(roleSchema.name, role.name)) - .limit(1); +export const initRoles = async () => { + for (const role of roles) { + await db.insert(roleSchema).values(role).onConflictDoNothing(); // Évite les doublons - if (!currentRole) { - throw new Error(`Role not found: ${role.name}`); - } + //Initier le table pour le jeux des orga + const [currentRole] = await db + .select() + .from(roleSchema) + .where(eq(roleSchema.name, role.name)) + .limit(1); - await db.insert(rolePoints).values({ role_id: currentRole.id, points: 0 }).onConflictDoNothing() + if (!currentRole) { + throw new Error(`Role not found: ${role.name}`); } - }; + + await db.insert(rolePoints).values({ role_id: currentRole.id, points: 0 }).onConflictDoNothing() + } +}; diff --git a/backend/src/services/news.service.ts b/backend/src/services/news.service.ts index c65bb9d..c586056 100644 --- a/backend/src/services/news.service.ts +++ b/backend/src/services/news.service.ts @@ -3,7 +3,7 @@ import { db } from "../database/db"; import { News, newsSchema } from "../schemas/Basic/news.schema"; import { eq, desc, and } from "drizzle-orm"; // Créer une actu -export const createNews = async ( +export const createNews = async ( title: string, description: string, type: string, @@ -29,7 +29,7 @@ export const getAllNews = async () => { return await db.select().from(newsSchema).orderBy(desc(newsSchema.created_at)); }; -// Lister les actus publiées (pour l’onglet côté utilisateur) +// Lister les actus publiées (pour l'onglet côté utilisateur) export const getPublishedNews = async () => { return await db.select().from(newsSchema).where(eq(newsSchema.published, true)).orderBy(desc(newsSchema.created_at)); }; @@ -37,18 +37,18 @@ export const getPublishedNews = async () => { // Filtrer par type export const getPublishedNewsByType = async (type: string) => { return await db.select().from(newsSchema) - .where(and(eq(newsSchema.published, true),eq(newsSchema.type, type))) + .where(and(eq(newsSchema.published, true), eq(newsSchema.type, type))) .orderBy(desc(newsSchema.created_at)); }; // Publier une actu export const publishNews = async (id: number) => { - await db.update(newsSchema).set({ published : true }).where(eq(newsSchema.id, id)).returning(); + await db.update(newsSchema).set({ published: true }).where(eq(newsSchema.id, id)).returning(); }; -export const deleteNews = async(newsId: number) => { +export const deleteNews = async (newsId: number) => { await db.delete(newsSchema).where(eq(newsSchema.id, newsId)); diff --git a/backend/src/services/role.service.ts b/backend/src/services/role.service.ts index 850ecac..4c3247d 100644 --- a/backend/src/services/role.service.ts +++ b/backend/src/services/role.service.ts @@ -23,14 +23,14 @@ export const assignRoleToUser = async (userId: number, roleId: number) => { await db.insert(userRolesSchema).values(newUserRole); }; -// 2. Supprimer un rôle d’un utilisateur +// 2. Supprimer un rôle d'un utilisateur export const removeRoleFromUser = async (userId: number, roleId: number) => { await db .delete(userRolesSchema) .where(and(eq(userRolesSchema.user_id, userId), eq(userRolesSchema.role_id, roleId))); }; -// 3. Récupérer les rôles d’un utilisateur +// 3. Récupérer les rôles d'un utilisateur export const getUserRoles = async (userId: number) => { return await db .select({ @@ -83,7 +83,7 @@ export const getUsersByRoleName = async (roleName: string) => { .where(eq(userPreferencesSchema.roleId, roleId)); }; -// 6. Mettre à jour les préférences d’un utilisateur +// 6. Mettre à jour les préférences d'un utilisateur export const updateUserPreferences = async (userId: number, roleIds: number[]) => { await db.delete(userPreferencesSchema).where(eq(userPreferencesSchema.userId, userId)); @@ -91,7 +91,7 @@ export const updateUserPreferences = async (userId: number, roleIds: number[]) = await db.insert(userPreferencesSchema).values(newPreferences); }; -// 7. Récupérer les préférences d’un utilisateur +// 7. Récupérer les préférences d'un utilisateur export const getUserPreferences = async (userId: number) => { return await db .select({ @@ -158,7 +158,7 @@ export const getAllRolePoints = async () => { return await db.select().from(rolePoints); }; -// 12. Récupérer les points d’un rôle spécifique +// 12. Récupérer les points d'un rôle spécifique export const getRolePoints = async (roleId: number) => { const result = await db .select() @@ -170,35 +170,35 @@ export const getRolePoints = async (roleId: number) => { }; // 13. Checker le role d'User -export const checkRoleUser = async (userId: number, roleId: number) =>{ +export const checkRoleUser = async (userId: number, roleId: number) => { const existingRole = await db - .select() - .from(userRolesSchema) - .where(and(eq(userRolesSchema.user_id, userId), eq(userRolesSchema.role_id, roleId))); - - return existingRole; + .select() + .from(userRolesSchema) + .where(and(eq(userRolesSchema.user_id, userId), eq(userRolesSchema.role_id, roleId))); + + return existingRole; }; // 13. Ajouter un role à l'User -export const insertUserRole = async(userId: number, roleId: number) =>{ +export const insertUserRole = async (userId: number, roleId: number) => { - const newUserRole : UserRole = {user_id: userId, role_id: roleId} - await db.insert(userRolesSchema).values(newUserRole); + const newUserRole: UserRole = { user_id: userId, role_id: roleId } + await db.insert(userRolesSchema).values(newUserRole); } // 13. Recevoir tout les roles -export const getRoles = async()=>{ - try{ - const roles = await db.select({ - roleId: roleSchema.id, - name: roleSchema.name, - description: roleSchema.description - }).from(roleSchema); - return roles; - } - catch(error){ - throw new Error("Erreur lors de la récupération des rôles : " + error.message); - } +export const getRoles = async () => { + try { + const roles = await db.select({ + roleId: roleSchema.id, + name: roleSchema.name, + description: roleSchema.description + }).from(roleSchema); + return roles; + } + catch (error) { + throw new Error("Erreur lors de la récupération des rôles : " + error.message); + } } \ No newline at end of file diff --git a/backend/src/services/team.service.ts b/backend/src/services/team.service.ts index e4f3991..9e3249b 100644 --- a/backend/src/services/team.service.ts +++ b/backend/src/services/team.service.ts @@ -11,46 +11,47 @@ import { permission } from "process"; export const createTeam = async (teamName: string, members: number[]) => { - const newTeam = await db.insert(teamSchema).values({ name: teamName }).returning(); - const teamId = newTeam[0].id; - await Promise.all(members.map((userId) => - db.insert(userTeamsSchema).values({ team_id: teamId, user_id: userId }) - )); - - return newTeam; + const newTeam = await db.insert(teamSchema).values({ name: teamName }).returning(); + const teamId = newTeam[0].id; + await Promise.all(members.map((userId) => + db.insert(userTeamsSchema).values({ team_id: teamId, user_id: userId }) + )); + + return newTeam; }; export const createTeamLight = async (teamName: string, factionId: number) => { - const newTeam = await db.insert(teamSchema).values({ name: teamName }).returning({teamId : teamSchema.id}); + const newTeam = await db.insert(teamSchema).values({ name: teamName }).returning({ teamId: teamSchema.id }); - if(factionId){ - await db.insert(teamFactionSchema).values({faction_id : factionId, team_id: newTeam[0].teamId}); + if (factionId) { + await db.insert(teamFactionSchema).values({ faction_id: factionId, team_id: newTeam[0].teamId }); } return newTeam; }; -export const getUserTeam = async(userId : number) => { +export const getUserTeam = async (userId: number) => { - const userTeam = await db.select({userTeamId: userTeamsSchema.team_id}).from(userTeamsSchema).where(eq(userTeamsSchema.user_id, userId)); + const userTeam = await db.select({ userTeamId: userTeamsSchema.team_id }).from(userTeamsSchema).where(eq(userTeamsSchema.user_id, userId)); - return userTeam[0]?.userTeamId + return userTeam[0]?.userTeamId } -export const getTeams = async() => { +export const getTeams = async () => { const teams = await db.select( { - teamId : teamSchema.id, - name : teamSchema.name, - description : teamSchema.description, - type : teamSchema.type}).from(teamSchema); + teamId: teamSchema.id, + name: teamSchema.name, + description: teamSchema.description, + type: teamSchema.type + }).from(teamSchema); return teams } -export const getTeamsAll = async() => { +export const getTeamsAll = async () => { const teams = await db.select().from(teamSchema); @@ -68,26 +69,26 @@ export const getTeamsAll = async() => { } -export const modifyTeam = async ( teamID: number, teamMembers: number[], factionID:number, name? :string, type?: string) => { +export const modifyTeam = async (teamID: number, teamMembers: number[], factionID: number, name?: string, type?: string) => { // 1. Mise à jour des champs de l'équipe - + if (name !== undefined) { await db .update(teamSchema) - .set({name: name}) + .set({ name: name }) .where(eq(teamSchema.id, teamID)); } if (type !== undefined) { await db .update(teamSchema) - .set({type: type}) + .set({ type: type }) .where(eq(teamSchema.id, teamID)); } - // 2. Mise à jour des membres de l’équipe (remplace les anciens) - if(teamMembers.length !== 0){ + // 2. Mise à jour des membres de l'équipe (remplace les anciens) + if (teamMembers.length !== 0) { if (Array.isArray(teamMembers)) { // Supprimer les anciens membres await db.delete(userTeamsSchema).where(eq(userTeamsSchema.team_id, teamID)); @@ -102,13 +103,13 @@ export const modifyTeam = async ( teamID: number, teamMembers: number[], faction ); } } - }else{ + } else { await db.delete(userTeamsSchema).where(eq(userTeamsSchema.team_id, teamID)); } // 3. Mise à jour de la faction (remplace la relation précédente) if (factionID !== undefined) { - // Supprimer l’ancienne relation + // Supprimer l'ancienne relation await db.delete(teamFactionSchema).where(eq(teamFactionSchema.team_id, teamID)); // Ajouter la nouvelle @@ -150,7 +151,7 @@ export const getAllTeamsWithUsers = async () => { teamId: teamSchema.id, teamName: teamSchema.name, teamType: teamSchema.type, - teamFaction : factionSchema.name, + teamFaction: factionSchema.name, userId: userSchema.id, firstName: userSchema.first_name, lastName: userSchema.last_name, @@ -160,14 +161,14 @@ export const getAllTeamsWithUsers = async () => { .from(teamSchema) .innerJoin(userTeamsSchema, eq(teamSchema.id, userTeamsSchema.team_id)) .innerJoin(userSchema, eq(userSchema.id, userTeamsSchema.user_id)) - .innerJoin(teamFactionSchema, eq(teamSchema.id, teamFactionSchema.team_id)) - .innerJoin(factionSchema, eq(factionSchema.id, teamFactionSchema.faction_id)); + .innerJoin(teamFactionSchema, eq(teamSchema.id, teamFactionSchema.team_id)) + .innerJoin(factionSchema, eq(factionSchema.id, teamFactionSchema.faction_id)); const teamsMap = new Map { export const getTeamFaction = async (teamId: any) => { const teamFactionId = await db - .select({faction_id : teamFactionSchema.faction_id}) + .select({ faction_id: teamFactionSchema.faction_id }) .from(teamFactionSchema) .where(eq(teamFactionSchema.team_id, teamId)); @@ -253,7 +254,7 @@ export const getUsersWithTeam = async () => { teamId: userTeamsSchema.team_id, } ).from(userTeamsSchema); - return userswithteam; + return userswithteam; } catch (err) { console.error('Erreur lors de la récupération des utilisateurs possédant une team ', err); throw new Error('Erreur de base de données'); @@ -264,7 +265,7 @@ export const getTeam = async (teamId: any) => { try { const team = await db.select( { - teamId : teamSchema.id, + teamId: teamSchema.id, teamName: teamSchema.name } ).from(teamSchema).where(eq(teamSchema.id, teamId)); diff --git a/backend/src/services/tent.service.ts b/backend/src/services/tent.service.ts index 917fc24..758f1cb 100644 --- a/backend/src/services/tent.service.ts +++ b/backend/src/services/tent.service.ts @@ -43,12 +43,12 @@ export const cancelTent = async (userId1: number) => { return await db .delete(userTentSchema) .where( - or(eq(userTentSchema.user_id_1, userId1), eq(userTentSchema.user_id_2, userId1)), + or(eq(userTentSchema.user_id_1, userId1), eq(userTentSchema.user_id_2, userId1)), ); }; /** - * Récupérer la tente d’un utilisateur. + * Récupérer la tente d'un utilisateur. */ export const getTentByUser = async (userId: number) => { return await db @@ -61,7 +61,7 @@ export const getTentByUser = async (userId: number) => { * Récupérer toutes les tentes (avec infos des 2 utilisateurs). */ export const getAllTents = async () => { - + const user2 = alias(userSchema, "user2"); return await db diff --git a/backend/src/utils/emailtemplates.ts b/backend/src/utils/emailtemplates.ts index ae36d14..1f76fc3 100644 --- a/backend/src/utils/emailtemplates.ts +++ b/backend/src/utils/emailtemplates.ts @@ -69,17 +69,17 @@ export const templateNotebook = `

    Salut à toi !!!!

    -

    Si tu reçois ce mail, c’est que tu es sur le point de rejoindre l’UTT et de vivre tes premières années en école supérieure.

    +

    Si tu reçois ce mail, c'est que tu es sur le point de rejoindre l'UTT et de vivre tes premières années en école supérieure.

    -

    Mais après toutes ces vacances, il est important de ne pas s’endormir et de vite se remettre au travail !

    +

    Mais après toutes ces vacances, il est important de ne pas s'endormir et de vite se remettre au travail !

    -

    C’est pourquoi l’intégration te propose un cahier de vacances qui te permettra de te remettre à niveau.

    +

    C'est pourquoi l'intégration te propose un cahier de vacances qui te permettra de te remettre à niveau.

    -

    Toutes les bases y sont revues, de la terminale… jusqu’au CP. À toi de nous prouver que tu en es capable ! Méthodologie et rigueur seront nécessaires pour en venir à bout (et pas mal d’humour également).

    +

    Toutes les bases y sont revues, de la terminale… jusqu'au CP. À toi de nous prouver que tu en es capable ! Méthodologie et rigueur seront nécessaires pour en venir à bout (et pas mal d'humour également).

    -

    Ce cahier sera examiné par un jury extrêmement talentueux : des ingénieurs hors pair, ayant déjà prouvé leur valeur lors d’un concours de Ricard sur la plage de Banyuls-sur-Mer.

    +

    Ce cahier sera examiné par un jury extrêmement talentueux : des ingénieurs hors pair, ayant déjà prouvé leur valeur lors d'un concours de Ricard sur la plage de Banyuls-sur-Mer.

    -

    À toi de leur montrer que tu peux égaler leurs compétences ! Ce jury n’hésitera pas à te récompenser pour tes efforts si tu nous renvoies tes réponses à cette adresse mail.

    +

    À toi de leur montrer que tu peux égaler leurs compétences ! Ce jury n'hésitera pas à te récompenser pour tes efforts si tu nous renvoies tes réponses à cette adresse mail.

    Alors si tu veux y participer, tu peux le télécharger juste ici et le renvoyer à clement.duranson@utt.fr avant le dimanche 31 août.

    @@ -97,7 +97,7 @@ export const templateNotebook = `

    À très vite !

    -

    Toute l’équipe de l’intégration

    +

    Toute l'équipe de l'intégration

    @@ -162,9 +162,9 @@ export const templateAttributionBus = `
  • Vêtements : changes pour 2 jours, pull, maillot de bain 👙
  • Manteau imperméable 🧥
  • Affaires salissables : change complet & chaussures (à mettre dès le départ en bus) 🚌
  • -
  • Produits d’hygiène : brosse à dent, serviette, nécessaire de toilette 🪥
  • +
  • Produits d'hygiène : brosse à dent, serviette, nécessaire de toilette 🪥
  • Tongues/crocs pour les douches 🩴
  • -
  • Papiers importants : Carte d’identité, CB & liquide, autorisation parentale (pour les mineurs) 💳
  • +
  • Papiers importants : Carte d'identité, CB & liquide, autorisation parentale (pour les mineurs) 💳
  • Ta place au WEI 📩
  • Crème solaire & anti-moustique ☀️
  • De quoi grignoter (prenez un pique-nique à manger avant de prendre le bus, pas dans le bus) 😋
  • @@ -173,7 +173,7 @@ export const templateAttributionBus = `

    🚫 Affaires interdites :

      -
    • Boissons autres que de l’eau
    • +
    • Boissons autres que de l'eau
    • Substances illicites
    • Armes blanches
    • Déodorant en spray
    • @@ -271,15 +271,15 @@ export const templateWelcome = `

      Hello there, newcomer!

      -

      Congratulations on your admission to UTT! We are the integration team – volunteer students who are carefully preparing your arrival to make it truly unforgettable.

      -

      A bunch of amazing events, all based on voluntary participation, await you starting on Monday, September 1st, whether you're arriving in your 1st year, 3rd year, Master's or Bachelor’s program.

      -

      Everything is set up for you to have fun and meet the people who will make your time at UTT unforgettable. But first things first – it’s time to get ready.

      +

      Congratulations on your admission to UTT! We are the integration team - volunteer students who are carefully preparing your arrival to make it truly unforgettable.

      +

      A bunch of amazing events, all based on voluntary participation, await you starting on Monday, September 1st, whether you're arriving in your 1st year, 3rd year, Master's or Bachelor's program.

      +

      Everything is set up for you to have fun and meet the people who will make your time at UTT unforgettable. But first things first - it's time to get ready.

      Please make sure to complete the following tasks before you arrive:

      To access the integration website, you just need to change your password by clicking the following link:

      Change your password

      Warning: this link is valid only once!

      -

      Once that’s done, you’ll be able to log into your account and find all the information about the integration week events here:

      https://integration.utt.fr

      Also, don’t forget to link your Discord account via the "My Account" section so you can connect with your team and the other newcomers. +

      Once that's done, you'll be able to log into your account and find all the information about the integration week events here:

      https://integration.utt.fr

      Also, don't forget to link your Discord account via the "My Account" section so you can connect with your team and the other newcomers.

      When you arrive at UTT, an older student will become your mentor ("parrain" or "marraine"). They will be your main contact to help you discover the school and student life in Troyes, and to answer any questions you may have about UTT, housing, classes, life in Troyes, etc.

      To match you with someone who fits you best, we invite you to fill out this questionnaire

      @@ -350,7 +350,7 @@ export const templateNotifyTentConfirmation = `

      - 👉 Tu peux consulter l’état de ta tente sur le site de l'inté dans l’onglet Tentes. + 👉 Tu peux consulter l'état de ta tente sur le site de l'inté dans l'onglet Tentes.

      diff --git a/frontend/src/components/Admin/AdminChallenge/adminChalengeList.tsx b/frontend/src/components/Admin/AdminChallenge/adminChalengeList.tsx index a4a05d2..b6f823e 100644 --- a/frontend/src/components/Admin/AdminChallenge/adminChalengeList.tsx +++ b/frontend/src/components/Admin/AdminChallenge/adminChalengeList.tsx @@ -1,5 +1,5 @@ import { useMemo, useState } from "react"; -import { Card } from "../../ui/card"; +import { Card, CardContent, CardHeader, CardTitle } from "../../ui/card"; import { Button } from "../../ui/button"; import { Challenge } from "../../../interfaces/challenge.interface"; import { deleteChallenge, validateChallenge } from "../../../services/requests/challenge.service"; @@ -94,133 +94,143 @@ const AdminChallengeList = ({ challenges, refreshChallenges, onEdit, teams, fact }; return ( - -

      📜 Challenges

      - - {/* 🔎 Barre de recherche */} -
      - - setSearchTerm(e.target.value)} - className="flex-1" - /> -
      - - {/* Liste filtrée */} -
      - {filteredChallenges.length > 0 ? ( - filteredChallenges.map((c) => ( -
      -
      -

      {c.title}

      -

      {c.description}

      -

      Catégorie : {c.category}

      -

      Points : {c.points}

      -
      - -
      - - - -
      - - {showValidationFormForId === c.id && ( -
      -

      ✅ Valider le challenge

      - - setSelectedTargetId(Number(option?.value))} - options={users.map((u: User) => ({ - value: u.userId, - label: `${u.firstName} ${u.lastName}`, - }))} - /> - )} - - {validationType === "team" && ( - setSelectedTargetId(Number(option?.value))} - options={factions.map((f: Faction) => ({ - value: f.factionId, - label: f.name, - }))} - /> - )} + + + + 📜 Challenges + + + + + {/* 🔎 Barre de recherche */} +
      + + setSearchTerm(e.target.value)} + className="flex-1" + /> +
      + + {/* Liste filtrée */} +
      + {filteredChallenges.length > 0 ? ( + filteredChallenges.map((c) => ( + + +
      +

      {c.title}

      +

      {c.description}

      +

      Catégorie : {c.category}

      +

      Points : {c.points}

      +
      -
      +
      +
      -
      - )} -
      - )) - ) : ( -

      Aucun challenge trouvé.

      - )} -
      + + {showValidationFormForId === c.id && ( + + +

      ✅ Valider le challenge

      + + setSelectedTargetId(Number(option?.value))} + options={users.map((u: User) => ({ + value: u.userId, + label: `${u.firstName} ${u.lastName}`, + }))} + /> + )} + + {validationType === "team" && ( + setSelectedTargetId(Number(option?.value))} + options={factions.map((f: Faction) => ({ + value: f.factionId, + label: f.name, + }))} + /> + )} + +
      + + +
      +
      +
      + )} + + + )) + ) : ( +

      Aucun challenge trouvé.

      + )} +
      + ); }; diff --git a/frontend/src/components/Admin/AdminChallenge/adminChallengeAddPointsForm.tsx b/frontend/src/components/Admin/AdminChallenge/adminChallengeAddPointsForm.tsx index 5903831..761c97c 100644 --- a/frontend/src/components/Admin/AdminChallenge/adminChallengeAddPointsForm.tsx +++ b/frontend/src/components/Admin/AdminChallenge/adminChallengeAddPointsForm.tsx @@ -1,6 +1,7 @@ import { useState, useEffect } from "react"; import { Input } from "../../ui/input"; import { Button } from "../../ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "../../ui/card"; import Select from "react-select"; import { getAllFactionsAdmin } from "../../../services/requests/faction.service"; import { Faction } from "../../../interfaces/faction.interface"; @@ -65,38 +66,42 @@ export const AdminChallengeAddPointsForm = () => { }; return ( -
      -
      -

      - 🎯 Ajouter des points à une faction -

      +
      + + + + 🎯 Ajouter des points à une faction + + + - setTitle(e.target.value)} placeholder="Titre du challenge" /> + setTitle(e.target.value)} placeholder="Titre du challenge" /> - setPoints(e.target.value)} placeholder="Nombre de points" /> + setPoints(e.target.value)} placeholder="Nombre de points" /> - setReason(e.target.value)} placeholder="Raison" /> + setReason(e.target.value)} placeholder="Raison" /> -
      - -
      -
      +
      + +
      + +
      ); }; diff --git a/frontend/src/components/Admin/AdminChallenge/adminChallengeEditor.tsx b/frontend/src/components/Admin/AdminChallenge/adminChallengeEditor.tsx index 3d8a3a8..c707ee2 100644 --- a/frontend/src/components/Admin/AdminChallenge/adminChallengeEditor.tsx +++ b/frontend/src/components/Admin/AdminChallenge/adminChallengeEditor.tsx @@ -1,7 +1,7 @@ import { useState, useEffect } from "react"; import { Button } from "../../ui/button"; import { Input } from "../../ui/input"; -import { Card } from "../../ui/card"; +import { Card, CardContent, CardHeader, CardTitle } from "../../ui/card"; import Select from "react-select"; import { createChallenge, updateChallenge } from "../../../services/requests/challenge.service"; import { Challenge } from "../../../interfaces/challenge.interface"; @@ -61,48 +61,50 @@ const ChallengeEditor = ({ editingChallenge, setEditingChallenge, refreshChallen }; return ( - -

      - {editingChallenge ? "✏️ Modifier Challenge" : "🛠️ Créer Challenge"} -

      - - setForm({ ...form, title: e.target.value })} - /> - setForm({ ...form, description: e.target.value })} - /> - setForm({ ...form, points: Number(e.target.value) })} - /> - -
      - - {editingChallenge && ( - - )} -
      + {editingChallenge && ( + + )} +
      + ); }; diff --git a/frontend/src/components/Admin/AdminChallenge/adminChallengeValidatedList.tsx b/frontend/src/components/Admin/AdminChallenge/adminChallengeValidatedList.tsx index dad8c74..5f70419 100644 --- a/frontend/src/components/Admin/AdminChallenge/adminChallengeValidatedList.tsx +++ b/frontend/src/components/Admin/AdminChallenge/adminChallengeValidatedList.tsx @@ -1,5 +1,6 @@ import { useMemo, useState } from "react"; import { Button } from "../../ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "../../ui/card"; import { Input } from "../../ui/input"; import { unvalidateChallenge } from "../../../services/requests/challenge.service"; import Swal from "sweetalert2"; @@ -68,64 +69,70 @@ export const AdminValidatedChallengesList = ({ }; return ( -
      -
      -

      📋 Challenges validés

      +
      + + + + 📋 Challenges validés + + + - {/* Recherche */} -
      - - setSearch(e.target.value)} - className="border-none focus:ring-0 bg-transparent flex-1" - /> -
      + {/* Recherche */} +
      + + setSearch(e.target.value)} + className="border-none focus:ring-0 bg-transparent flex-1" + /> +
      - {/* Grille */} - {filtered.length === 0 ? ( -

      Aucun challenge validé trouvé.

      - ) : ( -
      - {filtered.map((c) => ( -
      -
      -

      {c.challenge_name}

      -

      {c.challenge_categorie}

      -

      {c.challenge_description}

      -
      + {/* Grille */} + {filtered.length === 0 ? ( +

      Aucun challenge validé trouvé.

      + ) : ( +
      + {filtered.map((c) => ( +
      +
      +

      {c.challenge_name}

      +

      {c.challenge_categorie}

      +

      {c.challenge_description}

      +
      -
      -

      Points : {c.points}

      -

      - Validé le : {new Date(c.validated_at).toLocaleDateString()} -

      -
      +
      +

      Points : {c.points}

      +

      + Validé le : {new Date(c.validated_at).toLocaleDateString()} +

      +
      -
      -

      Destinataire :

      - {c.target_faction_name &&

      {c.target_faction_name}

      } - {c.target_team_name &&

      {c.target_team_name}

      } - {(c.target_user_firstname || c.target_user_lastname) && ( -

      {c.target_user_firstname} {c.target_user_lastname}

      - )} -
      +
      +

      Destinataire :

      + {c.target_faction_name &&

      {c.target_faction_name}

      } + {c.target_team_name &&

      {c.target_team_name}

      } + {(c.target_user_firstname || c.target_user_lastname) && ( +

      {c.target_user_firstname} {c.target_user_lastname}

      + )} +
      - -
      - ))} -
      - )} -
      + +
      + ))} +
      + )} + +
      ); }; diff --git a/frontend/src/components/Admin/AdminPerm/adminPermAction.tsx b/frontend/src/components/Admin/AdminPerm/adminPermAction.tsx index 55593b8..c7a35ce 100644 --- a/frontend/src/components/Admin/AdminPerm/adminPermAction.tsx +++ b/frontend/src/components/Admin/AdminPerm/adminPermAction.tsx @@ -1,4 +1,5 @@ import { Button } from "../../../components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "../../../components/ui/card"; import Swal from "sweetalert2"; import { openPermanence, closePermanence } from "../../../services/requests/permanence.service"; @@ -38,7 +39,7 @@ const PermanenceActions: React.FC = ({ permanences, onRe const toOpen = permanences.filter((p) => { const permDate = normalizeDate(new Date(p.start_at)).getTime(); return permDate > today && permDate <= threshold && !p.is_open; - }); + }); try { await Promise.all(toOpen.map((p) => openPermanence(p.id))); @@ -75,14 +76,23 @@ const PermanenceActions: React.FC = ({ permanences, onRe }; return ( -
      - - -
      + + + + ⚡ Actions rapides + + + +
      + + +
      +
      +
      ); }; diff --git a/frontend/src/components/Admin/AdminPerm/adminPermForm.tsx b/frontend/src/components/Admin/AdminPerm/adminPermForm.tsx index 2b9b0f1..ed10c23 100644 --- a/frontend/src/components/Admin/AdminPerm/adminPermForm.tsx +++ b/frontend/src/components/Admin/AdminPerm/adminPermForm.tsx @@ -1,5 +1,5 @@ import { useState, useEffect } from "react"; -import { Card } from "../../../components/ui/card"; +import { Card, CardHeader, CardContent, CardTitle } from "../../../components/ui/card"; import { Input } from "../../../components/ui/input"; import { Textarea } from "../../../components/ui/textarea"; import { Button } from "../../../components/ui/button"; @@ -58,8 +58,8 @@ const PermanenceForm = ({ setName(editPermanence.name); setDesc(editPermanence.description); setLocation(editPermanence.location); - setStartAt(formatDateForInput(editPermanence.start_at)); - setEndAt(formatDateForInput(editPermanence.end_at)); + setStartAt(formatDateForInput(editPermanence.start_at)); + setEndAt(formatDateForInput(editPermanence.end_at)); setCapacity(editPermanence.capacity); setDifficulty(editPermanence.difficulty); if (editPermanence.respo) { @@ -96,15 +96,15 @@ const PermanenceForm = ({ difficulty, respoId, }; - - if (editMode && editPermanence) { - await updatePermanence(editPermanence.id, payload); - Swal.fire("Succès", "Permanence mise à jour", "success"); - onCancelEdit(); - } else { - await createPermanence(payload); - Swal.fire("Succès", "Permanence créée", "success"); - } + + if (editMode && editPermanence) { + await updatePermanence(editPermanence.id, payload); + Swal.fire("Succès", "Permanence mise à jour", "success"); + onCancelEdit(); + } else { + await createPermanence(payload); + Swal.fire("Succès", "Permanence créée", "success"); + } resetForm(); onRefresh(); @@ -134,8 +134,13 @@ const PermanenceForm = ({ : null; return ( - -
      + + + + {editMode ? "✏️ Modifier une permanence" : "➕ Créer une permanence"} + + + setLocation(e.target.value)} /> - - setStartAt(e.target.value)} - /> - - setEndAt(e.target.value)} - /> - - setCapacity(Number(e.target.value))} - /> - - setDifficulty(Number(e.target.value))} - /> +
      + + setStartAt(e.target.value)} + /> +
      +
      + + setEndAt(e.target.value)} + /> +
      +
      + + setCapacity(Number(e.target.value))} + /> +
      +
      + + setDifficulty(Number(e.target.value))} + /> +
      - {/* Sélection du responsable */} +
      + - -
      + +
      - {message && ( -

      - {message} -

      - )} + }`} + > + {message} +

      + )} -
      -

      - 📄 Exemple de fichier CSV : -

      -
      -          {`name,description,location,start_at,end_at,capacity,difficulty,
      +        
      +          
      +            
      +              📄 Exemple de fichier CSV :
      +            
      +          
      +          
      +            
      +              {`name,description,location,start_at,end_at,capacity,difficulty,
       Permanence 1,Accueil matin,A001,2025-05-01T08:00,2025-05-01T10:00,3,10
       Permanence 2,Accueil après-midi,A002,2025-05-02T14:00,2025-05-02T16:00,4,15`}
      -        
      -

      - Le fichier doit être encodé en UTF-8 et utiliser une virgule comme séparateur. Les dates doivent être au format - - aaaa-mm-jjThh:mm - . -

      -
      -
      + +

      + Le fichier doit être encodé en UTF-8 et utiliser une virgule comme séparateur. Les dates doivent être au format + + aaaa-mm-jjThh:mm + . +

      + + + + ); }; \ No newline at end of file diff --git a/frontend/src/components/Admin/AdminPerm/adminPermList.tsx b/frontend/src/components/Admin/AdminPerm/adminPermList.tsx index dbff9b3..ef973a3 100644 --- a/frontend/src/components/Admin/AdminPerm/adminPermList.tsx +++ b/frontend/src/components/Admin/AdminPerm/adminPermList.tsx @@ -1,5 +1,5 @@ import { Button } from "../../../components/ui/button"; -import { Card } from "../../../components/ui/card"; +import { Card, CardContent, CardHeader, CardTitle } from "../../../components/ui/card"; import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "../../../components/ui/accordion"; import { ChevronDown } from "lucide-react"; import Swal from "sweetalert2"; @@ -58,81 +58,92 @@ const PermanenceList = ({ permanences, users, onRefresh, onEdit }: PermanenceLis }, {}); return ( - - {Object.entries(groupedByDay).map(([day, perms]) => ( - - - {day.charAt(0).toUpperCase() + day.slice(1)} - - - - {perms.map((perm) => ( - -
      - {/* Colonne gauche : infos + actions */} -
      -

      {perm.name}

      -

      {perm.description}

      + + + + 📋 Permanences existantes + + + + + {Object.entries(groupedByDay).map(([day, perms]) => ( + + + {day.charAt(0).toUpperCase() + day.slice(1)} + + + + {perms.map((perm) => ( + + +
      + {/* Colonne gauche : infos + actions */} +
      +

      {perm.name}

      +

      {perm.description}

      -
      -
      📍 Lieu : {perm.location}
      -
      - 🕒 Début :{" "} - {format(new Date(perm.start_at), "HH:mm", { locale: fr })} -
      -
      - 🕔 Fin :{" "} - {format(new Date(perm.end_at), "HH:mm", { locale: fr })} -
      -
      👥 Capacité : {perm.capacity}
      -
      🎚️ Difficulté : {perm.difficulty}
      -
      - 👤 Responsable :{" "} - {perm.respo ? `${perm.respo.firstName} ${perm.respo.lastName}` : "Aucun"} -
      -
      +
      +
      📍 Lieu : {perm.location}
      +
      + 🕒 Début :{" "} + {format(new Date(perm.start_at), "HH:mm", { locale: fr })} +
      +
      + 🕔 Fin :{" "} + {format(new Date(perm.end_at), "HH:mm", { locale: fr })} +
      +
      👥 Capacité : {perm.capacity}
      +
      🎚️ Difficulté : {perm.difficulty}
      +
      + 👤 Responsable :{" "} + {perm.respo ? `${perm.respo.firstName} ${perm.respo.lastName}` : "Aucun"} +
      +
      - {/* Actions */} -
      - {perm.is_open ? ( - - ) : ( - - )} - - -
      -
      + {/* Actions */} +
      + {perm.is_open ? ( + + ) : ( + + )} + + +
      +
      - {/* Colonne droite : membres */} -
      -

      👥 Membres

      - -
      -
      - - ))} - - - ))} - + {/* Colonne droite : membres */} +
      +

      👥 Membres

      + +
      +
      + +
      + ))} +
      +
      + ))} +
      + + ); }; diff --git a/frontend/src/components/Admin/AdminPerm/adminPermMembers.tsx b/frontend/src/components/Admin/AdminPerm/adminPermMembers.tsx index 677d263..b54b7d4 100644 --- a/frontend/src/components/Admin/AdminPerm/adminPermMembers.tsx +++ b/frontend/src/components/Admin/AdminPerm/adminPermMembers.tsx @@ -1,7 +1,7 @@ import { useEffect, useMemo, useState } from "react"; import Select, { SingleValue } from "react-select"; import { Button } from "../../../components/ui/button"; -import { Card } from "../../../components/ui/card"; +import { Card, CardContent, CardHeader, CardTitle } from "../../../components/ui/card"; import Swal from "sweetalert2"; import { @@ -52,7 +52,7 @@ const PermanenceMembers: React.FC = ({ perm, users, onRe const fetchMembers = async (): Promise => { try { setLoading(true); - const res = await getUsersByPermanence(perm.id); + const res = await getUsersByPermanence(perm.id); setMembers((res.data as PermanenceMember[]) ?? []); } catch { Swal.fire("Erreur", "Impossible de récupérer les membres", "error"); @@ -121,20 +121,22 @@ const PermanenceMembers: React.FC = ({ perm, users, onRe }; return ( - -
      -

      Membres

      - -
      + + +
      + Membres + +
      +
      {expanded && ( -
      + {loading ? (

      Chargement…

      ) : members.length === 0 ? ( -

      Aucun membre pour l’instant.

      +

      Aucun membre pour l'instant.

      ) : (
        {members.map((user) => ( @@ -147,11 +149,10 @@ const PermanenceMembers: React.FC = ({ perm, users, onRe
        @@ -186,7 +187,7 @@ const PermanenceMembers: React.FC = ({ perm, users, onRe

        Attention : en tant qu'Admin vous pouvez bypass les quotas

        -
        + )} ); diff --git a/frontend/src/components/Admin/adminBus.tsx b/frontend/src/components/Admin/adminBus.tsx index 04a853a..31877d0 100644 --- a/frontend/src/components/Admin/adminBus.tsx +++ b/frontend/src/components/Admin/adminBus.tsx @@ -2,12 +2,13 @@ import { useState } from "react"; import Swal from "sweetalert2"; import { busAttribution, importBusCSV } from "../../services/requests/bus.service"; import { Button } from "../ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "../ui/card"; export const AdminBusTools = () => { const [file, setFile] = useState(null); const [message, setMessage] = useState(""); - // 📤 Envoi des emails d’attribution bus + // 📤 Envoi des emails d'attribution bus const handleSendBusAttribution = async () => { const confirm = await Swal.fire({ title: "Envoyer les attributions de bus ?", @@ -65,95 +66,101 @@ export const AdminBusTools = () => { }; return ( -
        - {/* Titre principal */} -

        - 🚍 Outils d’administration des Bus -

        - - {/* Section attribution */} -
        -

        - ✉️ Envoyer les attributions -

        -

        - Envoie les emails d’attribution de bus à tous les utilisateurs. -

        -
        - + > + Envoyer les emails + +
        -
        - -
        - - {/* Section import CSV */} -
        -

        - 📥 Importer un fichier CSV -

        -

        - Chargez un fichier CSV contenant les attributions de bus. -

        - -
        - + + {/* Section import CSV */} +
        +

        + 📥 Importer un fichier CSV +

        +

        + Chargez un fichier CSV contenant les attributions de bus. +

        + +
        + + /> - -
        - - {message && ( -

        + Importer CSV + +

        + + {message && ( +

        - {message} -

        - )} -
        - -
        - - {/* Exemple CSV */} -
        -

        - 📄 Exemple de fichier CSV : -

        -
        -{`user_id,bus,departure_time
        +                }`}
        +            >
        +              {message}
        +            

        + )} +
        + +
        + + {/* Exemple CSV */} + + + + 📄 Exemple de fichier CSV : + + + +
        +              {`user_id,bus,departure_time
         1,2,11h
         2,3,13h
         3,1,11h`}
        -        
        -

        - Le fichier doit être encodé en UTF-8 et utiliser une virgule comme séparateur. -

        -
        -
      + +

      + Le fichier doit être encodé en UTF-8 et utiliser une virgule comme séparateur. +

      + +
      + +
      ); }; diff --git a/frontend/src/components/Admin/adminEmail.tsx b/frontend/src/components/Admin/adminEmail.tsx index 3b2bab3..083f85b 100644 --- a/frontend/src/components/Admin/adminEmail.tsx +++ b/frontend/src/components/Admin/adminEmail.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from 'react'; import Select from 'react-select'; import { emailPreview, sendEmail } from '../../services/requests/email.service'; -import { Card } from '../ui/card'; +import { Card, CardContent, CardHeader, CardTitle } from "../ui/card"; import { Button } from '../ui/button'; import { Input } from '../ui/input'; import { User } from '../../interfaces/user.interface'; @@ -57,28 +57,28 @@ export const AdminEmail = () => { } }; -const handleSend = async () => { - // On mappe toujours pour avoir un tableau de string + const handleSend = async () => { + // On mappe toujours pour avoir un tableau de string - const emails = sendTo.map((u) => u.value); + const emails = sendTo.map((u) => u.value); - const payload = { - subject, - templateName: isCustom ? 'custom' : templateName, - format, - permission, - sendTo: permission ? null : emails, - html: isCustom ? customContent : undefined, - }; + const payload = { + subject, + templateName: isCustom ? 'custom' : templateName, + format, + permission, + sendTo: permission ? null : emails, + html: isCustom ? customContent : undefined, + }; - const res = await sendEmail(payload); - Swal.fire({ - icon: 'success', - title: 'Email envoyé', - text: res.message, - }); -}; + const res = await sendEmail(payload); + Swal.fire({ + icon: 'success', + title: 'Email envoyé', + text: res.message, + }); + }; const confirmSend = async () => { const result = await Swal.fire({ @@ -100,55 +100,61 @@ const handleSend = async () => { return ( - -

      📬 Envoi d'e-mail

      - setSubject(e.target.value)} /> -
      - { - setIsCustom(e.target.checked); - if (e.target.checked) setTemplateName(''); - }} - /> - -
      - {!isCustom ? ( + + + + 📬 Envoi d'e-mail + + + + setSubject(e.target.value)} /> +
      + { + setIsCustom(e.target.checked); + if (e.target.checked) setTemplateName(''); + }} + /> + +
      + {!isCustom ? ( +