2026-02-25 09:29:45 +00:00
|
|
|
import axios from 'axios';
|
|
|
|
|
import { RegisterData, UserData, ProductData, OrderData, AuthResponse } from '../types';
|
2026-02-26 14:12:19 +00:00
|
|
|
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');
|
2026-03-15 15:05:19 +00:00
|
|
|
|
|
|
|
|
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-26 14:12:19 +00:00
|
|
|
|
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-26 14:12:19 +00:00
|
|
|
|
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-26 14:12:19 +00:00
|
|
|
|
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-26 14:12:19 +00:00
|
|
|
|
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-26 14:12:19 +00:00
|
|
|
|
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-26 14:12:19 +00:00
|
|
|
|
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-26 14:12:19 +00:00
|
|
|
|
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
|
|
|
}
|