From 4375aedb347091ca038976e313e1d76332e850d7 Mon Sep 17 00:00:00 2001 From: Samuel James Date: Sat, 5 Sep 2026 15:29:04 +0000 Subject: [PATCH] Clean up: Remove services files --- frontend/src/services/api.ts | 259 ----------------------------------- 1 file changed, 259 deletions(-) delete mode 100644 frontend/src/services/api.ts diff --git a/frontend/src/services/api.ts b/frontend/src/services/api.ts deleted file mode 100644 index 4a91ce6..0000000 --- a/frontend/src/services/api.ts +++ /dev/null @@ -1,259 +0,0 @@ -import { - Task, - TaskDraft, - TASK_PRIORITIES, - TASK_STATUSES, - TITLE_MAX_LENGTH, - DESCRIPTION_MAX_LENGTH, - User, -} from '../types'; - -/** - * In-memory stand-in for the REST API described in - * architect/03-feasibility/frontend-handoff.md. Method names and payload shapes - * match the documented endpoints so the Backend phase can swap the bodies for - * fetch/axios calls without touching any component. - * - * GET /api/auth/me -> getCurrentUser() - * GET /api/users -> getUsers() - * GET /api/tasks -> getTasks() - * POST /api/tasks -> createTask(draft) - * PUT /api/tasks/:id -> updateTask(id, changes) - * DELETE /api/tasks/:id -> deleteTask(id) - */ - -/** Error type carrying a message that is safe to show a user. */ -export class ApiError extends Error { - status: number; - - constructor(message: string, status = 400) { - super(message); - this.name = 'ApiError'; - this.status = status; - } -} - -const users: User[] = [ - { id: 1, username: 'sarah.chen', full_name: 'Sarah Chen' }, - { id: 2, username: 'alex.rivera', full_name: 'Alex Rivera' }, - { id: 3, username: 'david.kim', full_name: 'David Kim' }, - { id: 4, username: 'maria.garcia', full_name: 'Maria Garcia' }, - { id: 5, username: 'ben.carter', full_name: 'Ben Carter' }, -]; - -/** Signed-in user. Real implementation reads this from GET /api/auth/me. */ -const currentUser = users[0]; - -const isoDate = (dayOffset: number): string => { - const date = new Date(); - date.setDate(date.getDate() + dayOffset); - return date.toISOString().slice(0, 10); -}; - -const timestamp = (dayOffset: number): string => { - const date = new Date(); - date.setDate(date.getDate() + dayOffset); - return date.toISOString(); -}; - -let tasks: Task[] = [ - { - id: 1, - title: 'Website Redesign - Homepage Layout', - description: 'Finalize wireframes and mobile view', - assigned_to: users[1], - status: 'in_progress', - priority: 'high', - due_date: isoDate(5), - created_by: currentUser, - created_at: timestamp(-9), - updated_at: timestamp(-1), - }, - { - id: 2, - title: 'Q4 Marketing Strategy', - description: 'Outline key campaigns and budget', - assigned_to: users[0], - status: 'open', - priority: 'medium', - due_date: isoDate(10), - created_by: users[2], - created_at: timestamp(-7), - updated_at: timestamp(-7), - }, - { - id: 3, - title: 'API Integration Debugging', - description: 'Fixing errors in payment gateway', - assigned_to: users[2], - status: 'blocked', - priority: 'high', - due_date: isoDate(8), - created_by: currentUser, - created_at: timestamp(-6), - updated_at: timestamp(-2), - }, - { - id: 4, - title: 'Partner Onboarding Playbook', - description: 'Document the hand-off steps for new partners', - assigned_to: users[2], - status: 'in_progress', - priority: 'high', - due_date: isoDate(-2), - created_by: users[3], - created_at: timestamp(-14), - updated_at: timestamp(-3), - }, - { - id: 5, - title: 'API Integration Review', - description: 'Second pass over the payment gateway contract', - assigned_to: users[0], - status: 'open', - priority: 'low', - due_date: isoDate(6), - created_by: users[1], - created_at: timestamp(-4), - updated_at: timestamp(-4), - }, - { - id: 6, - title: 'Mobile Nav Accessibility Audit', - description: 'Keyboard and screen reader pass on the mobile navigation', - assigned_to: users[3], - status: 'blocked', - priority: 'medium', - due_date: isoDate(12), - created_by: currentUser, - created_at: timestamp(-3), - updated_at: timestamp(-1), - }, -]; - -const delay = (ms: number): Promise => - new Promise((resolve) => setTimeout(resolve, ms)); - -const clone = (task: Task): Task => ({ ...task }); - -const nextId = (): number => - tasks.reduce((highest, task) => Math.max(highest, task.id), 0) + 1; - -/** - * Validates a draft before it reaches the store. The UI validates too, but this - * is the trust boundary the real API will guard, so the rules live here as well. - */ -const validateDraft = (draft: Partial, requireAll: boolean): void => { - if (requireAll || draft.title !== undefined) { - const title = (draft.title ?? '').trim(); - if (!title) { - throw new ApiError('A task title is required.'); - } - if (title.length > TITLE_MAX_LENGTH) { - throw new ApiError(`Keep the title under ${TITLE_MAX_LENGTH} characters.`); - } - } - - if (draft.description !== undefined && draft.description.length > DESCRIPTION_MAX_LENGTH) { - throw new ApiError(`Keep the description under ${DESCRIPTION_MAX_LENGTH} characters.`); - } - - if (requireAll || draft.assignee_id !== undefined) { - const assignee = users.find((user) => user.id === draft.assignee_id); - if (!assignee) { - throw new ApiError('Assign this task to a current team member.'); - } - } - - if (draft.status !== undefined && !TASK_STATUSES.includes(draft.status)) { - throw new ApiError('Choose a valid status.'); - } - - if (draft.priority !== undefined && !TASK_PRIORITIES.includes(draft.priority)) { - throw new ApiError('Choose a valid priority.'); - } - - if (draft.due_date) { - if (Number.isNaN(Date.parse(draft.due_date))) { - throw new ApiError('Enter a due date in YYYY-MM-DD format.'); - } - } -}; - -export const api = { - async getCurrentUser(): Promise { - await delay(150); - return { ...currentUser }; - }, - - async getUsers(): Promise { - await delay(200); - return users.map((user) => ({ ...user })); - }, - - async getTasks(): Promise { - await delay(400); - return tasks.map(clone); - }, - - async createTask(draft: TaskDraft): Promise { - await delay(350); - validateDraft(draft, true); - - const now = new Date().toISOString(); - const task: Task = { - id: nextId(), - title: draft.title.trim(), - description: draft.description.trim(), - assigned_to: users.find((user) => user.id === draft.assignee_id), - status: draft.status, - priority: draft.priority, - due_date: draft.due_date || undefined, - created_by: { ...currentUser }, - created_at: now, - updated_at: now, - }; - - tasks = [task, ...tasks]; - return clone(task); - }, - - async updateTask(id: number, changes: Partial): Promise { - await delay(300); - const existing = tasks.find((task) => task.id === id); - if (!existing) { - throw new ApiError('That task no longer exists. Refresh to see the latest list.', 404); - } - - validateDraft(changes, false); - - const updated: Task = { - ...existing, - title: changes.title !== undefined ? changes.title.trim() : existing.title, - description: - changes.description !== undefined ? changes.description.trim() : existing.description, - assigned_to: - changes.assignee_id !== undefined - ? users.find((user) => user.id === changes.assignee_id) - : existing.assigned_to, - status: changes.status ?? existing.status, - priority: changes.priority ?? existing.priority, - due_date: changes.due_date !== undefined ? changes.due_date || undefined : existing.due_date, - updated_at: new Date().toISOString(), - }; - - tasks = tasks.map((task) => (task.id === id ? updated : task)); - return clone(updated); - }, - - async deleteTask(id: number): Promise { - await delay(250); - const exists = tasks.some((task) => task.id === id); - if (!exists) { - throw new ApiError('That task no longer exists. Refresh to see the latest list.', 404); - } - tasks = tasks.filter((task) => task.id !== id); - }, -}; - -export type Api = typeof api;