- Marked tasks as completed in TODO for Daily management service, data model, and interactive checkboxes. - Added a new link to the Daily page in the Header component for easy navigation. - Introduced DailyCheckbox model in Prisma schema and corresponding TypeScript interfaces for better data handling. - Updated database schema to include daily checkboxes, enhancing task management capabilities.
290 lines
8.8 KiB
TypeScript
290 lines
8.8 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect, useCallback } from 'react';
|
|
import { dailyClient, DailyHistoryFilters, DailySearchFilters, ReorderCheckboxesData } from '@/clients/daily-client';
|
|
import { DailyView, DailyCheckbox, UpdateDailyCheckboxData } from '@/lib/types';
|
|
|
|
interface UseDailyState {
|
|
dailyView: DailyView | null;
|
|
loading: boolean;
|
|
error: string | null;
|
|
saving: boolean; // Pour indiquer les opérations en cours
|
|
}
|
|
|
|
interface UseDailyActions {
|
|
refreshDaily: () => Promise<void>;
|
|
addTodayCheckbox: (text: string, taskId?: string) => Promise<DailyCheckbox | null>;
|
|
addYesterdayCheckbox: (text: string, taskId?: string) => Promise<DailyCheckbox | null>;
|
|
updateCheckbox: (checkboxId: string, data: UpdateDailyCheckboxData) => Promise<DailyCheckbox | null>;
|
|
deleteCheckbox: (checkboxId: string) => Promise<void>;
|
|
toggleCheckbox: (checkboxId: string) => Promise<void>;
|
|
reorderCheckboxes: (data: ReorderCheckboxesData) => Promise<void>;
|
|
goToPreviousDay: () => Promise<void>;
|
|
goToNextDay: () => Promise<void>;
|
|
goToToday: () => Promise<void>;
|
|
setDate: (date: Date) => Promise<void>;
|
|
}
|
|
|
|
/**
|
|
* Hook pour la gestion d'une vue daily spécifique
|
|
*/
|
|
export function useDaily(initialDate?: Date): UseDailyState & UseDailyActions & { currentDate: Date } {
|
|
const [currentDate, setCurrentDate] = useState<Date>(initialDate || new Date());
|
|
const [dailyView, setDailyView] = useState<DailyView | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [saving, setSaving] = useState(false);
|
|
|
|
const refreshDaily = useCallback(async () => {
|
|
try {
|
|
setLoading(true);
|
|
setError(null);
|
|
|
|
const view = await dailyClient.getDailyView(currentDate);
|
|
setDailyView(view);
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'Erreur lors du chargement du daily');
|
|
console.error('Erreur refreshDaily:', err);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, [currentDate]);
|
|
|
|
const addTodayCheckbox = useCallback(async (text: string, taskId?: string): Promise<DailyCheckbox | null> => {
|
|
if (!dailyView) return null;
|
|
|
|
try {
|
|
setSaving(true);
|
|
setError(null);
|
|
|
|
const newCheckbox = await dailyClient.addTodayCheckbox(text, taskId);
|
|
|
|
// Mise à jour optimiste
|
|
setDailyView(prev => prev ? {
|
|
...prev,
|
|
today: [...prev.today, newCheckbox].sort((a, b) => a.order - b.order)
|
|
} : null);
|
|
|
|
return newCheckbox;
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'Erreur lors de l\'ajout de la checkbox');
|
|
console.error('Erreur addTodayCheckbox:', err);
|
|
return null;
|
|
} finally {
|
|
setSaving(false);
|
|
}
|
|
}, [dailyView]);
|
|
|
|
const addYesterdayCheckbox = useCallback(async (text: string, taskId?: string): Promise<DailyCheckbox | null> => {
|
|
if (!dailyView) return null;
|
|
|
|
try {
|
|
setSaving(true);
|
|
setError(null);
|
|
|
|
const newCheckbox = await dailyClient.addYesterdayCheckbox(text, taskId);
|
|
|
|
// Mise à jour optimiste
|
|
setDailyView(prev => prev ? {
|
|
...prev,
|
|
yesterday: [...prev.yesterday, newCheckbox].sort((a, b) => a.order - b.order)
|
|
} : null);
|
|
|
|
return newCheckbox;
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'Erreur lors de l\'ajout de la checkbox');
|
|
console.error('Erreur addYesterdayCheckbox:', err);
|
|
return null;
|
|
} finally {
|
|
setSaving(false);
|
|
}
|
|
}, [dailyView]);
|
|
|
|
const updateCheckbox = useCallback(async (checkboxId: string, data: UpdateDailyCheckboxData): Promise<DailyCheckbox | null> => {
|
|
if (!dailyView) return null;
|
|
|
|
try {
|
|
setSaving(true);
|
|
setError(null);
|
|
|
|
const updatedCheckbox = await dailyClient.updateCheckbox(checkboxId, data);
|
|
|
|
// Mise à jour optimiste
|
|
setDailyView(prev => prev ? {
|
|
...prev,
|
|
yesterday: prev.yesterday.map(cb => cb.id === checkboxId ? updatedCheckbox : cb),
|
|
today: prev.today.map(cb => cb.id === checkboxId ? updatedCheckbox : cb)
|
|
} : null);
|
|
|
|
return updatedCheckbox;
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'Erreur lors de la mise à jour de la checkbox');
|
|
console.error('Erreur updateCheckbox:', err);
|
|
return null;
|
|
} finally {
|
|
setSaving(false);
|
|
}
|
|
}, [dailyView]);
|
|
|
|
const deleteCheckbox = useCallback(async (checkboxId: string): Promise<void> => {
|
|
if (!dailyView) return;
|
|
|
|
try {
|
|
setSaving(true);
|
|
setError(null);
|
|
|
|
await dailyClient.deleteCheckbox(checkboxId);
|
|
|
|
// Mise à jour optimiste
|
|
setDailyView(prev => prev ? {
|
|
...prev,
|
|
yesterday: prev.yesterday.filter(cb => cb.id !== checkboxId),
|
|
today: prev.today.filter(cb => cb.id !== checkboxId)
|
|
} : null);
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'Erreur lors de la suppression de la checkbox');
|
|
console.error('Erreur deleteCheckbox:', err);
|
|
} finally {
|
|
setSaving(false);
|
|
}
|
|
}, [dailyView]);
|
|
|
|
const toggleCheckbox = useCallback(async (checkboxId: string): Promise<void> => {
|
|
if (!dailyView) return;
|
|
|
|
// Trouver la checkbox dans yesterday ou today
|
|
let checkbox = dailyView.yesterday.find(cb => cb.id === checkboxId);
|
|
if (!checkbox) {
|
|
checkbox = dailyView.today.find(cb => cb.id === checkboxId);
|
|
}
|
|
|
|
if (!checkbox) return;
|
|
|
|
await updateCheckbox(checkboxId, { isChecked: !checkbox.isChecked });
|
|
}, [dailyView, updateCheckbox]);
|
|
|
|
const reorderCheckboxes = useCallback(async (data: ReorderCheckboxesData): Promise<void> => {
|
|
try {
|
|
setSaving(true);
|
|
setError(null);
|
|
|
|
await dailyClient.reorderCheckboxes(data);
|
|
|
|
// Rafraîchir pour obtenir l'ordre correct
|
|
await refreshDaily();
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'Erreur lors du réordonnancement');
|
|
console.error('Erreur reorderCheckboxes:', err);
|
|
} finally {
|
|
setSaving(false);
|
|
}
|
|
}, [refreshDaily]);
|
|
|
|
const goToPreviousDay = useCallback(async (): Promise<void> => {
|
|
const previousDay = new Date(currentDate);
|
|
previousDay.setDate(previousDay.getDate() - 1);
|
|
setCurrentDate(previousDay);
|
|
}, [currentDate]);
|
|
|
|
const goToNextDay = useCallback(async (): Promise<void> => {
|
|
const nextDay = new Date(currentDate);
|
|
nextDay.setDate(nextDay.getDate() + 1);
|
|
setCurrentDate(nextDay);
|
|
}, [currentDate]);
|
|
|
|
const goToToday = useCallback(async (): Promise<void> => {
|
|
setCurrentDate(new Date());
|
|
}, []);
|
|
|
|
const setDate = useCallback(async (date: Date): Promise<void> => {
|
|
setCurrentDate(date);
|
|
}, []);
|
|
|
|
// Charger le daily quand la date change
|
|
useEffect(() => {
|
|
refreshDaily();
|
|
}, [refreshDaily]);
|
|
|
|
return {
|
|
// State
|
|
dailyView,
|
|
loading,
|
|
error,
|
|
saving,
|
|
currentDate,
|
|
|
|
// Actions
|
|
refreshDaily,
|
|
addTodayCheckbox,
|
|
addYesterdayCheckbox,
|
|
updateCheckbox,
|
|
deleteCheckbox,
|
|
toggleCheckbox,
|
|
reorderCheckboxes,
|
|
goToPreviousDay,
|
|
goToNextDay,
|
|
goToToday,
|
|
setDate
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Hook pour l'historique des checkboxes
|
|
*/
|
|
export function useDailyHistory() {
|
|
const [history, setHistory] = useState<{ date: Date; checkboxes: DailyCheckbox[] }[]>([]);
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const loadHistory = useCallback(async (filters?: DailyHistoryFilters) => {
|
|
try {
|
|
setLoading(true);
|
|
setError(null);
|
|
|
|
const historyData = await dailyClient.getCheckboxHistory(filters);
|
|
setHistory(historyData);
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'Erreur lors du chargement de l\'historique');
|
|
console.error('Erreur loadHistory:', err);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, []);
|
|
|
|
const searchCheckboxes = useCallback(async (filters: DailySearchFilters) => {
|
|
try {
|
|
setLoading(true);
|
|
setError(null);
|
|
|
|
const checkboxes = await dailyClient.searchCheckboxes(filters);
|
|
// Grouper par date pour l'affichage
|
|
const groupedHistory = checkboxes.reduce((acc, checkbox) => {
|
|
const dateKey = checkbox.date.toDateString();
|
|
const existing = acc.find(item => item.date.toDateString() === dateKey);
|
|
|
|
if (existing) {
|
|
existing.checkboxes.push(checkbox);
|
|
} else {
|
|
acc.push({ date: checkbox.date, checkboxes: [checkbox] });
|
|
}
|
|
|
|
return acc;
|
|
}, [] as { date: Date; checkboxes: DailyCheckbox[] }[]);
|
|
|
|
setHistory(groupedHistory);
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'Erreur lors de la recherche');
|
|
console.error('Erreur searchCheckboxes:', err);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, []);
|
|
|
|
return {
|
|
history,
|
|
loading,
|
|
error,
|
|
loadHistory,
|
|
searchCheckboxes
|
|
};
|
|
} |