kanban-app/frontend/src/hooks/useApi.ts

274 lines
8.7 KiB
TypeScript
Raw Normal View History

2026-02-25 09:29:45 +00:00
import axios from 'axios';
import { RegisterData, UserData, ProductData, OrderData, AuthResponse } from '../types';
import {
Board,
BoardWithDetails,
BoardCreate,
List,
Card,
CardWithDetails,
Label,
Checklist,
CommentWithUser,
} from '../types/kanban';
2026-02-27 10:15:50 +00:00
import { useMemo } from 'react';
2026-02-24 08:52:57 +00:00
const api = axios.create({
baseURL: '/api',
headers: {
'Content-Type': 'application/json',
},
2026-02-25 09:29:45 +00:00
});
2026-02-24 08:52:57 +00:00
// Add token to requests if available
api.interceptors.request.use(
(config) => {
2026-02-25 09:29:45 +00:00
const token = localStorage.getItem('token');
2026-02-24 08:52:57 +00:00
if (token) {
2026-02-25 09:29:45 +00:00
config.headers.Authorization = `Bearer ${token}`;
2026-02-24 08:52:57 +00:00
}
2026-02-25 09:29:45 +00:00
return config;
2026-02-24 08:52:57 +00:00
},
(error) => Promise.reject(error)
2026-02-25 09:29:45 +00:00
);
2026-02-24 08:52:57 +00:00
// Handle response errors
api.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
// Token expired or invalid
2026-02-25 09:29:45 +00:00
localStorage.removeItem('token');
localStorage.removeItem('user');
if (!['/login', '/register'].includes(window.location.pathname)) {
window.location.href = '/login';
}
2026-02-24 08:52:57 +00:00
}
2026-02-25 09:29:45 +00:00
return Promise.reject(error);
2026-02-24 08:52:57 +00:00
}
2026-02-25 09:29:45 +00:00
);
2026-02-24 08:52:57 +00:00
export function useApi() {
2026-02-27 10:15:50 +00:00
return useMemo(
() => ({
// Auth
login: async (email: string, password: string): Promise<AuthResponse> => {
const response = await api.post<AuthResponse>('/auth/login', {
email,
password,
});
return response.data;
},
register: async (userData: RegisterData): Promise<AuthResponse> => {
const response = await api.post<AuthResponse>('/auth/register', userData);
return response.data;
},
getCurrentUser: async (): Promise<UserData> => {
const response = await api.get<UserData>('/users/me');
return response.data;
},
2026-02-24 08:52:57 +00:00
2026-02-27 10:15:50 +00:00
// Products
getProducts: async (): Promise<ProductData[]> => {
const response = await api.get<ProductData[]>('/products');
return response.data;
},
getProduct: async (id: string): Promise<ProductData> => {
const response = await api.get<ProductData>(`/products/${id}`);
return response.data;
},
createProduct: async (productData: Omit<ProductData, 'id'>): Promise<ProductData> => {
const response = await api.post<ProductData>('/products', productData);
return response.data;
},
updateProduct: async (
id: string,
productData: Partial<ProductData>
): Promise<ProductData> => {
const response = await api.put<ProductData>(`/products/${id}`, productData);
return response.data;
},
deleteProduct: async (id: string): Promise<void> => {
await api.delete(`/products/${id}`);
},
2026-02-24 08:52:57 +00:00
2026-02-27 10:15:50 +00:00
// Orders
getOrders: async (): Promise<OrderData[]> => {
const response = await api.get<OrderData[]>('/orders');
return response.data;
},
getOrder: async (id: string): Promise<OrderData> => {
const response = await api.get<OrderData>(`/orders/${id}`);
return response.data;
},
createOrder: async (orderData: Omit<OrderData, 'id'>): Promise<OrderData> => {
const response = await api.post<OrderData>('/orders', orderData);
return response.data;
},
2026-02-27 10:15:50 +00:00
// Boards
getBoards: async (): Promise<Board[]> => {
const response = await api.get<Board[]>('/boards');
return response.data;
},
getBoard: async (id: number): Promise<BoardWithDetails> => {
const response = await api.get<BoardWithDetails>(`/boards/${id}`);
return response.data;
},
createBoard: async (boardData: BoardCreate): Promise<Board> => {
const response = await api.post<Board>('/boards', boardData);
return response.data;
},
updateBoard: async (id: number, boardData: Partial<BoardCreate>): Promise<Board> => {
const response = await api.put<Board>(`/boards/${id}`, boardData);
return response.data;
},
deleteBoard: async (id: number): Promise<void> => {
await api.delete(`/boards/${id}`);
},
2026-02-27 10:15:50 +00:00
// Lists
createList: async (
boardId: number,
listData: { name: string; pos: number }
): Promise<List> => {
const response = await api.post<List>(`/boards/${boardId}/lists`, listData);
return response.data;
},
updateList: async (
id: number,
listData: { name: string; pos: number; closed?: boolean }
): Promise<List> => {
const response = await api.put<List>(`/lists/${id}`, listData);
return response.data;
},
deleteList: async (id: number): Promise<void> => {
await api.delete(`/lists/${id}`);
},
2026-02-27 10:15:50 +00:00
// Cards
createCard: async (
listId: number,
cardData: {
name: string;
description?: string;
pos: number;
due?: string | null;
due_complete?: boolean;
badges?: Record<string, any>;
cover?: Record<string, any>;
desc_data?: Record<string, any>;
}
): Promise<Card> => {
const response = await api.post<Card>(`/lists/${listId}/cards`, cardData);
return response.data;
},
getCard: async (id: number): Promise<CardWithDetails> => {
const response = await api.get<CardWithDetails>(`/cards/${id}`);
return response.data;
},
updateCard: async (
id: number,
cardData: {
name: string;
description?: string;
pos: number;
due?: string | null;
due_complete?: boolean;
closed?: boolean;
list_id?: number;
badges?: Record<string, any>;
cover?: Record<string, any>;
desc_data?: Record<string, any>;
}
): Promise<Card> => {
const response = await api.put<Card>(`/cards/${id}`, cardData);
return response.data;
},
deleteCard: async (id: number): Promise<void> => {
await api.delete(`/cards/${id}`);
},
2026-02-27 10:15:50 +00:00
// Labels
getLabels: async (boardId: number): Promise<Label[]> => {
const response = await api.get<Label[]>(`/boards/${boardId}/labels`);
return response.data;
},
createLabel: async (
boardId: number,
labelData: { name: string; color: string }
): Promise<Label> => {
const response = await api.post<Label>(`/boards/${boardId}/labels`, labelData);
return response.data;
},
addLabelToCard: async (cardId: number, labelId: number): Promise<any> => {
const response = await api.post(`/cards/${cardId}/labels`, { label_id: labelId });
return response.data;
},
removeLabelFromCard: async (cardId: number, labelId: number): Promise<void> => {
await api.delete(`/cards/${cardId}/labels/${labelId}`);
},
2026-02-27 10:15:50 +00:00
// Checklists
createChecklist: async (
cardId: number,
checklistData: { name: string; pos: number }
): Promise<Checklist> => {
const response = await api.post<Checklist>(`/cards/${cardId}/checklists`, checklistData);
return response.data;
},
deleteChecklist: async (id: number): Promise<void> => {
await api.delete(`/checklists/${id}`);
},
2026-02-27 10:15:50 +00:00
// Check Items
createCheckItem: async (
checklistId: number,
itemData: {
name: string;
pos: number;
state: 'incomplete' | 'complete';
due?: string | null;
}
): Promise<any> => {
const response = await api.post(`/checklists/${checklistId}/items`, itemData);
return response.data;
},
updateCheckItem: async (
id: number,
itemData: {
name: string;
pos: number;
state: 'incomplete' | 'complete';
due?: string | null;
}
): Promise<any> => {
const response = await api.put(`/check-items/${id}`, itemData);
return response.data;
},
deleteCheckItem: async (id: number): Promise<void> => {
await api.delete(`/check-items/${id}`);
},
2026-02-27 10:15:50 +00:00
// Comments
getComments: async (cardId: number): Promise<CommentWithUser[]> => {
const response = await api.get<CommentWithUser[]>(`/cards/${cardId}/comments`);
return response.data;
},
createComment: async (cardId: number, commentData: { text: string }): Promise<any> => {
const response = await api.post(`/cards/${cardId}/comments`, commentData);
return response.data;
},
updateComment: async (id: number, commentData: { text: string }): Promise<any> => {
const response = await api.put(`/comments/${id}`, commentData);
return response.data;
},
deleteComment: async (id: number): Promise<void> => {
await api.delete(`/comments/${id}`);
},
}),
[]
);
2026-02-25 09:29:45 +00:00
}