- 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.
228 lines
6.1 KiB
TypeScript
228 lines
6.1 KiB
TypeScript
import { httpClient } from './base/http-client';
|
|
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 {
|
|
limit?: number;
|
|
}
|
|
|
|
export interface DailySearchFilters {
|
|
query: string;
|
|
limit?: number;
|
|
}
|
|
|
|
export interface ReorderCheckboxesData {
|
|
date: Date;
|
|
checkboxIds: string[];
|
|
}
|
|
|
|
export class DailyClient {
|
|
/**
|
|
* Récupère la vue daily d'aujourd'hui (hier + aujourd'hui)
|
|
*/
|
|
async getTodaysDailyView(): Promise<DailyView> {
|
|
const result = await httpClient.get<ApiDailyView>('/daily');
|
|
return this.transformDailyViewDates(result);
|
|
}
|
|
|
|
/**
|
|
* Récupère la vue daily pour une date donnée
|
|
*/
|
|
async getDailyView(date: Date): Promise<DailyView> {
|
|
const dateStr = this.formatDateForAPI(date);
|
|
const result = await httpClient.get<ApiDailyView>(`/daily?date=${dateStr}`);
|
|
return this.transformDailyViewDates(result);
|
|
}
|
|
|
|
/**
|
|
* Récupère l'historique des checkboxes
|
|
*/
|
|
async getCheckboxHistory(filters?: DailyHistoryFilters): Promise<{ date: Date; checkboxes: DailyCheckbox[] }[]> {
|
|
const params = new URLSearchParams({ action: 'history' });
|
|
|
|
if (filters?.limit) params.append('limit', filters.limit.toString());
|
|
|
|
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))
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Recherche dans les checkboxes
|
|
*/
|
|
async searchCheckboxes(filters: DailySearchFilters): Promise<DailyCheckbox[]> {
|
|
const params = new URLSearchParams({
|
|
action: 'search',
|
|
q: filters.query
|
|
});
|
|
|
|
if (filters.limit) params.append('limit', filters.limit.toString());
|
|
|
|
const result = await httpClient.get<ApiCheckbox[]>(`/daily?${params}`);
|
|
return result.map((cb: ApiCheckbox) => this.transformCheckboxDates(cb));
|
|
}
|
|
|
|
/**
|
|
* Ajoute une checkbox
|
|
*/
|
|
async addCheckbox(data: CreateDailyCheckboxData): Promise<DailyCheckbox> {
|
|
const payload = {
|
|
...data,
|
|
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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ajoute une checkbox pour aujourd'hui
|
|
*/
|
|
async addTodayCheckbox(text: string, taskId?: string): Promise<DailyCheckbox> {
|
|
return this.addCheckbox({
|
|
date: new Date(),
|
|
text,
|
|
taskId
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Ajoute une checkbox pour hier
|
|
*/
|
|
async addYesterdayCheckbox(text: string, taskId?: string): Promise<DailyCheckbox> {
|
|
const yesterday = new Date();
|
|
yesterday.setDate(yesterday.getDate() - 1);
|
|
|
|
return this.addCheckbox({
|
|
date: yesterday,
|
|
text,
|
|
taskId
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Met à jour une checkbox
|
|
*/
|
|
async updateCheckbox(checkboxId: string, data: UpdateDailyCheckboxData): Promise<DailyCheckbox> {
|
|
const result = await httpClient.patch<ApiCheckbox>(`/daily/checkboxes/${checkboxId}`, data);
|
|
return this.transformCheckboxDates(result);
|
|
}
|
|
|
|
/**
|
|
* Supprime une checkbox
|
|
*/
|
|
async deleteCheckbox(checkboxId: string): Promise<void> {
|
|
return httpClient.delete(`/daily/checkboxes/${checkboxId}`);
|
|
}
|
|
|
|
/**
|
|
* Réordonne les checkboxes d'une date
|
|
*/
|
|
async reorderCheckboxes(data: ReorderCheckboxesData): Promise<void> {
|
|
return httpClient.post('/daily/checkboxes', {
|
|
date: this.formatDateForAPI(data.date),
|
|
checkboxIds: data.checkboxIds
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Coche/décoche une checkbox (raccourci)
|
|
*/
|
|
async toggleCheckbox(checkboxId: string, isChecked: boolean): Promise<DailyCheckbox> {
|
|
return this.updateCheckbox(checkboxId, { isChecked });
|
|
}
|
|
|
|
/**
|
|
* Formate une date pour l'API (évite les décalages timezone)
|
|
*/
|
|
formatDateForAPI(date: Date): string {
|
|
const year = date.getFullYear();
|
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
const day = String(date.getDate()).padStart(2, '0');
|
|
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)
|
|
*/
|
|
async getDailyViewByRelativeDate(relative: 'yesterday' | 'today' | 'tomorrow'): Promise<DailyView> {
|
|
const date = new Date();
|
|
|
|
switch (relative) {
|
|
case 'yesterday':
|
|
date.setDate(date.getDate() - 1);
|
|
break;
|
|
case 'tomorrow':
|
|
date.setDate(date.getDate() + 1);
|
|
break;
|
|
// 'today' ne change rien
|
|
}
|
|
|
|
return this.getDailyView(date);
|
|
}
|
|
|
|
/**
|
|
* Récupère toutes les dates qui ont des dailies
|
|
*/
|
|
async getDailyDates(): Promise<string[]> {
|
|
const response = await httpClient.get<{ dates: string[] }>('/daily/dates');
|
|
return response.dates;
|
|
}
|
|
}
|
|
|
|
// Instance singleton du client
|
|
export const dailyClient = new DailyClient(); |