chore: refactor project structure and clean up unused components
- Updated `TODO.md` to reflect new testing tasks and final structure expectations. - Simplified TypeScript path mappings in `tsconfig.json` for better clarity. - Revised business logic separation rules in `.cursor/rules` to align with new directory structure. - Deleted unused client components and services to streamline the codebase. - Adjusted import paths in scripts to match the new structure.
This commit is contained in:
114
src/clients/backup-client.ts
Normal file
114
src/clients/backup-client.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { httpClient } from './base/http-client';
|
||||
import { BackupInfo, BackupConfig } from '@/services/backup';
|
||||
|
||||
export interface BackupListResponse {
|
||||
backups: BackupInfo[];
|
||||
scheduler: {
|
||||
isRunning: boolean;
|
||||
isEnabled: boolean;
|
||||
interval: string;
|
||||
nextBackup: string | null;
|
||||
maxBackups: number;
|
||||
backupPath: string;
|
||||
};
|
||||
config: BackupConfig;
|
||||
}
|
||||
|
||||
export class BackupClient {
|
||||
private baseUrl = '/backups';
|
||||
|
||||
/**
|
||||
* Liste toutes les sauvegardes disponibles et l'état du scheduler
|
||||
*/
|
||||
async listBackups(): Promise<BackupListResponse> {
|
||||
const response = await httpClient.get<{ data: BackupListResponse }>(this.baseUrl);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée une nouvelle sauvegarde manuelle
|
||||
*/
|
||||
async createBackup(force: boolean = false): Promise<BackupInfo | null> {
|
||||
const response = await httpClient.post<{ data?: BackupInfo; skipped?: boolean; message?: string }>(this.baseUrl, {
|
||||
action: 'create',
|
||||
force
|
||||
});
|
||||
|
||||
if (response.skipped) {
|
||||
return null; // Backup was skipped
|
||||
}
|
||||
|
||||
return response.data!;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie l'intégrité de la base de données
|
||||
*/
|
||||
async verifyDatabase(): Promise<void> {
|
||||
await httpClient.post(this.baseUrl, {
|
||||
action: 'verify'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour la configuration des sauvegardes
|
||||
*/
|
||||
async updateConfig(config: Partial<BackupConfig>): Promise<BackupConfig> {
|
||||
const response = await httpClient.post<{ data: BackupConfig }>(this.baseUrl, {
|
||||
action: 'config',
|
||||
config
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Démarre ou arrête le planificateur automatique
|
||||
*/
|
||||
async toggleScheduler(enabled: boolean): Promise<{
|
||||
isRunning: boolean;
|
||||
isEnabled: boolean;
|
||||
interval: string;
|
||||
nextBackup: string | null;
|
||||
maxBackups: number;
|
||||
backupPath: string;
|
||||
}> {
|
||||
const response = await httpClient.post<{ data: {
|
||||
isRunning: boolean;
|
||||
isEnabled: boolean;
|
||||
interval: string;
|
||||
nextBackup: string | null;
|
||||
maxBackups: number;
|
||||
backupPath: string;
|
||||
} }>(this.baseUrl, {
|
||||
action: 'scheduler',
|
||||
enabled
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime une sauvegarde
|
||||
*/
|
||||
async deleteBackup(filename: string): Promise<void> {
|
||||
await httpClient.delete(`${this.baseUrl}/${filename}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restaure une sauvegarde (développement uniquement)
|
||||
*/
|
||||
async restoreBackup(filename: string): Promise<void> {
|
||||
await httpClient.post(`${this.baseUrl}/${filename}`, {
|
||||
action: 'restore'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les logs de backup
|
||||
*/
|
||||
async getBackupLogs(maxLines: number = 100): Promise<string[]> {
|
||||
const response = await httpClient.get<{ data: { logs: string[] } }>(`${this.baseUrl}?action=logs&maxLines=${maxLines}`);
|
||||
return response.data.logs;
|
||||
}
|
||||
}
|
||||
|
||||
export const backupClient = new BackupClient();
|
||||
79
src/clients/base/http-client.ts
Normal file
79
src/clients/base/http-client.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Client HTTP de base pour toutes les requêtes API
|
||||
*/
|
||||
export class HttpClient {
|
||||
private baseUrl: string;
|
||||
|
||||
constructor(baseUrl: string = '') {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
private async request<T>(
|
||||
endpoint: string,
|
||||
options: RequestInit = {}
|
||||
): Promise<T> {
|
||||
const url = `${this.baseUrl}${endpoint}`;
|
||||
|
||||
const config: RequestInit = {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers,
|
||||
},
|
||||
...options,
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch(url, config);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(errorData.error || `HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error(`HTTP Request failed: ${url}`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async get<T>(endpoint: string, params?: Record<string, string>): Promise<T> {
|
||||
const url = params
|
||||
? `${endpoint}?${new URLSearchParams(params)}`
|
||||
: endpoint;
|
||||
|
||||
return this.request<T>(url, { method: 'GET' });
|
||||
}
|
||||
|
||||
async post<T>(endpoint: string, data?: unknown): Promise<T> {
|
||||
return this.request<T>(endpoint, {
|
||||
method: 'POST',
|
||||
body: data ? JSON.stringify(data) : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
async put<T>(endpoint: string, data?: unknown): Promise<T> {
|
||||
return this.request<T>(endpoint, {
|
||||
method: 'PUT',
|
||||
body: data ? JSON.stringify(data) : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
async patch<T>(endpoint: string, data?: unknown): Promise<T> {
|
||||
return this.request<T>(endpoint, {
|
||||
method: 'PATCH',
|
||||
body: data ? JSON.stringify(data) : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
async delete<T>(endpoint: string, params?: Record<string, string>): Promise<T> {
|
||||
const url = params
|
||||
? `${endpoint}?${new URLSearchParams(params)}`
|
||||
: endpoint;
|
||||
|
||||
return this.request<T>(url, { method: 'DELETE' });
|
||||
}
|
||||
}
|
||||
|
||||
// Instance par défaut
|
||||
export const httpClient = new HttpClient('/api');
|
||||
158
src/clients/daily-client.ts
Normal file
158
src/clients/daily-client.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
import { httpClient } from './base/http-client';
|
||||
import { DailyCheckbox, DailyView, 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;
|
||||
}
|
||||
|
||||
// Types conservés pour la compatibilité des hooks d'historique et de recherche
|
||||
export interface ReorderCheckboxesData {
|
||||
date: Date;
|
||||
checkboxIds: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Client HTTP pour les données Daily (lecture seule)
|
||||
* Les mutations sont gérées par les server actions dans actions/daily.ts
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
36
src/clients/jira-client.ts
Normal file
36
src/clients/jira-client.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Client pour l'API Jira
|
||||
*/
|
||||
|
||||
import { HttpClient } from './base/http-client';
|
||||
import { JiraSyncResult } from '@/services/jira';
|
||||
|
||||
export interface JiraConnectionStatus {
|
||||
connected: boolean;
|
||||
message: string;
|
||||
details?: string;
|
||||
}
|
||||
|
||||
export class JiraClient extends HttpClient {
|
||||
constructor() {
|
||||
super('/api/jira');
|
||||
}
|
||||
|
||||
/**
|
||||
* Teste la connexion à Jira
|
||||
*/
|
||||
async testConnection(): Promise<JiraConnectionStatus> {
|
||||
return this.get<JiraConnectionStatus>('/sync');
|
||||
}
|
||||
|
||||
/**
|
||||
* Lance la synchronisation manuelle des tickets Jira
|
||||
*/
|
||||
async syncTasks(): Promise<JiraSyncResult> {
|
||||
const response = await this.post<{ data: JiraSyncResult }>('/sync');
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
// Instance singleton
|
||||
export const jiraClient = new JiraClient();
|
||||
61
src/clients/jira-config-client.ts
Normal file
61
src/clients/jira-config-client.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { httpClient } from './base/http-client';
|
||||
import { JiraConfig } from '@/lib/types';
|
||||
|
||||
export interface JiraConfigResponse {
|
||||
jiraConfig: JiraConfig;
|
||||
}
|
||||
|
||||
export interface SaveJiraConfigRequest {
|
||||
baseUrl: string;
|
||||
email: string;
|
||||
apiToken: string;
|
||||
projectKey?: string;
|
||||
ignoredProjects?: string[];
|
||||
}
|
||||
|
||||
export interface SaveJiraConfigResponse {
|
||||
success: boolean;
|
||||
message: string;
|
||||
jiraConfig: JiraConfig;
|
||||
}
|
||||
|
||||
class JiraConfigClient {
|
||||
private readonly basePath = '/user-preferences/jira-config';
|
||||
|
||||
/**
|
||||
* Récupère la configuration Jira actuelle
|
||||
*/
|
||||
async getJiraConfig(): Promise<JiraConfig> {
|
||||
const response = await httpClient.get<JiraConfigResponse>(this.basePath);
|
||||
return response.jiraConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sauvegarde la configuration Jira
|
||||
*/
|
||||
async saveJiraConfig(config: SaveJiraConfigRequest): Promise<SaveJiraConfigResponse> {
|
||||
return httpClient.put<SaveJiraConfigResponse>(this.basePath, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime la configuration Jira (remet à zéro)
|
||||
*/
|
||||
async deleteJiraConfig(): Promise<{ success: boolean; message: string }> {
|
||||
return httpClient.delete(this.basePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Valide l'existence d'un projet Jira
|
||||
*/
|
||||
async validateProject(projectKey: string): Promise<{
|
||||
success: boolean;
|
||||
exists: boolean;
|
||||
projectName?: string;
|
||||
error?: string;
|
||||
message: string;
|
||||
}> {
|
||||
return httpClient.post('/jira/validate-project', { projectKey });
|
||||
}
|
||||
}
|
||||
|
||||
export const jiraConfigClient = new JiraConfigClient();
|
||||
136
src/clients/tags-client.ts
Normal file
136
src/clients/tags-client.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
import { HttpClient } from './base/http-client';
|
||||
import { Tag } from '@/lib/types';
|
||||
|
||||
// Types pour les requêtes (now only used for validation - CRUD operations moved to server actions)
|
||||
|
||||
export interface TagFilters {
|
||||
q?: string; // Recherche par nom
|
||||
popular?: boolean; // Tags les plus utilisés
|
||||
limit?: number; // Limite de résultats
|
||||
}
|
||||
|
||||
// Types pour les réponses
|
||||
export interface TagsResponse {
|
||||
data: Array<Tag & { usage: number }>;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface TagResponse {
|
||||
data: Tag;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface PopularTag extends Tag {
|
||||
usage: number;
|
||||
}
|
||||
|
||||
export interface PopularTagsResponse {
|
||||
data: PopularTag[];
|
||||
message: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Client HTTP pour la gestion des tags
|
||||
*/
|
||||
export class TagsClient extends HttpClient {
|
||||
constructor() {
|
||||
super('/api/tags');
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère tous les tags
|
||||
*/
|
||||
async getTags(filters?: TagFilters): Promise<TagsResponse> {
|
||||
const params: Record<string, string> = {};
|
||||
|
||||
if (filters?.q) {
|
||||
params.q = filters.q;
|
||||
}
|
||||
|
||||
if (filters?.popular) {
|
||||
params.popular = 'true';
|
||||
}
|
||||
|
||||
if (filters?.limit) {
|
||||
params.limit = filters.limit.toString();
|
||||
}
|
||||
|
||||
return this.get<TagsResponse>('', Object.keys(params).length > 0 ? params : undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les tags populaires (les plus utilisés)
|
||||
*/
|
||||
async getPopularTags(limit: number = 10): Promise<PopularTagsResponse> {
|
||||
return this.get<PopularTagsResponse>('', { popular: 'true', limit: limit.toString() });
|
||||
}
|
||||
|
||||
/**
|
||||
* Recherche des tags par nom (pour autocomplete)
|
||||
*/
|
||||
async searchTags(query: string, limit: number = 10): Promise<TagsResponse> {
|
||||
return this.get<TagsResponse>('', { q: query, limit: limit.toString() });
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère un tag par son ID
|
||||
*/
|
||||
async getTagById(id: string): Promise<TagResponse> {
|
||||
return this.get<TagResponse>(`/${id}`);
|
||||
}
|
||||
|
||||
// CRUD operations removed - now handled by server actions in /actions/tags.ts
|
||||
|
||||
/**
|
||||
* Valide le format d'une couleur hexadécimale
|
||||
*/
|
||||
static isValidColor(color: string): boolean {
|
||||
return /^#[0-9A-F]{6}$/i.test(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Génère une couleur aléatoire pour un nouveau tag
|
||||
*/
|
||||
static generateRandomColor(): string {
|
||||
const colors = [
|
||||
'#3B82F6', // Blue
|
||||
'#EF4444', // Red
|
||||
'#10B981', // Green
|
||||
'#F59E0B', // Yellow
|
||||
'#8B5CF6', // Purple
|
||||
'#EC4899', // Pink
|
||||
'#06B6D4', // Cyan
|
||||
'#84CC16', // Lime
|
||||
'#F97316', // Orange
|
||||
'#6366F1', // Indigo
|
||||
];
|
||||
|
||||
return colors[Math.floor(Math.random() * colors.length)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Valide les données d'un tag (utilisé par les formulaires avant server actions)
|
||||
*/
|
||||
static validateTagData(data: { name?: string; color?: string }): string[] {
|
||||
const errors: string[] = [];
|
||||
|
||||
if (!data.name || typeof data.name !== 'string') {
|
||||
errors.push('Le nom du tag est requis');
|
||||
} else if (data.name.trim().length === 0) {
|
||||
errors.push('Le nom du tag ne peut pas être vide');
|
||||
} else if (data.name.length > 50) {
|
||||
errors.push('Le nom du tag ne peut pas dépasser 50 caractères');
|
||||
}
|
||||
|
||||
if (!data.color || typeof data.color !== 'string') {
|
||||
errors.push('La couleur du tag est requise');
|
||||
} else if (!this.isValidColor(data.color)) {
|
||||
errors.push('La couleur doit être au format hexadécimal (#RRGGBB)');
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
|
||||
// Instance singleton
|
||||
export const tagsClient = new TagsClient();
|
||||
81
src/clients/tasks-client.ts
Normal file
81
src/clients/tasks-client.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { httpClient } from './base/http-client';
|
||||
import { Task, TaskStatus, TaskPriority, TaskStats, DailyCheckbox } from '@/lib/types';
|
||||
|
||||
export interface TaskFilters {
|
||||
status?: TaskStatus[];
|
||||
source?: string[];
|
||||
search?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}
|
||||
|
||||
export interface TasksResponse {
|
||||
success: boolean;
|
||||
data: Task[];
|
||||
stats: TaskStats;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface CreateTaskData {
|
||||
title: string;
|
||||
description?: string;
|
||||
status?: TaskStatus;
|
||||
priority?: TaskPriority;
|
||||
tags?: string[];
|
||||
dueDate?: Date;
|
||||
}
|
||||
|
||||
export interface UpdateTaskData {
|
||||
taskId: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
status?: TaskStatus;
|
||||
priority?: TaskPriority;
|
||||
tags?: string[];
|
||||
dueDate?: Date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Client pour la gestion des tâches
|
||||
*/
|
||||
export class TasksClient {
|
||||
|
||||
/**
|
||||
* Récupère toutes les tâches avec filtres
|
||||
*/
|
||||
async getTasks(filters?: TaskFilters): Promise<TasksResponse> {
|
||||
const params: Record<string, string> = {};
|
||||
|
||||
if (filters?.status) {
|
||||
params.status = filters.status.join(',');
|
||||
}
|
||||
if (filters?.source) {
|
||||
params.source = filters.source.join(',');
|
||||
}
|
||||
if (filters?.search) {
|
||||
params.search = filters.search;
|
||||
}
|
||||
if (filters?.limit) {
|
||||
params.limit = filters.limit.toString();
|
||||
}
|
||||
if (filters?.offset) {
|
||||
params.offset = filters.offset.toString();
|
||||
}
|
||||
|
||||
return httpClient.get<TasksResponse>('/tasks', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les daily checkboxes liées à une tâche
|
||||
*/
|
||||
async getTaskCheckboxes(taskId: string): Promise<DailyCheckbox[]> {
|
||||
const response = await httpClient.get<{ data: DailyCheckbox[] }>(`/tasks/${taskId}/checkboxes`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
// Note: Les méthodes createTask, updateTask et deleteTask ont été migrées vers Server Actions
|
||||
// Voir /src/actions/tasks.ts pour createTask, updateTask, updateTaskTitle, updateTaskStatus, deleteTask
|
||||
}
|
||||
|
||||
// Instance singleton
|
||||
export const tasksClient = new TasksClient();
|
||||
28
src/clients/user-preferences-client.ts
Normal file
28
src/clients/user-preferences-client.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { httpClient } from './base/http-client';
|
||||
import { UserPreferences } from '@/lib/types';
|
||||
|
||||
export interface UserPreferencesResponse {
|
||||
success: boolean;
|
||||
data?: UserPreferences;
|
||||
message?: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Client HTTP pour les préférences utilisateur (lecture seule)
|
||||
* Les mutations sont gérées par les server actions dans actions/preferences.ts
|
||||
*/
|
||||
export const userPreferencesClient = {
|
||||
/**
|
||||
* Récupère toutes les préférences utilisateur
|
||||
*/
|
||||
async getPreferences(): Promise<UserPreferences> {
|
||||
const response = await httpClient.get<UserPreferencesResponse>('/user-preferences');
|
||||
|
||||
if (!response.success || !response.data) {
|
||||
throw new Error(response.error || 'Erreur lors de la récupération des préférences');
|
||||
}
|
||||
|
||||
return response.data;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user