Files
task-team/apps/tasks/lib/api.ts
Admin dd995d9c0f Auth with passwords + Playwright E2E tests + PostHog analytics
- bcrypt password hashing in auth (register, login, change-password)
- Login/register pages with password fields
- Profile update + OAuth placeholder endpoints
- Playwright test suite: auth, pages, API (3 test files)
- PostHog Docker analytics on :8010

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 14:26:51 +00:00

248 lines
7.0 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; password: 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>;
}
// Goals
export interface Goal {
id: string;
user_id: string | null;
title: string;
target_date: string | null;
progress_pct: number;
group_id: string | null;
plan: Record<string, unknown> | null;
created_at: string;
updated_at: string;
group_name: string | null;
group_color: string | null;
group_icon: string | null;
tasks?: Task[];
}
export interface GoalPlanResult {
plan: Record<string, unknown>;
tasks_created: number;
}
export interface GoalReport {
report: string;
stats: { done: number; total: number; pct: number };
}
export function getGoals(token: string) {
return apiFetch<{ data: Goal[] }>("/api/v1/goals", { token });
}
export function getGoal(token: string, id: string) {
return apiFetch<{ data: Goal }>(`/api/v1/goals/${id}`, { token });
}
export function createGoal(token: string, data: Partial<Goal>) {
return apiFetch<{ data: Goal }>("/api/v1/goals", { method: "POST", body: data, token });
}
export function updateGoal(token: string, id: string, data: Partial<Goal>) {
return apiFetch<{ data: Goal }>(`/api/v1/goals/${id}`, { method: "PUT", body: data, token });
}
export function deleteGoal(token: string, id: string) {
return apiFetch<void>(`/api/v1/goals/${id}`, { method: "DELETE", token });
}
export function generateGoalPlan(token: string, id: string) {
return apiFetch<{ data: GoalPlanResult }>(`/api/v1/goals/${id}/plan`, { method: "POST", token });
}
export function getGoalReport(token: string, id: string) {
return apiFetch<{ data: GoalReport }>(`/api/v1/goals/${id}/report`, { token });
}
// Projects
export interface Project {
id: string;
owner_id: string | null;
name: string;
description: string;
color: string;
icon: string;
members: string[];
task_count?: number;
done_count?: number;
created_at: string;
updated_at: string;
}
export function getProjects(token: string, userId?: string) {
const qs = userId ? "?user_id=" + userId : "";
return apiFetch<{ data: Project[] }>("/api/v1/projects" + qs, { token });
}
export function getProject(token: string, id: string) {
return apiFetch<{ data: Project }>("/api/v1/projects/" + id, { token });
}
export function createProject(token: string, data: Partial<Project>) {
return apiFetch<{ data: Project }>("/api/v1/projects", { method: "POST", body: data, token });
}
export function updateProject(token: string, id: string, data: Partial<Project>) {
return apiFetch<{ data: Project }>("/api/v1/projects/" + id, { method: "PUT", body: data, token });
}
export function deleteProject(token: string, id: string) {
return apiFetch<void>("/api/v1/projects/" + id, { method: "DELETE", token });
}
export function inviteToProject(token: string, id: string, userId: string) {
return apiFetch<{ data: Project; status: string }>("/api/v1/projects/" + id + "/invite", { method: "POST", body: { user_id: userId }, token });
}