···5555- if user not found, returns `404` with an empty takes array
5656- returns up to 40 takes ordered by creation date (newest first)
5757- includes project info and total time stats
5858+- includes userName for each take
58595960```
6061GET /api/projects?user=<userId>
···6364- with user param: returns a single project for that user
6465- without user param: returns all projects
6566- returns empty array if no projects found
6767+- includes userName for each project
6868+6969+```
7070+GET /api/time?userId=<userId>
7171+```
7272+returns the total time spent on takes for a user
7373+- requires userId parameter
7474+- returns 400 if userId is missing
7575+- returns 404 if user not found
66766777typical take object looks like:
6878```ts
···7585 elapsedTime: number; // seconds
7686 project: string;
7787 totalTakesTime: number; // seconds
8888+ userName: string;
8989+}
9090+```
9191+9292+typical project object looks like:
9393+```ts
9494+{
9595+ projectName: string;
9696+ projectDescription: string;
9797+ projectBannerUrl: string;
9898+ totalTakesTime: number; // seconds
9999+ userId: string;
100100+ userName: string;
101101+ takesCount: number;
78102}
79103```
80104
+3
src/features/api/index.ts
···22import video from "./routes/video";
33import { handleApiError } from "../../libs/apiError";
44import { projects } from "./routes/projects";
55+import { time } from "./routes/time";
5667export { default as video } from "./routes/video";
78···1617 return await recentTakes(url);
1718 case "projects":
1819 return await projects(url);
2020+ case "time":
2121+ return await time(url);
1922 default:
2023 return new Response(
2124 JSON.stringify({ error: "Route not found" }),
+61
src/features/api/routes/time.ts
···11+import { db } from "../../../libs/db";
22+import { users as usersTable } from "../../../libs/schema";
33+import { handleApiError } from "../../../libs/apiError";
44+import { eq } from "drizzle-orm";
55+66+export async function time(url: URL): Promise<Response> {
77+ try {
88+ const userId = url.searchParams.get("userId");
99+1010+ if (!userId) {
1111+ return new Response(
1212+ JSON.stringify({
1313+ error: "User ID is required",
1414+ }),
1515+ {
1616+ headers: {
1717+ "Content-Type": "application/json",
1818+ },
1919+ status: 400,
2020+ },
2121+ );
2222+ }
2323+2424+ // Get user's total takes time from the database
2525+ const userData = await db
2626+ .select({
2727+ totalTakesTime: usersTable.totalTakesTime,
2828+ })
2929+ .from(usersTable)
3030+ .where(eq(usersTable.id, userId))
3131+ .limit(1);
3232+3333+ if (!userData[0]) {
3434+ return new Response(
3535+ JSON.stringify({
3636+ error: "User not found",
3737+ }),
3838+ {
3939+ headers: {
4040+ "Content-Type": "application/json",
4141+ },
4242+ status: 404,
4343+ },
4444+ );
4545+ }
4646+4747+ return new Response(
4848+ JSON.stringify({
4949+ userId,
5050+ totalTakesTime: userData[0].totalTakesTime || 0,
5151+ }),
5252+ {
5353+ headers: {
5454+ "Content-Type": "application/json",
5555+ },
5656+ },
5757+ );
5858+ } catch (error) {
5959+ return handleApiError(error, "userTime");
6060+ }
6161+}