Clean up: Remove services files
This commit is contained in:
parent
603be6cbd8
commit
4375aedb34
@ -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<void> =>
|
||||
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<TaskDraft>, 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<User> {
|
||||
await delay(150);
|
||||
return { ...currentUser };
|
||||
},
|
||||
|
||||
async getUsers(): Promise<User[]> {
|
||||
await delay(200);
|
||||
return users.map((user) => ({ ...user }));
|
||||
},
|
||||
|
||||
async getTasks(): Promise<Task[]> {
|
||||
await delay(400);
|
||||
return tasks.map(clone);
|
||||
},
|
||||
|
||||
async createTask(draft: TaskDraft): Promise<Task> {
|
||||
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<TaskDraft>): Promise<Task> {
|
||||
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<void> {
|
||||
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;
|
||||
Loading…
Reference in New Issue
Block a user