refactor(web): extract shared API client factory, fix types, use crypto.randomUUID

- 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.
This commit is contained in:
saravanakumardb1 2026-03-10 19:53:45 -07:00
parent c6aa775cd3
commit c2202e9e52
5 changed files with 28 additions and 54 deletions

View File

@ -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(),

View File

@ -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,
},
});
}

View File

@ -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]));

View File

@ -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") {

View File

@ -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<SavedView[]> {
const api = createNotesApiClient();
const params = new URLSearchParams();