UI: approval detail page, agent hiring UX, costs breakdown, sidebar badges, and dashboard improvements

Add ApprovalDetail page with comment thread, revision request/resubmit flow,
and ApprovalPayload component for structured payload display. Extend AgentDetail
with permissions management, config revision history, and duplicate action.
Add agent hire dialog with permission-gated access. Rework Costs page with
per-agent breakdown table and period filtering. Add sidebar badge counts for
pending approvals and inbox items. Enhance Dashboard with live metrics and
sparkline trends. Extend Agents list with pending_approval status and bulk
actions. Update IssueDetail with approval linking. Various component improvements
to MetricCard, InlineEditor, CommentThread, and StatusBadge.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Forgotten
2026-02-19 13:03:08 -06:00
parent 0d73e1b407
commit 176d279403
31 changed files with 1271 additions and 214 deletions

View File

@@ -1,4 +1,4 @@
import type { Approval } from "@paperclip/shared";
import type { Approval, ApprovalComment, Issue } from "@paperclip/shared";
import { api } from "./client";
export const approvalsApi = {
@@ -8,8 +8,17 @@ export const approvalsApi = {
),
create: (companyId: string, data: Record<string, unknown>) =>
api.post<Approval>(`/companies/${companyId}/approvals`, data),
get: (id: string) => api.get<Approval>(`/approvals/${id}`),
approve: (id: string, decisionNote?: string) =>
api.post<Approval>(`/approvals/${id}/approve`, { decisionNote }),
reject: (id: string, decisionNote?: string) =>
api.post<Approval>(`/approvals/${id}/reject`, { decisionNote }),
requestRevision: (id: string, decisionNote?: string) =>
api.post<Approval>(`/approvals/${id}/request-revision`, { decisionNote }),
resubmit: (id: string, payload?: Record<string, unknown>) =>
api.post<Approval>(`/approvals/${id}/resubmit`, { payload }),
listComments: (id: string) => api.get<ApprovalComment[]>(`/approvals/${id}/comments`),
addComment: (id: string, body: string) =>
api.post<ApprovalComment>(`/approvals/${id}/comments`, { body }),
listIssues: (id: string) => api.get<Issue[]>(`/approvals/${id}/issues`),
};