- note-agent-actions API: updateApprovalState now accepts optional reviewNote - inbox-store: approve/reject actions pass reviewNote through to backend - Inbox screen: TextInput for review note above approval cards - Review note cleared after successful approve/reject Verification: mobile typecheck passes.
133 lines
3.7 KiB
TypeScript
133 lines
3.7 KiB
TypeScript
import { getApiClient } from './client';
|
|
import { listWorkspaces } from './workspaces';
|
|
|
|
export type MobileApprovalItem = {
|
|
id: string;
|
|
workspaceId: string;
|
|
noteId: string;
|
|
title: string;
|
|
summary: string;
|
|
status: 'pending' | 'approved' | 'rejected';
|
|
};
|
|
|
|
export type MobileActivityItem = {
|
|
id: string;
|
|
workspaceId: string;
|
|
noteId: string;
|
|
title: string;
|
|
summary: string;
|
|
kind: 'note' | 'task' | 'agent';
|
|
};
|
|
|
|
type NoteAgentActionDoc = {
|
|
id: string;
|
|
workspaceId: string;
|
|
noteId: string;
|
|
actorId: string;
|
|
actionType: 'create' | 'update' | 'summarize' | 'extract_tasks' | 'attach_citation';
|
|
state: 'draft' | 'proposed' | 'approved' | 'rejected' | 'applied';
|
|
reason?: string;
|
|
afterSummary?: string;
|
|
updatedAt: string;
|
|
};
|
|
|
|
type NoteAgentActionListResponse = {
|
|
items: NoteAgentActionDoc[];
|
|
};
|
|
|
|
function toApprovalStatus(action: NoteAgentActionDoc): MobileApprovalItem['status'] {
|
|
if (action.state === 'approved' || action.state === 'applied') {
|
|
return 'approved';
|
|
}
|
|
|
|
if (action.state === 'rejected') {
|
|
return 'rejected';
|
|
}
|
|
|
|
return 'pending';
|
|
}
|
|
|
|
function toApprovalItem(action: NoteAgentActionDoc): MobileApprovalItem {
|
|
return {
|
|
id: action.id,
|
|
workspaceId: action.workspaceId,
|
|
noteId: action.noteId,
|
|
title: action.afterSummary ?? action.reason ?? `${action.actionType} proposal`,
|
|
summary: action.reason ?? action.afterSummary ?? `Agent proposed a ${action.actionType.replaceAll('_', ' ')} change.`,
|
|
status: toApprovalStatus(action),
|
|
};
|
|
}
|
|
|
|
function toActivityKind(actionType: NoteAgentActionDoc['actionType']): MobileActivityItem['kind'] {
|
|
if (actionType === 'extract_tasks') {
|
|
return 'task';
|
|
}
|
|
|
|
if (actionType === 'attach_citation') {
|
|
return 'note';
|
|
}
|
|
|
|
return 'agent';
|
|
}
|
|
|
|
function toActivityItem(action: NoteAgentActionDoc): MobileActivityItem {
|
|
return {
|
|
id: action.id,
|
|
workspaceId: action.workspaceId,
|
|
noteId: action.noteId,
|
|
title: action.afterSummary ?? `${action.actionType.replaceAll('_', ' ')} update`,
|
|
summary: action.reason ?? action.afterSummary ?? `State: ${action.state}`,
|
|
kind: toActivityKind(action.actionType),
|
|
};
|
|
}
|
|
|
|
async function listAgentActionsForWorkspace(workspaceId: string): Promise<NoteAgentActionDoc[]> {
|
|
const response = await getApiClient().fetch<NoteAgentActionListResponse>(
|
|
`/note-agent-actions?workspaceId=${encodeURIComponent(workspaceId)}`,
|
|
);
|
|
return response.items;
|
|
}
|
|
|
|
export async function listApprovalQueue(): Promise<MobileApprovalItem[]> {
|
|
const workspaces = await listWorkspaces();
|
|
const actionGroups = await Promise.all(workspaces.map((workspace) => listAgentActionsForWorkspace(workspace.id)));
|
|
|
|
return actionGroups
|
|
.flat()
|
|
.filter((action) => action.state === 'draft' || action.state === 'proposed')
|
|
.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt))
|
|
.map(toApprovalItem);
|
|
}
|
|
|
|
export async function listActivityFeed(): Promise<MobileActivityItem[]> {
|
|
const workspaces = await listWorkspaces();
|
|
const actionGroups = await Promise.all(workspaces.map((workspace) => listAgentActionsForWorkspace(workspace.id)));
|
|
|
|
return actionGroups
|
|
.flat()
|
|
.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt))
|
|
.slice(0, 10)
|
|
.map(toActivityItem);
|
|
}
|
|
|
|
export async function updateApprovalState(
|
|
id: string,
|
|
workspaceId: string,
|
|
state: 'approved' | 'rejected',
|
|
reviewNote?: string,
|
|
): Promise<MobileApprovalItem> {
|
|
const updated = await getApiClient().fetch<NoteAgentActionDoc>(
|
|
`/note-agent-actions/${encodeURIComponent(id)}?workspaceId=${encodeURIComponent(workspaceId)}`,
|
|
{
|
|
method: 'PATCH',
|
|
body: JSON.stringify({
|
|
state,
|
|
reviewedAt: new Date().toISOString(),
|
|
...(reviewNote ? { reviewNote } : {}),
|
|
}),
|
|
},
|
|
);
|
|
|
|
return toApprovalItem(updated);
|
|
}
|