feat: enhance DailyClient and useDaily hook for improved checkbox handling
- Added new API response types (`ApiCheckbox`, `ApiDailyView`, `ApiHistoryItem`) for better type safety. - Updated `getTodaysDailyView`, `getDailyView`, and `getHistory` methods to utilize new types and transform date strings into Date objects. - Refactored `addCheckbox` and `updateCheckbox` methods to handle checkbox creation and updates with improved error handling. - Optimized `DailyAddForm` for better UX by removing unnecessary loading states and implementing optimistic UI updates. - Enhanced `useDaily` hook to support checkbox type management and rollback on errors during updates. - Updated `DailyPageClient` to leverage new checkbox handling methods for adding tasks.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { dailyClient, DailyHistoryFilters, DailySearchFilters, ReorderCheckboxesData } from '@/clients/daily-client';
|
||||
import { DailyView, DailyCheckbox, UpdateDailyCheckboxData } from '@/lib/types';
|
||||
import { DailyView, DailyCheckbox, UpdateDailyCheckboxData, DailyCheckboxType } from '@/lib/types';
|
||||
|
||||
interface UseDailyState {
|
||||
dailyView: DailyView | null;
|
||||
@@ -15,8 +15,8 @@ interface UseDailyState {
|
||||
interface UseDailyActions {
|
||||
refreshDaily: () => Promise<void>;
|
||||
refreshDailySilent: () => Promise<void>;
|
||||
addTodayCheckbox: (text: string, taskId?: string) => Promise<DailyCheckbox | null>;
|
||||
addYesterdayCheckbox: (text: string, taskId?: string) => Promise<DailyCheckbox | null>;
|
||||
addTodayCheckbox: (text: string, type?: DailyCheckboxType, taskId?: string) => Promise<DailyCheckbox | null>;
|
||||
addYesterdayCheckbox: (text: string, type?: DailyCheckboxType, taskId?: string) => Promise<DailyCheckbox | null>;
|
||||
updateCheckbox: (checkboxId: string, data: UpdateDailyCheckboxData) => Promise<DailyCheckbox | null>;
|
||||
deleteCheckbox: (checkboxId: string) => Promise<void>;
|
||||
toggleCheckbox: (checkboxId: string) => Promise<void>;
|
||||
@@ -67,66 +67,141 @@ export function useDaily(initialDate?: Date, initialDailyView?: DailyView): UseD
|
||||
}
|
||||
}, [currentDate]);
|
||||
|
||||
const addTodayCheckbox = useCallback(async (text: string, taskId?: string): Promise<DailyCheckbox | null> => {
|
||||
const addTodayCheckbox = useCallback(async (text: string, type?: DailyCheckboxType, taskId?: string): Promise<DailyCheckbox | null> => {
|
||||
if (!dailyView) return null;
|
||||
|
||||
// Créer une checkbox temporaire pour l'affichage optimiste
|
||||
const tempCheckbox: DailyCheckbox = {
|
||||
id: `temp-${Date.now()}`, // ID temporaire
|
||||
date: currentDate,
|
||||
text,
|
||||
isChecked: false,
|
||||
type: type || 'task', // Utilise le type fourni ou 'task' par défaut
|
||||
order: dailyView.today.length, // Ordre temporaire
|
||||
taskId,
|
||||
task: undefined,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date()
|
||||
};
|
||||
|
||||
const previousDailyView = dailyView;
|
||||
|
||||
// Mise à jour optimiste IMMÉDIATE
|
||||
setDailyView(prev => prev ? {
|
||||
...prev,
|
||||
today: [...prev.today, tempCheckbox]
|
||||
} : null);
|
||||
|
||||
try {
|
||||
setSaving(true);
|
||||
setError(null);
|
||||
// Appel API en arrière-plan
|
||||
const newCheckbox = await dailyClient.addCheckbox({
|
||||
date: currentDate,
|
||||
text,
|
||||
type: type || 'task',
|
||||
taskId
|
||||
});
|
||||
|
||||
const newCheckbox = await dailyClient.addTodayCheckbox(text, taskId);
|
||||
|
||||
// Mise à jour optimiste
|
||||
// Remplacer la checkbox temporaire par la vraie
|
||||
setDailyView(prev => prev ? {
|
||||
...prev,
|
||||
today: [...prev.today, newCheckbox].sort((a, b) => a.order - b.order)
|
||||
today: prev.today.map(cb =>
|
||||
cb.id === tempCheckbox.id ? newCheckbox : cb
|
||||
).sort((a, b) => a.order - b.order)
|
||||
} : null);
|
||||
|
||||
return newCheckbox;
|
||||
} catch (err) {
|
||||
// Rollback en cas d'erreur
|
||||
setDailyView(previousDailyView);
|
||||
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]);
|
||||
}, [dailyView, currentDate]);
|
||||
|
||||
const addYesterdayCheckbox = useCallback(async (text: string, taskId?: string): Promise<DailyCheckbox | null> => {
|
||||
const addYesterdayCheckbox = useCallback(async (text: string, type?: DailyCheckboxType, taskId?: string): Promise<DailyCheckbox | null> => {
|
||||
if (!dailyView) return null;
|
||||
|
||||
// Créer une checkbox temporaire pour l'affichage optimiste
|
||||
const yesterday = new Date(currentDate);
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
|
||||
const tempCheckbox: DailyCheckbox = {
|
||||
id: `temp-${Date.now()}`, // ID temporaire
|
||||
date: yesterday,
|
||||
text,
|
||||
isChecked: false,
|
||||
type: type || 'task', // Utilise le type fourni ou 'task' par défaut
|
||||
order: dailyView.yesterday.length, // Ordre temporaire
|
||||
taskId,
|
||||
task: undefined,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date()
|
||||
};
|
||||
|
||||
const previousDailyView = dailyView;
|
||||
|
||||
// Mise à jour optimiste IMMÉDIATE
|
||||
setDailyView(prev => prev ? {
|
||||
...prev,
|
||||
yesterday: [...prev.yesterday, tempCheckbox]
|
||||
} : null);
|
||||
|
||||
try {
|
||||
setSaving(true);
|
||||
setError(null);
|
||||
// Appel API en arrière-plan
|
||||
const newCheckbox = await dailyClient.addCheckbox({
|
||||
date: yesterday,
|
||||
text,
|
||||
type: type || 'task',
|
||||
taskId
|
||||
});
|
||||
|
||||
const newCheckbox = await dailyClient.addYesterdayCheckbox(text, taskId);
|
||||
|
||||
// Mise à jour optimiste
|
||||
// Remplacer la checkbox temporaire par la vraie
|
||||
setDailyView(prev => prev ? {
|
||||
...prev,
|
||||
yesterday: [...prev.yesterday, newCheckbox].sort((a, b) => a.order - b.order)
|
||||
yesterday: prev.yesterday.map(cb =>
|
||||
cb.id === tempCheckbox.id ? newCheckbox : cb
|
||||
).sort((a, b) => a.order - b.order)
|
||||
} : null);
|
||||
|
||||
return newCheckbox;
|
||||
} catch (err) {
|
||||
// Rollback en cas d'erreur
|
||||
setDailyView(previousDailyView);
|
||||
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]);
|
||||
}, [dailyView, currentDate]);
|
||||
|
||||
const updateCheckbox = useCallback(async (checkboxId: string, data: UpdateDailyCheckboxData): Promise<DailyCheckbox | null> => {
|
||||
if (!dailyView) return null;
|
||||
|
||||
// Trouver la checkbox existante
|
||||
let existingCheckbox = dailyView.yesterday.find(cb => cb.id === checkboxId);
|
||||
if (!existingCheckbox) {
|
||||
existingCheckbox = dailyView.today.find(cb => cb.id === checkboxId);
|
||||
}
|
||||
|
||||
if (!existingCheckbox) return null;
|
||||
|
||||
const previousDailyView = dailyView;
|
||||
|
||||
// Créer la checkbox mise à jour pour l'affichage optimiste
|
||||
const optimisticCheckbox = { ...existingCheckbox, ...data, updatedAt: new Date() };
|
||||
|
||||
// Mise à jour optimiste IMMÉDIATE
|
||||
setDailyView(prev => prev ? {
|
||||
...prev,
|
||||
yesterday: prev.yesterday.map(cb => cb.id === checkboxId ? optimisticCheckbox : cb),
|
||||
today: prev.today.map(cb => cb.id === checkboxId ? optimisticCheckbox : cb)
|
||||
} : null);
|
||||
|
||||
try {
|
||||
setSaving(true);
|
||||
setError(null);
|
||||
|
||||
// Appel API en arrière-plan
|
||||
const updatedCheckbox = await dailyClient.updateCheckbox(checkboxId, data);
|
||||
|
||||
// Mise à jour optimiste
|
||||
// Remplacer par la vraie checkbox retournée par l'API
|
||||
setDailyView(prev => prev ? {
|
||||
...prev,
|
||||
yesterday: prev.yesterday.map(cb => cb.id === checkboxId ? updatedCheckbox : cb),
|
||||
@@ -135,34 +210,35 @@ export function useDaily(initialDate?: Date, initialDailyView?: DailyView): UseD
|
||||
|
||||
return updatedCheckbox;
|
||||
} catch (err) {
|
||||
// Rollback en cas d'erreur
|
||||
setDailyView(previousDailyView);
|
||||
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;
|
||||
|
||||
const previousDailyView = dailyView;
|
||||
|
||||
// Mise à jour optimiste IMMÉDIATE - supprimer la checkbox
|
||||
setDailyView(prev => prev ? {
|
||||
...prev,
|
||||
yesterday: prev.yesterday.filter(cb => cb.id !== checkboxId),
|
||||
today: prev.today.filter(cb => cb.id !== checkboxId)
|
||||
} : null);
|
||||
|
||||
try {
|
||||
setSaving(true);
|
||||
setError(null);
|
||||
|
||||
// Appel API en arrière-plan
|
||||
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);
|
||||
// Pas besoin de mise à jour supplémentaire, la suppression est déjà faite
|
||||
} catch (err) {
|
||||
// Rollback en cas d'erreur - restaurer la checkbox
|
||||
setDailyView(previousDailyView);
|
||||
setError(err instanceof Error ? err.message : 'Erreur lors de la suppression de la checkbox');
|
||||
console.error('Erreur deleteCheckbox:', err);
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
}, [dailyView]);
|
||||
|
||||
@@ -177,8 +253,30 @@ export function useDaily(initialDate?: Date, initialDailyView?: DailyView): UseD
|
||||
|
||||
if (!checkbox) return;
|
||||
|
||||
await updateCheckbox(checkboxId, { isChecked: !checkbox.isChecked });
|
||||
}, [dailyView, updateCheckbox]);
|
||||
// Mise à jour optimiste IMMÉDIATE
|
||||
const newCheckedState = !checkbox.isChecked;
|
||||
const previousDailyView = dailyView;
|
||||
|
||||
setDailyView(prev => prev ? {
|
||||
...prev,
|
||||
yesterday: prev.yesterday.map(cb =>
|
||||
cb.id === checkboxId ? { ...cb, isChecked: newCheckedState } : cb
|
||||
),
|
||||
today: prev.today.map(cb =>
|
||||
cb.id === checkboxId ? { ...cb, isChecked: newCheckedState } : cb
|
||||
)
|
||||
} : null);
|
||||
|
||||
try {
|
||||
// Appel API en arrière-plan
|
||||
await dailyClient.updateCheckbox(checkboxId, { isChecked: newCheckedState });
|
||||
} catch (err) {
|
||||
// Rollback en cas d'erreur
|
||||
setDailyView(previousDailyView);
|
||||
setError(err instanceof Error ? err.message : 'Erreur lors de la mise à jour de la checkbox');
|
||||
console.error('Erreur toggleCheckbox:', err);
|
||||
}
|
||||
}, [dailyView]);
|
||||
|
||||
const reorderCheckboxes = useCallback(async (data: ReorderCheckboxesData): Promise<void> => {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user