diff --git a/mobile/src/api/notes.ts b/mobile/src/api/notes.ts index 43afd57..58b7c76 100644 --- a/mobile/src/api/notes.ts +++ b/mobile/src/api/notes.ts @@ -1,54 +1,80 @@ -import { createPlatformClient } from '@bytelyst/platform-client'; -import { API_CONFIG } from './config'; -import { getAuthClient } from './auth'; import { getApiClient } from './client'; +import { listWorkspaces, type MobileWorkspace } from './workspaces'; export type MobileNote = { id: string; + workspaceId: string; title: string; body: string; workspaceName: string; + status: 'draft' | 'active' | 'archived'; + updatedAt: string; }; -const FALLBACK_NOTES: MobileNote[] = [ - { - id: 'note-1', - title: 'Product launch checklist', - body: 'Finalize launch narrative and mobile scope cut line.', - workspaceName: 'Ops', - }, - { - id: 'note-2', - title: 'Agent review policy draft', - body: 'Human approval required for destructive and external actions.', - workspaceName: 'Platform', - }, -]; +type NoteDoc = { + id: string; + workspaceId: string; + title: string; + body: string; + status: 'draft' | 'active' | 'archived'; + updatedAt: string; +}; -function getClient() { - const auth = getAuthClient(); +type NoteListResponse = { + items: NoteDoc[]; +}; - return createPlatformClient({ - baseUrl: API_CONFIG.productBaseUrl, - productId: API_CONFIG.productId, - getAccessToken: () => auth.getAccessToken(), - refreshAccessToken: () => auth.refreshAccessToken(), - timeoutMs: API_CONFIG.timeoutMs, - }); +function mapWorkspaceNames(workspaces: MobileWorkspace[]) { + return new Map(workspaces.map((workspace) => [workspace.id, workspace.name])); +} + +function toMobileNote(note: NoteDoc, workspaceNames: Map): MobileNote { + return { + id: note.id, + workspaceId: note.workspaceId, + title: note.title, + body: note.body, + workspaceName: workspaceNames.get(note.workspaceId) ?? note.workspaceId, + status: note.status, + updatedAt: note.updatedAt, + }; } export async function listNotes(): Promise { - try { - return await getApiClient().fetch('/notes'); - } catch { - return FALLBACK_NOTES; - } + const [response, workspaces] = await Promise.all([ + getApiClient().fetch('/notes'), + listWorkspaces(), + ]); + + const workspaceNames = mapWorkspaceNames(workspaces); + return response.items.map((note) => toMobileNote(note, workspaceNames)); } -export async function getNote(id: string): Promise { - try { - return await getClient().get(`/notes/${id}`); - } catch { - return FALLBACK_NOTES.find((note) => note.id === id) ?? null; - } +export async function getNote(id: string, workspaceId: string): Promise { + const [note, workspaces] = await Promise.all([ + getApiClient().fetch(`/notes/${id}?workspaceId=${encodeURIComponent(workspaceId)}`), + listWorkspaces(), + ]); + + return toMobileNote(note, mapWorkspaceNames(workspaces)); +} + +export async function updateNote( + id: string, + workspaceId: string, + title: string, + body: string, +): Promise { + const [updated, workspaces] = await Promise.all([ + getApiClient().fetch(`/notes/${id}?workspaceId=${encodeURIComponent(workspaceId)}`, { + method: 'PATCH', + body: JSON.stringify({ + title, + body, + }), + }), + listWorkspaces(), + ]); + + return toMobileNote(updated, mapWorkspaceNames(workspaces)); } diff --git a/mobile/src/api/workspaces.ts b/mobile/src/api/workspaces.ts index 6042c48..cb5b454 100644 --- a/mobile/src/api/workspaces.ts +++ b/mobile/src/api/workspaces.ts @@ -1,12 +1,24 @@ +import { getApiClient } from './client'; + export type MobileWorkspace = { id: string; name: string; + description?: string; +}; + +type WorkspaceListResponse = { + items: Array<{ + id: string; + name: string; + description?: string; + }>; }; export async function listWorkspaces(): Promise { - return [ - { id: 'ws-1', name: 'Ops' }, - { id: 'ws-2', name: 'Platform' }, - { id: 'ws-3', name: 'Research' }, - ]; + const response = await getApiClient().fetch('/workspaces'); + return response.items.map((workspace) => ({ + id: workspace.id, + name: workspace.name, + description: workspace.description, + })); } diff --git a/mobile/src/store/notes-store.ts b/mobile/src/store/notes-store.ts index f252161..24a4931 100644 --- a/mobile/src/store/notes-store.ts +++ b/mobile/src/store/notes-store.ts @@ -1,5 +1,5 @@ import { create } from 'zustand'; -import { getNote, listNotes, type MobileNote } from '../api/notes'; +import { getNote, listNotes, updateNote as persistNote, type MobileNote } from '../api/notes'; export type NotesState = { notes: MobileNote[]; @@ -8,7 +8,7 @@ export type NotesState = { hydrate: () => Promise; openNote: (id: string) => Promise; saveDraft: (title: string, body: string) => void; - updateNote: (id: string, title: string, body: string) => void; + updateNote: (id: string, title: string, body: string) => Promise; }; export const useNotesStore = create((set, get) => ({ @@ -22,20 +22,29 @@ export const useNotesStore = create((set, get) => ({ }, async openNote(id: string) { set({ isLoading: true }); - const note = await getNote(id); + const current = get().notes.find((note: MobileNote) => note.id === id); + if (!current) { + set({ selectedNote: null, isLoading: false }); + return; + } + + const note = await getNote(id, current.workspaceId); set({ selectedNote: note, isLoading: false }); }, saveDraft(title: string, body: string) { const draft: MobileNote = { id: `draft-${Date.now()}`, + workspaceId: 'drafts', title: title.trim() || 'Untitled draft', body, workspaceName: 'Drafts', + status: 'draft', + updatedAt: new Date().toISOString(), }; set({ notes: [draft, ...get().notes], selectedNote: draft }); }, - updateNote(id: string, title: string, body: string) { + async updateNote(id: string, title: string, body: string) { const nextTitle = title.trim() || 'Untitled draft'; const current = get().notes.find((note: MobileNote) => note.id === id); @@ -43,15 +52,28 @@ export const useNotesStore = create((set, get) => ({ return; } - const updated: MobileNote = { - ...current, - title: nextTitle, - body, - }; + if (current.workspaceId === 'drafts') { + const updated: MobileNote = { + ...current, + title: nextTitle, + body, + updatedAt: new Date().toISOString(), + }; + + set({ + notes: get().notes.map((note: MobileNote) => (note.id === id ? updated : note)), + selectedNote: updated, + }); + return; + } + + set({ isLoading: true }); + const updated = await persistNote(id, current.workspaceId, nextTitle, body); set({ notes: get().notes.map((note: MobileNote) => (note.id === id ? updated : note)), selectedNote: updated, + isLoading: false, }); }, }));