// Мини-клиент API. Ошибки авторизации редиректят на вход.
export interface Tag {
id: number
name: string
}
export interface Project {
id: number
name: string
relevance_status: string
priority: number | null
note: string
}
export interface Task {
id: number
title: string
description: string
task_type: string
status: string
detail_state: string
parent_task_id: number | null
project: Project | null
priority: number | null
tags: Tag[]
created_at: string
approved_at: string | null
done_at: string | null
}
export class ApiError extends Error {
status: number
constructor(status: number, message: string) {
super(message)
this.status = status
}
}
async function request<T>(path: string, options: RequestInit = {}): Promise<T> {
const res = await fetch(path, {
headers: { 'Content-Type': 'application/json' },
...options,
})
if (res.status === 401) {
window.location.href = '/auth/login'
throw new ApiError(401, 'Not authenticated')
}
if (!res.ok) {
const body = await res.json().catch(() => ({ detail: res.statusText }))
throw new ApiError(res.status, body.detail ?? 'Ошибка запроса')
}
return res.json() as Promise<T>
}
export const api = {
// tasks
listTasks: (params: Record<string, string | number> = {}) =>
request<Task[]>('/api/tasks?' + new URLSearchParams(
Object.entries(params).map(([k, v]) => [k, String(v)]),
)),
getTask: (id: number) => request<Task>(`/api/tasks/${id}`),
createTask: (title: string, description = '') =>
request<{ id: number }>('/api/tasks', {
method: 'POST',
body: JSON.stringify({ title, description }),
}),
updateTask: (id: number, patch: Partial<Task> & { tag_ids?: number[] }) =>
request<Task>(`/api/tasks/${id}`, { method: 'PATCH', body: JSON.stringify(patch) }),
approveTask: (id: number) => request<Task>(`/api/tasks/${id}/approve`, { method: 'POST' }),
deleteTask: (id: number) => request<{ ok: boolean }>(`/api/tasks/${id}`, { method: 'DELETE' }),
// projects
listProjects: () => request<Project[]>('/api/projects'),
createProject: (name: string) =>
request<Project>('/api/projects', { method: 'POST', body: JSON.stringify({ name }) }),
// tags
listTags: () => request<Tag[]>('/api/tags'),
createTag: (name: string) =>
request<Tag>('/api/tags', { method: 'POST', body: JSON.stringify({ name }) }),
}