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:
@@ -1,5 +1,30 @@
|
|||||||
import { httpClient } from './base/http-client';
|
import { httpClient } from './base/http-client';
|
||||||
import { DailyCheckbox, DailyView, CreateDailyCheckboxData, UpdateDailyCheckboxData } from '@/lib/types';
|
import { DailyCheckbox, DailyView, CreateDailyCheckboxData, UpdateDailyCheckboxData, Task } from '@/lib/types';
|
||||||
|
|
||||||
|
// Types pour les réponses API (avec dates en string)
|
||||||
|
interface ApiCheckbox {
|
||||||
|
id: string;
|
||||||
|
date: string;
|
||||||
|
text: string;
|
||||||
|
isChecked: boolean;
|
||||||
|
type: 'task' | 'meeting';
|
||||||
|
order: number;
|
||||||
|
taskId?: string;
|
||||||
|
task?: Task;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ApiDailyView {
|
||||||
|
date: string;
|
||||||
|
yesterday: ApiCheckbox[];
|
||||||
|
today: ApiCheckbox[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ApiHistoryItem {
|
||||||
|
date: string;
|
||||||
|
checkboxes: ApiCheckbox[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface DailyHistoryFilters {
|
export interface DailyHistoryFilters {
|
||||||
limit?: number;
|
limit?: number;
|
||||||
@@ -20,7 +45,8 @@ export class DailyClient {
|
|||||||
* Récupère la vue daily d'aujourd'hui (hier + aujourd'hui)
|
* Récupère la vue daily d'aujourd'hui (hier + aujourd'hui)
|
||||||
*/
|
*/
|
||||||
async getTodaysDailyView(): Promise<DailyView> {
|
async getTodaysDailyView(): Promise<DailyView> {
|
||||||
return httpClient.get('/daily');
|
const result = await httpClient.get<ApiDailyView>('/daily');
|
||||||
|
return this.transformDailyViewDates(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,7 +54,8 @@ export class DailyClient {
|
|||||||
*/
|
*/
|
||||||
async getDailyView(date: Date): Promise<DailyView> {
|
async getDailyView(date: Date): Promise<DailyView> {
|
||||||
const dateStr = this.formatDateForAPI(date);
|
const dateStr = this.formatDateForAPI(date);
|
||||||
return httpClient.get(`/daily?date=${dateStr}`);
|
const result = await httpClient.get<ApiDailyView>(`/daily?date=${dateStr}`);
|
||||||
|
return this.transformDailyViewDates(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,7 +66,11 @@ export class DailyClient {
|
|||||||
|
|
||||||
if (filters?.limit) params.append('limit', filters.limit.toString());
|
if (filters?.limit) params.append('limit', filters.limit.toString());
|
||||||
|
|
||||||
return httpClient.get(`/daily?${params}`);
|
const result = await httpClient.get<ApiHistoryItem[]>(`/daily?${params}`);
|
||||||
|
return result.map(item => ({
|
||||||
|
date: new Date(item.date),
|
||||||
|
checkboxes: item.checkboxes.map((cb: ApiCheckbox) => this.transformCheckboxDates(cb))
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -53,17 +84,26 @@ export class DailyClient {
|
|||||||
|
|
||||||
if (filters.limit) params.append('limit', filters.limit.toString());
|
if (filters.limit) params.append('limit', filters.limit.toString());
|
||||||
|
|
||||||
return httpClient.get(`/daily?${params}`);
|
const result = await httpClient.get<ApiCheckbox[]>(`/daily?${params}`);
|
||||||
|
return result.map((cb: ApiCheckbox) => this.transformCheckboxDates(cb));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ajoute une checkbox
|
* Ajoute une checkbox
|
||||||
*/
|
*/
|
||||||
async addCheckbox(data: CreateDailyCheckboxData): Promise<DailyCheckbox> {
|
async addCheckbox(data: CreateDailyCheckboxData): Promise<DailyCheckbox> {
|
||||||
return httpClient.post('/daily', {
|
const payload = {
|
||||||
...data,
|
...data,
|
||||||
date: this.formatDateForAPI(data.date)
|
date: this.formatDateForAPI(data.date)
|
||||||
});
|
};
|
||||||
|
try {
|
||||||
|
const result = await httpClient.post<ApiCheckbox>('/daily', payload);
|
||||||
|
// Transformer les dates string en objets Date
|
||||||
|
return this.transformCheckboxDates(result);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ DailyClient addCheckbox error:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -95,7 +135,8 @@ export class DailyClient {
|
|||||||
* Met à jour une checkbox
|
* Met à jour une checkbox
|
||||||
*/
|
*/
|
||||||
async updateCheckbox(checkboxId: string, data: UpdateDailyCheckboxData): Promise<DailyCheckbox> {
|
async updateCheckbox(checkboxId: string, data: UpdateDailyCheckboxData): Promise<DailyCheckbox> {
|
||||||
return httpClient.patch(`/daily/checkboxes/${checkboxId}`, data);
|
const result = await httpClient.patch<ApiCheckbox>(`/daily/checkboxes/${checkboxId}`, data);
|
||||||
|
return this.transformCheckboxDates(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -132,6 +173,29 @@ export class DailyClient {
|
|||||||
return `${year}-${month}-${day}`; // YYYY-MM-DD
|
return `${year}-${month}-${day}`; // YYYY-MM-DD
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforme les dates string d'une checkbox en objets Date
|
||||||
|
*/
|
||||||
|
private transformCheckboxDates(checkbox: ApiCheckbox): DailyCheckbox {
|
||||||
|
return {
|
||||||
|
...checkbox,
|
||||||
|
date: new Date(checkbox.date),
|
||||||
|
createdAt: new Date(checkbox.createdAt),
|
||||||
|
updatedAt: new Date(checkbox.updatedAt)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforme les dates string d'une vue daily en objets Date
|
||||||
|
*/
|
||||||
|
private transformDailyViewDates(view: ApiDailyView): DailyView {
|
||||||
|
return {
|
||||||
|
date: new Date(view.date),
|
||||||
|
yesterday: view.yesterday.map((cb: ApiCheckbox) => this.transformCheckboxDates(cb)),
|
||||||
|
today: view.today.map((cb: ApiCheckbox) => this.transformCheckboxDates(cb))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Récupère la vue daily d'une date relative (hier, aujourd'hui, demain)
|
* Récupère la vue daily d'une date relative (hier, aujourd'hui, demain)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -14,25 +14,23 @@ interface DailyAddFormProps {
|
|||||||
export function DailyAddForm({ onAdd, disabled = false, placeholder = "Ajouter une tâche..." }: DailyAddFormProps) {
|
export function DailyAddForm({ onAdd, disabled = false, placeholder = "Ajouter une tâche..." }: DailyAddFormProps) {
|
||||||
const [newCheckboxText, setNewCheckboxText] = useState('');
|
const [newCheckboxText, setNewCheckboxText] = useState('');
|
||||||
const [selectedType, setSelectedType] = useState<DailyCheckboxType>('task');
|
const [selectedType, setSelectedType] = useState<DailyCheckboxType>('task');
|
||||||
const [addingCheckbox, setAddingCheckbox] = useState(false);
|
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const handleAddCheckbox = async () => {
|
const handleAddCheckbox = () => {
|
||||||
if (!newCheckboxText.trim()) return;
|
if (!newCheckboxText.trim()) return;
|
||||||
|
|
||||||
setAddingCheckbox(true);
|
const text = newCheckboxText.trim();
|
||||||
try {
|
|
||||||
await onAdd(newCheckboxText.trim(), selectedType); // Pas de taskId lors de l'ajout
|
// Vider et refocus IMMÉDIATEMENT pour l'UX optimiste
|
||||||
setNewCheckboxText('');
|
setNewCheckboxText('');
|
||||||
// Garder le type sélectionné pour enchaîner les créations du même type
|
|
||||||
// setSelectedType('task'); // <- Supprimé pour garder la sélection
|
|
||||||
// Garder le focus sur l'input pour enchainer les entrées
|
|
||||||
setTimeout(() => {
|
|
||||||
inputRef.current?.focus();
|
inputRef.current?.focus();
|
||||||
}, 100);
|
|
||||||
} finally {
|
// Lancer l'ajout en arrière-plan (fire and forget)
|
||||||
setAddingCheckbox(false);
|
onAdd(text, selectedType).catch(error => {
|
||||||
}
|
console.error('Erreur lors de l\'ajout:', error);
|
||||||
|
// En cas d'erreur, on pourrait restaurer le texte
|
||||||
|
// setNewCheckboxText(text);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleKeyPress = (e: React.KeyboardEvent) => {
|
const handleKeyPress = (e: React.KeyboardEvent) => {
|
||||||
@@ -61,7 +59,7 @@ export function DailyAddForm({ onAdd, disabled = false, placeholder = "Ajouter u
|
|||||||
? 'border-l-green-500 bg-green-100 dark:bg-green-900/40 text-green-900 dark:text-green-100 font-medium'
|
? 'border-l-green-500 bg-green-100 dark:bg-green-900/40 text-green-900 dark:text-green-100 font-medium'
|
||||||
: 'border-l-green-300 hover:border-l-green-400 opacity-70 hover:opacity-90'
|
: 'border-l-green-300 hover:border-l-green-400 opacity-70 hover:opacity-90'
|
||||||
}`}
|
}`}
|
||||||
disabled={addingCheckbox || disabled}
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
✅ Tâche
|
✅ Tâche
|
||||||
</Button>
|
</Button>
|
||||||
@@ -75,7 +73,7 @@ export function DailyAddForm({ onAdd, disabled = false, placeholder = "Ajouter u
|
|||||||
? 'border-l-blue-500 bg-blue-100 dark:bg-blue-900/40 text-blue-900 dark:text-blue-100 font-medium'
|
? 'border-l-blue-500 bg-blue-100 dark:bg-blue-900/40 text-blue-900 dark:text-blue-100 font-medium'
|
||||||
: 'border-l-blue-300 hover:border-l-blue-400 opacity-70 hover:opacity-90'
|
: 'border-l-blue-300 hover:border-l-blue-400 opacity-70 hover:opacity-90'
|
||||||
}`}
|
}`}
|
||||||
disabled={addingCheckbox || disabled}
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
🗓️ Réunion
|
🗓️ Réunion
|
||||||
</Button>
|
</Button>
|
||||||
@@ -90,17 +88,17 @@ export function DailyAddForm({ onAdd, disabled = false, placeholder = "Ajouter u
|
|||||||
value={newCheckboxText}
|
value={newCheckboxText}
|
||||||
onChange={(e) => setNewCheckboxText(e.target.value)}
|
onChange={(e) => setNewCheckboxText(e.target.value)}
|
||||||
onKeyDown={handleKeyPress}
|
onKeyDown={handleKeyPress}
|
||||||
disabled={addingCheckbox || disabled}
|
disabled={disabled}
|
||||||
className="flex-1 min-w-[300px]"
|
className="flex-1 min-w-[300px]"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
onClick={handleAddCheckbox}
|
onClick={handleAddCheckbox}
|
||||||
disabled={!newCheckboxText.trim() || addingCheckbox || disabled}
|
disabled={!newCheckboxText.trim() || disabled}
|
||||||
variant="primary"
|
variant="primary"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="min-w-[40px]"
|
className="min-w-[40px]"
|
||||||
>
|
>
|
||||||
{addingCheckbox ? '...' : '+'}
|
+
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import { dailyClient, DailyHistoryFilters, DailySearchFilters, ReorderCheckboxesData } from '@/clients/daily-client';
|
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 {
|
interface UseDailyState {
|
||||||
dailyView: DailyView | null;
|
dailyView: DailyView | null;
|
||||||
@@ -15,8 +15,8 @@ interface UseDailyState {
|
|||||||
interface UseDailyActions {
|
interface UseDailyActions {
|
||||||
refreshDaily: () => Promise<void>;
|
refreshDaily: () => Promise<void>;
|
||||||
refreshDailySilent: () => Promise<void>;
|
refreshDailySilent: () => Promise<void>;
|
||||||
addTodayCheckbox: (text: string, taskId?: string) => Promise<DailyCheckbox | null>;
|
addTodayCheckbox: (text: string, type?: DailyCheckboxType, taskId?: string) => Promise<DailyCheckbox | null>;
|
||||||
addYesterdayCheckbox: (text: string, taskId?: string) => Promise<DailyCheckbox | null>;
|
addYesterdayCheckbox: (text: string, type?: DailyCheckboxType, taskId?: string) => Promise<DailyCheckbox | null>;
|
||||||
updateCheckbox: (checkboxId: string, data: UpdateDailyCheckboxData) => Promise<DailyCheckbox | null>;
|
updateCheckbox: (checkboxId: string, data: UpdateDailyCheckboxData) => Promise<DailyCheckbox | null>;
|
||||||
deleteCheckbox: (checkboxId: string) => Promise<void>;
|
deleteCheckbox: (checkboxId: string) => Promise<void>;
|
||||||
toggleCheckbox: (checkboxId: string) => Promise<void>;
|
toggleCheckbox: (checkboxId: string) => Promise<void>;
|
||||||
@@ -67,66 +67,141 @@ export function useDaily(initialDate?: Date, initialDailyView?: DailyView): UseD
|
|||||||
}
|
}
|
||||||
}, [currentDate]);
|
}, [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;
|
if (!dailyView) return null;
|
||||||
|
|
||||||
try {
|
// Créer une checkbox temporaire pour l'affichage optimiste
|
||||||
setSaving(true);
|
const tempCheckbox: DailyCheckbox = {
|
||||||
setError(null);
|
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 newCheckbox = await dailyClient.addTodayCheckbox(text, taskId);
|
const previousDailyView = dailyView;
|
||||||
|
|
||||||
// Mise à jour optimiste
|
// Mise à jour optimiste IMMÉDIATE
|
||||||
setDailyView(prev => prev ? {
|
setDailyView(prev => prev ? {
|
||||||
...prev,
|
...prev,
|
||||||
today: [...prev.today, newCheckbox].sort((a, b) => a.order - b.order)
|
today: [...prev.today, tempCheckbox]
|
||||||
|
} : null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Appel API en arrière-plan
|
||||||
|
const newCheckbox = await dailyClient.addCheckbox({
|
||||||
|
date: currentDate,
|
||||||
|
text,
|
||||||
|
type: type || 'task',
|
||||||
|
taskId
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remplacer la checkbox temporaire par la vraie
|
||||||
|
setDailyView(prev => prev ? {
|
||||||
|
...prev,
|
||||||
|
today: prev.today.map(cb =>
|
||||||
|
cb.id === tempCheckbox.id ? newCheckbox : cb
|
||||||
|
).sort((a, b) => a.order - b.order)
|
||||||
} : null);
|
} : null);
|
||||||
|
|
||||||
return newCheckbox;
|
return newCheckbox;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
// Rollback en cas d'erreur
|
||||||
|
setDailyView(previousDailyView);
|
||||||
setError(err instanceof Error ? err.message : 'Erreur lors de l\'ajout de la checkbox');
|
setError(err instanceof Error ? err.message : 'Erreur lors de l\'ajout de la checkbox');
|
||||||
console.error('Erreur addTodayCheckbox:', err);
|
console.error('Erreur addTodayCheckbox:', err);
|
||||||
return null;
|
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;
|
if (!dailyView) return null;
|
||||||
|
|
||||||
try {
|
// Créer une checkbox temporaire pour l'affichage optimiste
|
||||||
setSaving(true);
|
const yesterday = new Date(currentDate);
|
||||||
setError(null);
|
yesterday.setDate(yesterday.getDate() - 1);
|
||||||
|
|
||||||
const newCheckbox = await dailyClient.addYesterdayCheckbox(text, taskId);
|
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()
|
||||||
|
};
|
||||||
|
|
||||||
// Mise à jour optimiste
|
const previousDailyView = dailyView;
|
||||||
|
|
||||||
|
// Mise à jour optimiste IMMÉDIATE
|
||||||
setDailyView(prev => prev ? {
|
setDailyView(prev => prev ? {
|
||||||
...prev,
|
...prev,
|
||||||
yesterday: [...prev.yesterday, newCheckbox].sort((a, b) => a.order - b.order)
|
yesterday: [...prev.yesterday, tempCheckbox]
|
||||||
|
} : null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Appel API en arrière-plan
|
||||||
|
const newCheckbox = await dailyClient.addCheckbox({
|
||||||
|
date: yesterday,
|
||||||
|
text,
|
||||||
|
type: type || 'task',
|
||||||
|
taskId
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remplacer la checkbox temporaire par la vraie
|
||||||
|
setDailyView(prev => prev ? {
|
||||||
|
...prev,
|
||||||
|
yesterday: prev.yesterday.map(cb =>
|
||||||
|
cb.id === tempCheckbox.id ? newCheckbox : cb
|
||||||
|
).sort((a, b) => a.order - b.order)
|
||||||
} : null);
|
} : null);
|
||||||
|
|
||||||
return newCheckbox;
|
return newCheckbox;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
// Rollback en cas d'erreur
|
||||||
|
setDailyView(previousDailyView);
|
||||||
setError(err instanceof Error ? err.message : 'Erreur lors de l\'ajout de la checkbox');
|
setError(err instanceof Error ? err.message : 'Erreur lors de l\'ajout de la checkbox');
|
||||||
console.error('Erreur addYesterdayCheckbox:', err);
|
console.error('Erreur addYesterdayCheckbox:', err);
|
||||||
return null;
|
return null;
|
||||||
} finally {
|
|
||||||
setSaving(false);
|
|
||||||
}
|
}
|
||||||
}, [dailyView]);
|
}, [dailyView, currentDate]);
|
||||||
|
|
||||||
const updateCheckbox = useCallback(async (checkboxId: string, data: UpdateDailyCheckboxData): Promise<DailyCheckbox | null> => {
|
const updateCheckbox = useCallback(async (checkboxId: string, data: UpdateDailyCheckboxData): Promise<DailyCheckbox | null> => {
|
||||||
if (!dailyView) return null;
|
if (!dailyView) return null;
|
||||||
|
|
||||||
try {
|
// Trouver la checkbox existante
|
||||||
setSaving(true);
|
let existingCheckbox = dailyView.yesterday.find(cb => cb.id === checkboxId);
|
||||||
setError(null);
|
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 {
|
||||||
|
// Appel API en arrière-plan
|
||||||
const updatedCheckbox = await dailyClient.updateCheckbox(checkboxId, data);
|
const updatedCheckbox = await dailyClient.updateCheckbox(checkboxId, data);
|
||||||
|
|
||||||
// Mise à jour optimiste
|
// Remplacer par la vraie checkbox retournée par l'API
|
||||||
setDailyView(prev => prev ? {
|
setDailyView(prev => prev ? {
|
||||||
...prev,
|
...prev,
|
||||||
yesterday: prev.yesterday.map(cb => cb.id === checkboxId ? updatedCheckbox : cb),
|
yesterday: prev.yesterday.map(cb => cb.id === checkboxId ? updatedCheckbox : cb),
|
||||||
@@ -135,34 +210,35 @@ export function useDaily(initialDate?: Date, initialDailyView?: DailyView): UseD
|
|||||||
|
|
||||||
return updatedCheckbox;
|
return updatedCheckbox;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
// Rollback en cas d'erreur
|
||||||
|
setDailyView(previousDailyView);
|
||||||
setError(err instanceof Error ? err.message : 'Erreur lors de la mise à jour de la checkbox');
|
setError(err instanceof Error ? err.message : 'Erreur lors de la mise à jour de la checkbox');
|
||||||
console.error('Erreur updateCheckbox:', err);
|
console.error('Erreur updateCheckbox:', err);
|
||||||
return null;
|
return null;
|
||||||
} finally {
|
|
||||||
setSaving(false);
|
|
||||||
}
|
}
|
||||||
}, [dailyView]);
|
}, [dailyView]);
|
||||||
|
|
||||||
const deleteCheckbox = useCallback(async (checkboxId: string): Promise<void> => {
|
const deleteCheckbox = useCallback(async (checkboxId: string): Promise<void> => {
|
||||||
if (!dailyView) return;
|
if (!dailyView) return;
|
||||||
|
|
||||||
try {
|
const previousDailyView = dailyView;
|
||||||
setSaving(true);
|
|
||||||
setError(null);
|
|
||||||
|
|
||||||
await dailyClient.deleteCheckbox(checkboxId);
|
// Mise à jour optimiste IMMÉDIATE - supprimer la checkbox
|
||||||
|
|
||||||
// Mise à jour optimiste
|
|
||||||
setDailyView(prev => prev ? {
|
setDailyView(prev => prev ? {
|
||||||
...prev,
|
...prev,
|
||||||
yesterday: prev.yesterday.filter(cb => cb.id !== checkboxId),
|
yesterday: prev.yesterday.filter(cb => cb.id !== checkboxId),
|
||||||
today: prev.today.filter(cb => cb.id !== checkboxId)
|
today: prev.today.filter(cb => cb.id !== checkboxId)
|
||||||
} : null);
|
} : null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Appel API en arrière-plan
|
||||||
|
await dailyClient.deleteCheckbox(checkboxId);
|
||||||
|
// Pas besoin de mise à jour supplémentaire, la suppression est déjà faite
|
||||||
} catch (err) {
|
} 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');
|
setError(err instanceof Error ? err.message : 'Erreur lors de la suppression de la checkbox');
|
||||||
console.error('Erreur deleteCheckbox:', err);
|
console.error('Erreur deleteCheckbox:', err);
|
||||||
} finally {
|
|
||||||
setSaving(false);
|
|
||||||
}
|
}
|
||||||
}, [dailyView]);
|
}, [dailyView]);
|
||||||
|
|
||||||
@@ -177,8 +253,30 @@ export function useDaily(initialDate?: Date, initialDailyView?: DailyView): UseD
|
|||||||
|
|
||||||
if (!checkbox) return;
|
if (!checkbox) return;
|
||||||
|
|
||||||
await updateCheckbox(checkboxId, { isChecked: !checkbox.isChecked });
|
// Mise à jour optimiste IMMÉDIATE
|
||||||
}, [dailyView, updateCheckbox]);
|
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> => {
|
const reorderCheckboxes = useCallback(async (data: ReorderCheckboxesData): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ export function DailyPageClient({
|
|||||||
error,
|
error,
|
||||||
saving,
|
saving,
|
||||||
currentDate,
|
currentDate,
|
||||||
refreshDailySilent,
|
addTodayCheckbox,
|
||||||
|
addYesterdayCheckbox,
|
||||||
toggleCheckbox,
|
toggleCheckbox,
|
||||||
updateCheckbox,
|
updateCheckbox,
|
||||||
deleteCheckbox,
|
deleteCheckbox,
|
||||||
@@ -61,44 +62,15 @@ export function DailyPageClient({
|
|||||||
}, [initialDailyDates.length]);
|
}, [initialDailyDates.length]);
|
||||||
|
|
||||||
const handleAddTodayCheckbox = async (text: string, type: DailyCheckboxType) => {
|
const handleAddTodayCheckbox = async (text: string, type: DailyCheckboxType) => {
|
||||||
try {
|
await addTodayCheckbox(text, type);
|
||||||
const { dailyClient } = await import('@/clients/daily-client');
|
|
||||||
await dailyClient.addCheckbox({
|
|
||||||
date: currentDate,
|
|
||||||
text,
|
|
||||||
type,
|
|
||||||
// Pas de taskId lors de l'ajout - sera ajouté via l'édition
|
|
||||||
isChecked: false
|
|
||||||
});
|
|
||||||
// Recharger silencieusement la vue daily (sans clignotement)
|
|
||||||
refreshDailySilent().catch(console.error);
|
|
||||||
// Recharger aussi les dates pour le calendrier
|
// Recharger aussi les dates pour le calendrier
|
||||||
await refreshDailyDates();
|
await refreshDailyDates();
|
||||||
} catch (error) {
|
|
||||||
console.error('Erreur lors de l\'ajout de la tâche:', error);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddYesterdayCheckbox = async (text: string, type: DailyCheckboxType) => {
|
const handleAddYesterdayCheckbox = async (text: string, type: DailyCheckboxType) => {
|
||||||
try {
|
await addYesterdayCheckbox(text, type);
|
||||||
const yesterday = new Date(currentDate);
|
|
||||||
yesterday.setDate(yesterday.getDate() - 1);
|
|
||||||
|
|
||||||
const { dailyClient } = await import('@/clients/daily-client');
|
|
||||||
await dailyClient.addCheckbox({
|
|
||||||
date: yesterday,
|
|
||||||
text,
|
|
||||||
type,
|
|
||||||
// Pas de taskId lors de l'ajout - sera ajouté via l'édition
|
|
||||||
isChecked: false
|
|
||||||
});
|
|
||||||
// Recharger silencieusement la vue daily (sans clignotement)
|
|
||||||
refreshDailySilent().catch(console.error);
|
|
||||||
// Recharger aussi les dates pour le calendrier
|
// Recharger aussi les dates pour le calendrier
|
||||||
await refreshDailyDates();
|
await refreshDailyDates();
|
||||||
} catch (error) {
|
|
||||||
console.error('Erreur lors de l\'ajout de la tâche:', error);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleToggleCheckbox = async (checkboxId: string) => {
|
const handleToggleCheckbox = async (checkboxId: string) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user