From c2202e9e528466c2f9c342638d6688804552ed5f Mon Sep 17 00:00:00 2001 From: saravanakumardb1 Date: Tue, 10 Mar 2026 19:53:45 -0700 Subject: [PATCH] refactor(web): extract shared API client factory, fix types, use crypto.randomUUID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Created api-helpers.ts with shared getAccessToken() + createNotesApiClient() - Removed duplicate API client factory from notes-client.ts, review-client.ts, saved-views-client.ts (3 copies → 1 shared module) - Added reviewedBy/reviewedAt/reviewNote fields to review-client NoteAgentActionDoc type to match backend response shape - Search page: use crypto.randomUUID() for saved view IDs instead of Date.now() to prevent collisions on rapid saves Verification: web typecheck + 6/6 tests pass. --- web/src/app/(app)/search/page.tsx | 2 +- web/src/lib/api-helpers.ts | 20 ++++++++++++++++++++ web/src/lib/notes-client.ts | 21 ++------------------- web/src/lib/review-client.ts | 23 ++++------------------- web/src/lib/saved-views-client.ts | 16 +--------------- 5 files changed, 28 insertions(+), 54 deletions(-) create mode 100644 web/src/lib/api-helpers.ts diff --git a/web/src/app/(app)/search/page.tsx b/web/src/app/(app)/search/page.tsx index 2d6fb62..1479640 100644 --- a/web/src/app/(app)/search/page.tsx +++ b/web/src/app/(app)/search/page.tsx @@ -46,7 +46,7 @@ export default function SearchPage() { if (!query.trim()) return; try { const view = await createSavedView({ - id: `sv-${Date.now()}`, + id: crypto.randomUUID(), name: query.trim().slice(0, 60), scope: "search", query: query.trim(), diff --git a/web/src/lib/api-helpers.ts b/web/src/lib/api-helpers.ts new file mode 100644 index 0000000..55b0f10 --- /dev/null +++ b/web/src/lib/api-helpers.ts @@ -0,0 +1,20 @@ +import { createApiClient } from "@bytelyst/api-client"; +import { NOTES_API_URL, PRODUCT_ID } from "@/lib/product-config"; + +export function getAccessToken(): string | null { + if (typeof window === "undefined") { + return null; + } + + return localStorage.getItem(`${PRODUCT_ID}_access_token`); +} + +export function createNotesApiClient() { + return createApiClient({ + baseUrl: NOTES_API_URL, + getToken: getAccessToken, + defaultHeaders: { + "x-product-id": PRODUCT_ID, + }, + }); +} diff --git a/web/src/lib/notes-client.ts b/web/src/lib/notes-client.ts index 37e33a0..b22f9b3 100644 --- a/web/src/lib/notes-client.ts +++ b/web/src/lib/notes-client.ts @@ -1,6 +1,6 @@ -import { createApiClient } from "@bytelyst/api-client"; import { extractSuggestedTasks } from "@/lib/extraction-client"; -import { NOTES_API_URL, PRODUCT_ID } from "@/lib/product-config"; +import { PRODUCT_ID } from "@/lib/product-config"; +import { createNotesApiClient } from "@/lib/api-helpers"; import type { AgentTimelineItem, ArtifactSummary, LinkedNote, NoteDetail, NoteSummary, NoteTask, WorkspaceSummary } from "@/lib/types"; type NoteDoc = { @@ -90,23 +90,6 @@ type NoteRelationshipListResponse = { items: NoteRelationshipDoc[]; }; -function getAccessToken(): string | null { - if (typeof window === "undefined") { - return null; - } - - return localStorage.getItem(`${PRODUCT_ID}_access_token`); -} - -function createNotesApiClient() { - return createApiClient({ - baseUrl: NOTES_API_URL, - getToken: getAccessToken, - defaultHeaders: { - "x-product-id": PRODUCT_ID, - }, - }); -} function buildWorkspaceMap(workspaces: WorkspaceDoc[]) { return new Map(workspaces.map((workspace) => [workspace.id, workspace])); diff --git a/web/src/lib/review-client.ts b/web/src/lib/review-client.ts index 8de86f4..bea058c 100644 --- a/web/src/lib/review-client.ts +++ b/web/src/lib/review-client.ts @@ -1,5 +1,4 @@ -import { createApiClient } from "@bytelyst/api-client"; -import { NOTES_API_URL, PRODUCT_ID } from "@/lib/product-config"; +import { createNotesApiClient } from "@/lib/api-helpers"; import type { AgentTimelineItem, ApprovalQueueItem } from "@/lib/types"; import { listWorkspaceSummaries } from "@/lib/notes-client"; @@ -15,6 +14,9 @@ type NoteAgentActionDoc = { reason?: string; beforeSummary?: string; afterSummary?: string; + reviewedBy?: string; + reviewedAt?: string; + reviewNote?: string; updatedAt: string; }; @@ -22,23 +24,6 @@ type NoteAgentActionListResponse = { items: NoteAgentActionDoc[]; }; -function getAccessToken(): string | null { - if (typeof window === "undefined") { - return null; - } - - return localStorage.getItem(`${PRODUCT_ID}_access_token`); -} - -function createNotesApiClient() { - return createApiClient({ - baseUrl: NOTES_API_URL, - getToken: getAccessToken, - defaultHeaders: { - "x-product-id": PRODUCT_ID, - }, - }); -} function toSeverity(actionType: NoteAgentActionDoc["actionType"]): ApprovalQueueItem["severity"] { if (actionType === "update" || actionType === "extract_tasks") { diff --git a/web/src/lib/saved-views-client.ts b/web/src/lib/saved-views-client.ts index 8d5b2d5..e422e01 100644 --- a/web/src/lib/saved-views-client.ts +++ b/web/src/lib/saved-views-client.ts @@ -1,7 +1,6 @@ "use client"; -import { createApiClient } from "@bytelyst/api-client"; -import { NOTES_API_URL, PRODUCT_ID } from "@/lib/product-config"; +import { createNotesApiClient } from "@/lib/api-helpers"; export interface SavedView { id: string; @@ -20,19 +19,6 @@ interface SavedViewListResponse { total: number; } -function getAccessToken(): string | null { - if (typeof window === "undefined") return null; - return localStorage.getItem(`${PRODUCT_ID}_access_token`); -} - -function createNotesApiClient() { - return createApiClient({ - baseUrl: NOTES_API_URL, - getToken: getAccessToken, - defaultHeaders: { "x-product-id": PRODUCT_ID }, - }); -} - export async function listSavedViews(scope?: SavedView["scope"]): Promise { const api = createNotesApiClient(); const params = new URLSearchParams();