diff --git a/mobile/src/api/note-agent-actions.ts b/mobile/src/api/note-agent-actions.ts index 5ec87be..a509fc5 100644 --- a/mobile/src/api/note-agent-actions.ts +++ b/mobile/src/api/note-agent-actions.ts @@ -114,6 +114,7 @@ export async function updateApprovalState( id: string, workspaceId: string, state: 'approved' | 'rejected', + reviewNote?: string, ): Promise { const updated = await getApiClient().fetch( `/note-agent-actions/${encodeURIComponent(id)}?workspaceId=${encodeURIComponent(workspaceId)}`, @@ -122,6 +123,7 @@ export async function updateApprovalState( body: JSON.stringify({ state, reviewedAt: new Date().toISOString(), + ...(reviewNote ? { reviewNote } : {}), }), }, ); diff --git a/mobile/src/app/(tabs)/inbox.tsx b/mobile/src/app/(tabs)/inbox.tsx index e18cb38..a212df8 100644 --- a/mobile/src/app/(tabs)/inbox.tsx +++ b/mobile/src/app/(tabs)/inbox.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import { Pressable, StyleSheet, Text, View } from 'react-native'; +import { Pressable, StyleSheet, Text, TextInput, View } from 'react-native'; import { useInboxStore, type ActivityItem, type ApprovalItem, type InboxState } from '../../store/inbox-store'; import { colors } from '../../theme'; @@ -11,6 +11,7 @@ export default function InboxScreen() { const approve = useInboxStore((state: InboxState) => state.approve); const reject = useInboxStore((state: InboxState) => state.reject); const [pendingApprovalId, setPendingApprovalId] = useState(null); + const [reviewNote, setReviewNote] = useState(''); useEffect(() => { void hydrate(); @@ -21,6 +22,17 @@ export default function InboxScreen() { Inbox Review simple agent actions and approve or reject them from mobile. {isLoading ? Loading approvals… : null} + {approvals.length > 0 ? ( + + ) : null} {approvals.map((item: ApprovalItem) => ( {item.title} @@ -36,7 +48,9 @@ export default function InboxScreen() { onPress={async () => { setPendingApprovalId(item.id); try { - await approve(item.id); + const note = reviewNote.trim() || undefined; + await approve(item.id, note); + setReviewNote(''); } finally { setPendingApprovalId(null); } @@ -55,7 +69,9 @@ export default function InboxScreen() { onPress={async () => { setPendingApprovalId(item.id); try { - await reject(item.id); + const note = reviewNote.trim() || undefined; + await reject(item.id, note); + setReviewNote(''); } finally { setPendingApprovalId(null); } @@ -181,4 +197,15 @@ const styles = StyleSheet.create({ fontWeight: '700', textTransform: 'uppercase', }, + reviewNoteInput: { + backgroundColor: colors.bgElevated, + borderRadius: 12, + borderWidth: 1, + borderColor: colors.borderDefault, + color: colors.textPrimary, + padding: 12, + fontSize: 14, + minHeight: 48, + textAlignVertical: 'top' as const, + }, }); diff --git a/mobile/src/store/inbox-store.ts b/mobile/src/store/inbox-store.ts index 7c1cecc..9ff0991 100644 --- a/mobile/src/store/inbox-store.ts +++ b/mobile/src/store/inbox-store.ts @@ -28,8 +28,8 @@ export type InboxState = { activity: ActivityItem[]; isLoading: boolean; hydrate: () => Promise; - approve: (id: string) => Promise; - reject: (id: string) => Promise; + approve: (id: string, reviewNote?: string) => Promise; + reject: (id: string, reviewNote?: string) => Promise; }; function toApprovalItem(item: MobileApprovalItem): ApprovalItem { @@ -53,26 +53,26 @@ export const useInboxStore = create((set, get) => ({ isLoading: false, }); }, - async approve(id: string) { + async approve(id: string, reviewNote?: string) { const current = get().approvals.find((item) => item.id === id); if (!current) { return; } - const updated = await updateApprovalState(id, current.workspaceId, 'approved'); + const updated = await updateApprovalState(id, current.workspaceId, 'approved', reviewNote); set((state) => ({ approvals: state.approvals.map((item) => item.id === id ? toApprovalItem(updated) : item, ), })); }, - async reject(id: string) { + async reject(id: string, reviewNote?: string) { const current = get().approvals.find((item) => item.id === id); if (!current) { return; } - const updated = await updateApprovalState(id, current.workspaceId, 'rejected'); + const updated = await updateApprovalState(id, current.workspaceId, 'rejected', reviewNote); set((state) => ({ approvals: state.approvals.map((item) => item.id === id ? toApprovalItem(updated) : item,