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; if (!query.trim()) return;
try { try {
const view = await createSavedView({ const view = await createSavedView({
id: `sv-${Date.now()}`, id: crypto.randomUUID(),
name: query.trim().slice(0, 60), name: query.trim().slice(0, 60),
scope: "search", scope: "search",
query: query.trim(), 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 { 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"; import type { AgentTimelineItem, ArtifactSummary, LinkedNote, NoteDetail, NoteSummary, NoteTask, WorkspaceSummary } from "@/lib/types";
type NoteDoc = { type NoteDoc = {
@ -90,23 +90,6 @@ type NoteRelationshipListResponse = {
items: NoteRelationshipDoc[]; 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[]) { function buildWorkspaceMap(workspaces: WorkspaceDoc[]) {
return new Map(workspaces.map((workspace) => [workspace.id, workspace])); return new Map(workspaces.map((workspace) => [workspace.id, workspace]));

View File

@ -1,5 +1,4 @@
import { createApiClient } from "@bytelyst/api-client"; import { createNotesApiClient } from "@/lib/api-helpers";
import { NOTES_API_URL, PRODUCT_ID } from "@/lib/product-config";
import type { AgentTimelineItem, ApprovalQueueItem } from "@/lib/types"; import type { AgentTimelineItem, ApprovalQueueItem } from "@/lib/types";
import { listWorkspaceSummaries } from "@/lib/notes-client"; import { listWorkspaceSummaries } from "@/lib/notes-client";
@ -15,6 +14,9 @@ type NoteAgentActionDoc = {
reason?: string; reason?: string;
beforeSummary?: string; beforeSummary?: string;
afterSummary?: string; afterSummary?: string;
reviewedBy?: string;
reviewedAt?: string;
reviewNote?: string;
updatedAt: string; updatedAt: string;
}; };
@ -22,23 +24,6 @@ type NoteAgentActionListResponse = {
items: NoteAgentActionDoc[]; 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"] { function toSeverity(actionType: NoteAgentActionDoc["actionType"]): ApprovalQueueItem["severity"] {
if (actionType === "update" || actionType === "extract_tasks") { if (actionType === "update" || actionType === "extract_tasks") {

View File

@ -1,7 +1,6 @@
"use client"; "use client";
import { createApiClient } from "@bytelyst/api-client"; import { createNotesApiClient } from "@/lib/api-helpers";
import { NOTES_API_URL, PRODUCT_ID } from "@/lib/product-config";
export interface SavedView { export interface SavedView {
id: string; id: string;
@ -20,19 +19,6 @@ interface SavedViewListResponse {
total: number; 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[]> { export async function listSavedViews(scope?: SavedView["scope"]): Promise<SavedView[]> {
const api = createNotesApiClient(); const api = createNotesApiClient();
const params = new URLSearchParams(); const params = new URLSearchParams();