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,11 @@
import type { Agent, AgentKeyCreated, AgentRuntimeState, HeartbeatRun } from "@paperclip/shared";
import type {
Agent,
AgentKeyCreated,
AgentRuntimeState,
HeartbeatRun,
Approval,
AgentConfigRevision,
} from "@paperclip/shared";
import { api } from "./client";
export interface AgentKey {
@@ -21,16 +28,35 @@ export interface OrgNode {
reports: OrgNode[];
}
export interface AgentHireResponse {
agent: Agent;
approval: Approval | null;
}
export const agentsApi = {
list: (companyId: string) => api.get<Agent[]>(`/companies/${companyId}/agents`),
org: (companyId: string) => api.get<OrgNode[]>(`/companies/${companyId}/org`),
listConfigurations: (companyId: string) =>
api.get<Record<string, unknown>[]>(`/companies/${companyId}/agent-configurations`),
get: (id: string) => api.get<Agent>(`/agents/${id}`),
getConfiguration: (id: string) => api.get<Record<string, unknown>>(`/agents/${id}/configuration`),
listConfigRevisions: (id: string) =>
api.get<AgentConfigRevision[]>(`/agents/${id}/config-revisions`),
getConfigRevision: (id: string, revisionId: string) =>
api.get<AgentConfigRevision>(`/agents/${id}/config-revisions/${revisionId}`),
rollbackConfigRevision: (id: string, revisionId: string) =>
api.post<Agent>(`/agents/${id}/config-revisions/${revisionId}/rollback`, {}),
create: (companyId: string, data: Record<string, unknown>) =>
api.post<Agent>(`/companies/${companyId}/agents`, data),
hire: (companyId: string, data: Record<string, unknown>) =>
api.post<AgentHireResponse>(`/companies/${companyId}/agent-hires`, data),
update: (id: string, data: Record<string, unknown>) => api.patch<Agent>(`/agents/${id}`, data),
updatePermissions: (id: string, data: { canCreateAgents: boolean }) =>
api.patch<Agent>(`/agents/${id}/permissions`, data),
pause: (id: string) => api.post<Agent>(`/agents/${id}/pause`, {}),
resume: (id: string) => api.post<Agent>(`/agents/${id}/resume`, {}),
terminate: (id: string) => api.post<Agent>(`/agents/${id}/terminate`, {}),
remove: (id: string) => api.delete<{ ok: true }>(`/agents/${id}`),
listKeys: (id: string) => api.get<AgentKey[]>(`/agents/${id}/keys`),
createKey: (id: string, name: string) => api.post<AgentKeyCreated>(`/agents/${id}/keys`, { name }),
revokeKey: (agentId: string, keyId: string) => api.delete<{ ok: true }>(`/agents/${agentId}/keys/${keyId}`),