249 lines
5.6 KiB
TypeScript
249 lines
5.6 KiB
TypeScript
/**
|
|
* 📝 Tasks Store (Zustand)
|
|
* Gerenciador de estado das tarefas
|
|
*/
|
|
|
|
import { create } from 'zustand';
|
|
import apiClient, { handleApiError } from '../api';
|
|
import {
|
|
TasksState,
|
|
Task,
|
|
CreateTaskPayload,
|
|
UpdateTaskPayload,
|
|
TaskFilters,
|
|
TasksListResponse,
|
|
TaskStatsResponse,
|
|
} from '../types';
|
|
|
|
interface TasksStore extends TasksState {
|
|
// Ações
|
|
fetchTasks: (filters?: TaskFilters) => Promise<void>;
|
|
fetchStats: () => Promise<void>;
|
|
createTask: (payload: CreateTaskPayload) => Promise<Task | null>;
|
|
updateTask: (id: string, payload: UpdateTaskPayload) => Promise<Task | null>;
|
|
deleteTask: (id: string) => Promise<boolean>;
|
|
setFilters: (filters: TaskFilters) => void;
|
|
setError: (error: string | null) => void;
|
|
setLoading: (loading: boolean) => void;
|
|
clear: () => void;
|
|
}
|
|
|
|
/**
|
|
* Criar store de tarefas
|
|
*/
|
|
export const useTasksStore = create<TasksStore>((set, get) => ({
|
|
// Estado inicial
|
|
tasks: [],
|
|
stats: null,
|
|
isLoading: false,
|
|
error: null,
|
|
filters: {
|
|
completed: undefined,
|
|
sortBy: 'created_at',
|
|
order: 'desc',
|
|
},
|
|
|
|
// ============================================================================
|
|
// AÇÕES
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Buscar tarefas com filtros
|
|
*/
|
|
fetchTasks: async (filters?: TaskFilters) => {
|
|
try {
|
|
set({ isLoading: true, error: null });
|
|
|
|
if (filters) {
|
|
set({ filters });
|
|
}
|
|
|
|
const currentFilters = filters || get().filters;
|
|
const params = new URLSearchParams();
|
|
|
|
if (currentFilters.completed !== undefined) {
|
|
params.append('completed', String(currentFilters.completed));
|
|
}
|
|
if (currentFilters.category) {
|
|
params.append('category', currentFilters.category);
|
|
}
|
|
if (currentFilters.priority) {
|
|
params.append('priority', currentFilters.priority);
|
|
}
|
|
if (currentFilters.sortBy) {
|
|
params.append('sortBy', currentFilters.sortBy);
|
|
}
|
|
if (currentFilters.order) {
|
|
params.append('order', currentFilters.order);
|
|
}
|
|
|
|
const url = `/tasks${params.toString() ? '?' + params.toString() : ''}`;
|
|
const response = await apiClient.get<TasksListResponse>(url);
|
|
|
|
set({
|
|
tasks: response.data.data,
|
|
isLoading: false,
|
|
});
|
|
} catch (error) {
|
|
const message = handleApiError(error);
|
|
set({ error: message, isLoading: false, tasks: [] });
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Buscar estatísticas
|
|
*/
|
|
fetchStats: async () => {
|
|
try {
|
|
const response = await apiClient.get<TaskStatsResponse>('/tasks/stats');
|
|
set({ stats: response.data.data });
|
|
} catch (error) {
|
|
console.error('Erro ao buscar estatísticas:', error);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Criar tarefa
|
|
*/
|
|
createTask: async (payload: CreateTaskPayload) => {
|
|
try {
|
|
set({ isLoading: true, error: null });
|
|
|
|
const response = await apiClient.post<{ data: Task }>('/tasks', payload);
|
|
const newTask = response.data.data;
|
|
|
|
// Adicionar à lista
|
|
set({ tasks: [newTask, ...get().tasks] });
|
|
|
|
// Atualizar stats se houver
|
|
if (get().stats) {
|
|
await get().fetchStats();
|
|
}
|
|
|
|
return newTask;
|
|
} catch (error) {
|
|
const message = handleApiError(error);
|
|
set({ error: message });
|
|
throw new Error(message);
|
|
} finally {
|
|
set({ isLoading: false });
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Atualizar tarefa
|
|
*/
|
|
updateTask: async (id: string, payload: UpdateTaskPayload) => {
|
|
try {
|
|
set({ isLoading: true, error: null });
|
|
|
|
const response = await apiClient.patch<{ data: Task }>(
|
|
`/tasks/${id}`,
|
|
payload,
|
|
);
|
|
const updatedTask = response.data.data;
|
|
|
|
// Atualizar na lista
|
|
set({
|
|
tasks: get().tasks.map((t) => (t.id === id ? updatedTask : t)),
|
|
});
|
|
|
|
// Atualizar stats se houver
|
|
if (get().stats) {
|
|
await get().fetchStats();
|
|
}
|
|
|
|
return updatedTask;
|
|
} catch (error) {
|
|
const message = handleApiError(error);
|
|
set({ error: message });
|
|
throw new Error(message);
|
|
} finally {
|
|
set({ isLoading: false });
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Deletar tarefa
|
|
*/
|
|
deleteTask: async (id: string) => {
|
|
try {
|
|
set({ isLoading: true, error: null });
|
|
|
|
await apiClient.delete(`/tasks/${id}`);
|
|
|
|
// Remover da lista
|
|
set({ tasks: get().tasks.filter((t) => t.id !== id) });
|
|
|
|
// Atualizar stats se houver
|
|
if (get().stats) {
|
|
await get().fetchStats();
|
|
}
|
|
|
|
return true;
|
|
} catch (error) {
|
|
const message = handleApiError(error);
|
|
set({ error: message });
|
|
return false;
|
|
} finally {
|
|
set({ isLoading: false });
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Setar filtros
|
|
*/
|
|
setFilters: (filters: TaskFilters) => {
|
|
set({ filters });
|
|
},
|
|
|
|
/**
|
|
* Setar erro
|
|
*/
|
|
setError: (error: string | null) => {
|
|
set({ error });
|
|
},
|
|
|
|
/**
|
|
* Setar loading
|
|
*/
|
|
setLoading: (isLoading: boolean) => {
|
|
set({ isLoading });
|
|
},
|
|
|
|
/**
|
|
* Limpar estado
|
|
*/
|
|
clear: () => {
|
|
set({
|
|
tasks: [],
|
|
stats: null,
|
|
isLoading: false,
|
|
error: null,
|
|
filters: {
|
|
completed: undefined,
|
|
sortBy: 'created_at',
|
|
order: 'desc',
|
|
},
|
|
});
|
|
},
|
|
}));
|
|
|
|
// Hook customizado
|
|
export const useTasks = () => {
|
|
const store = useTasksStore();
|
|
return {
|
|
tasks: store.tasks,
|
|
stats: store.stats,
|
|
isLoading: store.isLoading,
|
|
error: store.error,
|
|
filters: store.filters,
|
|
fetchTasks: store.fetchTasks,
|
|
fetchStats: store.fetchStats,
|
|
createTask: store.createTask,
|
|
updateTask: store.updateTask,
|
|
deleteTask: store.deleteTask,
|
|
setFilters: store.setFilters,
|
|
};
|
|
};
|