From cb2e8e9c9fd7ef729237e7305c6ce7cb27148c0a Mon Sep 17 00:00:00 2001 From: Julien Froidefond Date: Mon, 15 Sep 2025 21:23:03 +0200 Subject: [PATCH] feat: enhance useDaily hook and DailyPageClient for improved data handling - Added `refreshDailySilent` method to `useDaily` for silent data refresh without loading state. - Updated `useDaily` to accept an optional `initialDailyView` parameter, improving initial state management. - Modified `DailyPageClient` to utilize `refreshDailySilent` for smoother user experience during checkbox updates. - Implemented server-side data fetching in `DailyPage` for better initial load performance. - Enhanced UI to indicate refreshing state in `DailySectionComponent`. --- hooks/useDaily.ts | 42 +++++++++++++++--- src/app/daily/DailyPageClient.tsx | 71 ++++++++++++++++++++++--------- src/app/daily/page.tsx | 25 ++++++++++- 3 files changed, 111 insertions(+), 27 deletions(-) diff --git a/hooks/useDaily.ts b/hooks/useDaily.ts index 1f4fdab..ee73cbe 100644 --- a/hooks/useDaily.ts +++ b/hooks/useDaily.ts @@ -7,12 +7,14 @@ import { DailyView, DailyCheckbox, UpdateDailyCheckboxData } from '@/lib/types'; interface UseDailyState { dailyView: DailyView | null; loading: boolean; + refreshing: boolean; // Pour les refresh silencieux error: string | null; saving: boolean; // Pour indiquer les opérations en cours } interface UseDailyActions { refreshDaily: () => Promise; + refreshDailySilent: () => Promise; addTodayCheckbox: (text: string, taskId?: string) => Promise; addYesterdayCheckbox: (text: string, taskId?: string) => Promise; updateCheckbox: (checkboxId: string, data: UpdateDailyCheckboxData) => Promise; @@ -28,10 +30,11 @@ interface UseDailyActions { /** * Hook pour la gestion d'une vue daily spécifique */ -export function useDaily(initialDate?: Date): UseDailyState & UseDailyActions & { currentDate: Date } { +export function useDaily(initialDate?: Date, initialDailyView?: DailyView): UseDailyState & UseDailyActions & { currentDate: Date } { const [currentDate, setCurrentDate] = useState(initialDate || new Date()); - const [dailyView, setDailyView] = useState(null); - const [loading, setLoading] = useState(true); + const [dailyView, setDailyView] = useState(initialDailyView || null); + const [loading, setLoading] = useState(!initialDailyView); // Pas de loading si on a des données SSR + const [refreshing, setRefreshing] = useState(false); // Pour les refresh silencieux const [error, setError] = useState(null); const [saving, setSaving] = useState(false); @@ -50,6 +53,20 @@ export function useDaily(initialDate?: Date): UseDailyState & UseDailyActions & } }, [currentDate]); + const refreshDailySilent = useCallback(async () => { + try { + setRefreshing(true); + // Refresh silencieux sans setLoading(true) pour éviter le clignotement + const view = await dailyClient.getDailyView(currentDate); + setDailyView(view); + } catch (err) { + console.error('Erreur refreshDailySilent:', err); + // On n'affiche pas l'erreur pour ne pas perturber l'UX + } finally { + setRefreshing(false); + } + }, [currentDate]); + const addTodayCheckbox = useCallback(async (text: string, taskId?: string): Promise => { if (!dailyView) return null; @@ -200,21 +217,36 @@ export function useDaily(initialDate?: Date): UseDailyState & UseDailyActions & setCurrentDate(date); }, []); + // État pour savoir si c'est le premier chargement + const [isInitialLoad, setIsInitialLoad] = useState(!initialDailyView); + // Charger le daily quand la date change useEffect(() => { - refreshDaily(); - }, [refreshDaily]); + if (isInitialLoad) { + // Premier chargement : utiliser refreshDaily normal seulement si pas de données SSR + if (!initialDailyView) { + refreshDaily().finally(() => setIsInitialLoad(false)); + } else { + setIsInitialLoad(false); + } + } else { + // Changements suivants : utiliser refreshDailySilent + refreshDailySilent(); + } + }, [refreshDaily, refreshDailySilent, isInitialLoad, initialDailyView]); return { // State dailyView, loading, + refreshing, error, saving, currentDate, // Actions refreshDaily, + refreshDailySilent, addTodayCheckbox, addYesterdayCheckbox, updateCheckbox, diff --git a/src/app/daily/DailyPageClient.tsx b/src/app/daily/DailyPageClient.tsx index 4c69bde..98bfe25 100644 --- a/src/app/daily/DailyPageClient.tsx +++ b/src/app/daily/DailyPageClient.tsx @@ -3,12 +3,13 @@ import { useState, useRef, useEffect } from 'react'; import React from 'react'; import { useDaily } from '@/hooks/useDaily'; -import { DailyCheckbox } from '@/lib/types'; +import { DailyCheckbox, DailyView } from '@/lib/types'; import { Button } from '@/components/ui/Button'; import { Input } from '@/components/ui/Input'; import { Card } from '@/components/ui/Card'; import Link from 'next/link'; import { DailyCalendar } from '@/components/daily/DailyCalendar'; +import { dailyClient } from '@/clients/daily-client'; interface DailySectionProps { title: string; @@ -19,6 +20,7 @@ interface DailySectionProps { onUpdateCheckbox: (checkboxId: string, text: string) => Promise; onDeleteCheckbox: (checkboxId: string) => Promise; saving: boolean; + refreshing?: boolean; } function DailySectionComponent({ @@ -29,7 +31,8 @@ function DailySectionComponent({ onToggleCheckbox, onUpdateCheckbox, onDeleteCheckbox, - saving + saving, + refreshing = false }: DailySectionProps) { const [newCheckboxText, setNewCheckboxText] = useState(''); const [addingCheckbox, setAddingCheckbox] = useState(false); @@ -103,8 +106,11 @@ function DailySectionComponent({ return (
-

+

{title} ({formatShortDate(date)}) + {refreshing && ( +
+ )}

{checkboxes.filter(cb => cb.isChecked).length}/{checkboxes.length} @@ -204,14 +210,25 @@ function DailySectionComponent({ ); } -export function DailyPageClient() { +interface DailyPageClientProps { + initialDailyView?: DailyView; + initialDailyDates?: string[]; + initialDate?: Date; +} + +export function DailyPageClient({ + initialDailyView, + initialDailyDates = [], + initialDate +}: DailyPageClientProps = {}) { const { dailyView, loading, + refreshing, error, saving, currentDate, - refreshDaily, + refreshDailySilent, toggleCheckbox, updateCheckbox, deleteCheckbox, @@ -219,16 +236,28 @@ export function DailyPageClient() { goToNextDay, goToToday, setDate - } = useDaily(); + } = useDaily(initialDate, initialDailyView); - const [dailyDates, setDailyDates] = useState([]); + const [dailyDates, setDailyDates] = useState(initialDailyDates); - // Charger les dates avec des dailies pour le calendrier + // Fonction pour rafraîchir la liste des dates avec des dailies + const refreshDailyDates = async () => { + try { + const dates = await dailyClient.getDailyDates(); + setDailyDates(dates); + } catch (error) { + console.error('Erreur lors du refresh des dates:', error); + } + }; + + // Charger les dates avec des dailies pour le calendrier (seulement si pas de données SSR) useEffect(() => { - import('@/clients/daily-client').then(({ dailyClient }) => { - return dailyClient.getDailyDates(); - }).then(setDailyDates).catch(console.error); - }, []); + if (initialDailyDates.length === 0) { + import('@/clients/daily-client').then(({ dailyClient }) => { + return dailyClient.getDailyDates(); + }).then(setDailyDates).catch(console.error); + } + }, [initialDailyDates.length]); const handleAddTodayCheckbox = async (text: string) => { try { @@ -238,11 +267,10 @@ export function DailyPageClient() { text, isChecked: false }); - // Recharger la vue daily après ajout - await refreshDaily(); + // Recharger silencieusement la vue daily (sans clignotement) + refreshDailySilent().catch(console.error); // Recharger aussi les dates pour le calendrier - const updatedDates = await dailyClient.getDailyDates(); - setDailyDates(updatedDates); + await refreshDailyDates(); } catch (error) { console.error('Erreur lors de l\'ajout de la tâche:', error); } @@ -259,11 +287,10 @@ export function DailyPageClient() { text, isChecked: false }); - // Recharger la vue daily après ajout - await refreshDaily(); + // Recharger silencieusement la vue daily (sans clignotement) + refreshDailySilent().catch(console.error); // Recharger aussi les dates pour le calendrier - const updatedDates = await dailyClient.getDailyDates(); - setDailyDates(updatedDates); + await refreshDailyDates(); } catch (error) { console.error('Erreur lors de l\'ajout de la tâche:', error); } @@ -275,6 +302,8 @@ export function DailyPageClient() { const handleDeleteCheckbox = async (checkboxId: string) => { await deleteCheckbox(checkboxId); + // Refresh dates après suppression pour mettre à jour le calendrier + await refreshDailyDates(); }; const handleUpdateCheckbox = async (checkboxId: string, text: string) => { @@ -416,6 +445,7 @@ export function DailyPageClient() { onUpdateCheckbox={handleUpdateCheckbox} onDeleteCheckbox={handleDeleteCheckbox} saving={saving} + refreshing={refreshing} /> {/* Section Aujourd'hui */} @@ -428,6 +458,7 @@ export function DailyPageClient() { onUpdateCheckbox={handleUpdateCheckbox} onDeleteCheckbox={handleDeleteCheckbox} saving={saving} + refreshing={refreshing} />
)} diff --git a/src/app/daily/page.tsx b/src/app/daily/page.tsx index f778794..fc31b5f 100644 --- a/src/app/daily/page.tsx +++ b/src/app/daily/page.tsx @@ -1,11 +1,32 @@ import { Metadata } from 'next'; import { DailyPageClient } from './DailyPageClient'; +import { dailyService } from '@/services/daily'; export const metadata: Metadata = { title: 'Daily - Tower Control', description: 'Gestion quotidienne des tâches et objectifs', }; -export default function DailyPage() { - return ; +export default async function DailyPage() { + // Récupérer les données côté serveur + const today = new Date(); + + try { + const [dailyView, dailyDates] = await Promise.all([ + dailyService.getDailyView(today), + dailyService.getDailyDates() + ]); + + return ( + + ); + } catch (error) { + console.error('Erreur SSR Daily:', error); + // Fallback vers client-side rendering + return ; + } }