From f5dab3cb9506817b9efb9aa5de86f05dc3ca8b39 Mon Sep 17 00:00:00 2001 From: Julien Froidefond Date: Thu, 18 Dec 2025 08:50:14 +0100 Subject: [PATCH] Refactor HousesPage and HouseManagement components: Introduce TypeScript types for house and invitation data structures to enhance type safety. Update data serialization logic for improved clarity and maintainability. Refactor UI components for better readability and consistency, including adjustments to conditional rendering and styling in HouseManagement. Optimize fetch logic in HousesSection with useCallback for performance improvements. --- app/houses/page.tsx | 44 +++++- components/houses/HouseManagement.tsx | 175 +++++++++++++++-------- components/houses/HousesSection.tsx | 43 ++++-- services/challenges/challenge.service.ts | 18 ++- 4 files changed, 195 insertions(+), 85 deletions(-) diff --git a/app/houses/page.tsx b/app/houses/page.tsx index 9e1d464..c33f898 100644 --- a/app/houses/page.tsx +++ b/app/houses/page.tsx @@ -5,9 +5,41 @@ import NavigationWrapper from "@/components/navigation/NavigationWrapper"; import HousesSection from "@/components/houses/HousesSection"; import { houseService } from "@/services/houses/house.service"; import { prisma } from "@/services/database"; +import type { House, HouseMembership, HouseInvitation } from "@/prisma/generated/prisma/client"; export const dynamic = "force-dynamic"; +// Types pour les données sérialisées +type HouseWithRelations = House & { + creator?: { + id: string; + username: string; + avatar: string | null; + } | null; + creatorId?: string; + memberships?: Array; +}; + +type InvitationWithRelations = HouseInvitation & { + house: { + id: string; + name: string; + }; + inviter: { + id: string; + username: string; + avatar: string | null; + }; +}; + export default async function HousesPage() { const session = await auth(); @@ -90,12 +122,12 @@ export default async function HousesPage() { ]); // Sérialiser les données pour le client - const houses = (housesData as any[]).map((house: any) => ({ + const houses = (housesData as HouseWithRelations[]).map((house: HouseWithRelations) => ({ id: house.id, name: house.name, description: house.description, - creator: house.creator || { id: house.creatorId, username: "Unknown", avatar: null }, - memberships: (house.memberships || []).map((m: any) => ({ + creator: house.creator || { id: house.creatorId || "", username: "Unknown", avatar: null }, + memberships: (house.memberships || []).map((m) => ({ id: m.id, role: m.role, user: { @@ -113,8 +145,8 @@ export default async function HousesPage() { id: myHouseData.id, name: myHouseData.name, description: myHouseData.description, - creator: (myHouseData as any).creator || { id: (myHouseData as any).creatorId, username: "Unknown", avatar: null }, - memberships: ((myHouseData as any).memberships || []).map((m: any) => ({ + creator: (myHouseData as HouseWithRelations).creator || { id: (myHouseData as HouseWithRelations).creatorId || "", username: "Unknown", avatar: null }, + memberships: ((myHouseData as HouseWithRelations).memberships || []).map((m) => ({ id: m.id, role: m.role, user: { @@ -128,7 +160,7 @@ export default async function HousesPage() { } : null; - const invitations = invitationsData.map((inv: any) => ({ + const invitations = (invitationsData as InvitationWithRelations[]).map((inv: InvitationWithRelations) => ({ id: inv.id, house: { id: inv.house.id, diff --git a/components/houses/HouseManagement.tsx b/components/houses/HouseManagement.tsx index 5a9be41..1511634 100644 --- a/components/houses/HouseManagement.tsx +++ b/components/houses/HouseManagement.tsx @@ -91,7 +91,9 @@ export default function HouseManagement({ const fetchRequests = async () => { if (!house || !isAdmin) return; try { - const response = await fetch(`/api/houses/${house.id}/requests?status=PENDING`); + const response = await fetch( + `/api/houses/${house.id}/requests?status=PENDING` + ); if (response.ok) { const data = await response.json(); setRequests(data); @@ -101,10 +103,13 @@ export default function HouseManagement({ } }; fetchRequests(); - }, [house?.id, isAdmin]); + }, [house, isAdmin]); const handleDelete = () => { - if (!house || !confirm("Êtes-vous sûr de vouloir supprimer cette maison ?")) { + if ( + !house || + !confirm("Êtes-vous sûr de vouloir supprimer cette maison ?") + ) { return; } @@ -168,11 +173,17 @@ export default function HouseManagement({ if (!house) { return ( -

+

Ma Maison

-

- Vous n'êtes membre d'aucune maison pour le moment. +

+ Vous n'êtes membre d'aucune maison pour le moment.

); @@ -180,27 +191,30 @@ export default function HouseManagement({ return (
-
-

{house.name}

{house.description && ( -

+

{house.description}

)} @@ -217,22 +231,40 @@ export default function HouseManagement({ {isEditing ? "Annuler" : "Modifier"} {isOwner && ( - )} )} {!isOwner && ( - )}
- {error && {error}} - {success && {success}} + {error && ( + + {error} + + )} + {success && ( + + {success} + + )} {isEditing ? ( ) : (
-

Membres ({house.memberships?.length ?? 0}) @@ -258,21 +290,25 @@ export default function HouseManagement({
{(house.memberships || []).map((membership) => { const isCurrentUser = membership.user.id === session?.user?.id; - const roleColor = - membership.role === "OWNER" ? "var(--accent)" : - membership.role === "ADMIN" ? "var(--primary)" : - "var(--muted-foreground)"; - + const roleColor = + membership.role === "OWNER" + ? "var(--accent)" + : membership.role === "ADMIN" + ? "var(--primary)" + : "var(--muted-foreground)"; + return (
@@ -285,16 +321,18 @@ export default function HouseManagement({ /> )}
- {membership.user.username} {isCurrentUser && " (Vous)"} - @@ -309,43 +347,55 @@ export default function HouseManagement({
- {membership.role === "OWNER" && "👑 "} {membership.role} - {isAdmin && - !isCurrentUser && + {isAdmin && + !isCurrentUser && (isOwner || membership.role === "MEMBER") && membership.role !== "OWNER" && ( - - )} + + )}
); @@ -411,15 +461,15 @@ export default function HouseManagement({ {isAdmin && pendingRequests.length > 0 && ( -

- Demandes d'adhésion + Demandes d'adhésion

@@ -427,4 +477,3 @@ export default function HouseManagement({
); } - diff --git a/components/houses/HousesSection.tsx b/components/houses/HousesSection.tsx index 8a0363e..7a6b6d4 100644 --- a/components/houses/HousesSection.tsx +++ b/components/houses/HousesSection.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState, useEffect } from "react"; +import { useState, useEffect, useCallback } from "react"; import { useSession } from "next-auth/react"; import Card from "@/components/ui/Card"; import Button from "@/components/ui/Button"; @@ -78,7 +78,7 @@ export default function HousesSection({ const [showCreateForm, setShowCreateForm] = useState(false); const [searchTerm, setSearchTerm] = useState(""); - const fetchHouses = async () => { + const fetchHouses = useCallback(async () => { try { const params = new URLSearchParams(); if (searchTerm) { @@ -94,7 +94,7 @@ export default function HousesSection({ } catch (error) { console.error("Error fetching houses:", error); } - }; + }, [searchTerm]); const fetchMyHouse = async () => { try { @@ -129,9 +129,13 @@ export default function HousesSection({ }, 300); return () => clearTimeout(timeout); } else { - fetchHouses(); + // Utiliser un timeout pour éviter setState synchrone dans effect + const timeout = setTimeout(() => { + fetchHouses(); + }, 0); + return () => clearTimeout(timeout); } - }, [searchTerm]); + }, [searchTerm, fetchHouses]); const handleUpdate = () => { fetchMyHouse(); @@ -165,15 +169,24 @@ export default function HousesSection({ <> {invitations.length > 0 && ( -

+

Mes Invitations

- +
)} -

+

Ma Maison

{myHouse ? ( @@ -194,8 +207,12 @@ export default function HousesSection({ /> ) : (
-

- Vous n'êtes membre d'aucune maison. Créez-en une ou demandez à rejoindre une maison existante. +

+ Vous n'êtes membre d'aucune maison. Créez-en + une ou demandez à rejoindre une maison existante.