Login/register pages now correctly unwrap API response {data: {token, user}}.
Cleaned up leftover apiFetch code.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
152 lines
4.1 KiB
TypeScript
152 lines
4.1 KiB
TypeScript
const API_BASE = typeof window !== "undefined" ? "" : (process.env.NEXT_PUBLIC_API_URL || "http://localhost:3000");
|
|
|
|
interface ApiOptions {
|
|
method?: string;
|
|
body?: unknown;
|
|
token?: string;
|
|
}
|
|
|
|
async function apiFetch<T>(path: string, opts: ApiOptions = {}): Promise<T> {
|
|
const headers: Record<string, string> = {
|
|
"Content-Type": "application/json",
|
|
};
|
|
if (opts.token) {
|
|
headers["Authorization"] = `Bearer ${opts.token}`;
|
|
}
|
|
|
|
const url = `${API_BASE}${path}`;
|
|
const res = await fetch(url, {
|
|
method: opts.method || "GET",
|
|
headers,
|
|
body: opts.body ? JSON.stringify(opts.body) : undefined,
|
|
cache: "no-store",
|
|
});
|
|
|
|
if (!res.ok) {
|
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
throw new Error(err.message || err.error || `HTTP ${res.status}`);
|
|
}
|
|
|
|
if (res.status === 204 || res.headers.get("content-length") === "0") {
|
|
return {} as T;
|
|
}
|
|
|
|
return res.json();
|
|
}
|
|
|
|
// Auth
|
|
export function register(data: { email: string; name: string; phone?: string }) {
|
|
return apiFetch<{ data: { token: string; user: User } }>("/api/v1/auth/register", {
|
|
method: "POST",
|
|
body: data,
|
|
});
|
|
}
|
|
|
|
export function login(data: { email: string; password?: string }) {
|
|
return apiFetch<{ data: { token: string; user: User } }>("/api/v1/auth/login", {
|
|
method: "POST",
|
|
body: data,
|
|
});
|
|
}
|
|
|
|
export function getMe(token: string) {
|
|
return apiFetch<{ user: User }>("/api/v1/auth/me", { token });
|
|
}
|
|
|
|
// Tasks
|
|
export function getTasks(token: string, params?: Record<string, string>) {
|
|
const qs = params ? "?" + new URLSearchParams(params).toString() : "";
|
|
return apiFetch<{ data: Task[]; total: number }>(`/api/v1/tasks${qs}`, { token });
|
|
}
|
|
|
|
export function getTask(token: string, id: string) {
|
|
return apiFetch<Task>(`/api/v1/tasks/${id}`, { token });
|
|
}
|
|
|
|
export function createTask(token: string, data: Partial<Task>) {
|
|
return apiFetch<Task>("/api/v1/tasks", { method: "POST", body: data, token });
|
|
}
|
|
|
|
export function updateTask(token: string, id: string, data: Partial<Task>) {
|
|
return apiFetch<Task>(`/api/v1/tasks/${id}`, { method: "PUT", body: data, token });
|
|
}
|
|
|
|
export function deleteTask(token: string, id: string) {
|
|
return apiFetch<void>(`/api/v1/tasks/${id}`, { method: "DELETE", token });
|
|
}
|
|
|
|
// Groups
|
|
export function getGroups(token: string) {
|
|
return apiFetch<{ data: Group[] }>("/api/v1/groups", { token });
|
|
}
|
|
|
|
export function createGroup(token: string, data: Partial<Group>) {
|
|
return apiFetch<Group>("/api/v1/groups", { method: "POST", body: data, token });
|
|
}
|
|
|
|
export function updateGroup(token: string, id: string, data: Partial<Group>) {
|
|
return apiFetch<Group>(`/api/v1/groups/${id}`, { method: "PUT", body: data, token });
|
|
}
|
|
|
|
export function deleteGroup(token: string, id: string) {
|
|
return apiFetch<void>(`/api/v1/groups/${id}`, { method: "DELETE", token });
|
|
}
|
|
|
|
export function reorderGroups(token: string, ids: string[]) {
|
|
return apiFetch<void>("/api/v1/groups/reorder", { method: "PUT", body: { ids }, token });
|
|
}
|
|
|
|
// Connectors
|
|
export function getConnectors(token: string) {
|
|
return apiFetch<{ data: Connector[] }>("/api/v1/connectors", { token });
|
|
}
|
|
|
|
export function createConnector(token: string, data: Partial<Connector>) {
|
|
return apiFetch<Connector>("/api/v1/connectors", { method: "POST", body: data, token });
|
|
}
|
|
|
|
// Types
|
|
export interface User {
|
|
id: string;
|
|
email: string;
|
|
name: string;
|
|
phone?: string;
|
|
}
|
|
|
|
export interface Task {
|
|
id: string;
|
|
user_id: string | null;
|
|
group_id: string | null;
|
|
title: string;
|
|
description: string;
|
|
status: "pending" | "in_progress" | "done" | "cancelled";
|
|
priority: "low" | "medium" | "high" | "urgent";
|
|
scheduled_at: string | null;
|
|
due_at: string | null;
|
|
completed_at: string | null;
|
|
assigned_to: string[];
|
|
attachments: string[];
|
|
external_id: string | null;
|
|
external_source: string | null;
|
|
created_at: string;
|
|
updated_at: string;
|
|
group_name: string | null;
|
|
group_color: string | null;
|
|
group_icon: string | null;
|
|
}
|
|
|
|
export interface Group {
|
|
id: string;
|
|
name: string;
|
|
color: string;
|
|
icon: string | null;
|
|
sort_order: number;
|
|
}
|
|
|
|
export interface Connector {
|
|
id: string;
|
|
name: string;
|
|
type: string;
|
|
config: Record<string, unknown>;
|
|
}
|