UI: task sessions in agent detail, ApprovalCard extraction, and company settings page
Show task sessions list in AgentDetail with per-session reset. Extract ApprovalCard into standalone component from Approvals and Inbox pages, reducing duplication. Add CompanySettings page with issuePrefix configuration. Fix Sidebar active state for settings route. Display sessionDisplayId in agent properties. Various cleanups to Approvals and Inbox pages. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -81,9 +81,11 @@ export function AgentProperties({ agent, runtimeState }: AgentPropertiesProps) {
|
||||
<Separator />
|
||||
|
||||
<div className="space-y-1">
|
||||
{runtimeState?.sessionId && (
|
||||
{(runtimeState?.sessionDisplayId ?? runtimeState?.sessionId) && (
|
||||
<PropertyRow label="Session">
|
||||
<span className="text-xs font-mono">{runtimeState.sessionId.slice(0, 12)}...</span>
|
||||
<span className="text-xs font-mono">
|
||||
{String(runtimeState.sessionDisplayId ?? runtimeState.sessionId).slice(0, 12)}...
|
||||
</span>
|
||||
</PropertyRow>
|
||||
)}
|
||||
{runtimeState?.lastError && (
|
||||
|
||||
94
ui/src/components/ApprovalCard.tsx
Normal file
94
ui/src/components/ApprovalCard.tsx
Normal file
@@ -0,0 +1,94 @@
|
||||
import { CheckCircle2, XCircle, Clock } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Identity } from "./Identity";
|
||||
import { typeLabel, typeIcon, defaultTypeIcon, ApprovalPayloadRenderer } from "./ApprovalPayload";
|
||||
import { timeAgo } from "../lib/timeAgo";
|
||||
import type { Approval, Agent } from "@paperclip/shared";
|
||||
|
||||
function statusIcon(status: string) {
|
||||
if (status === "approved") return <CheckCircle2 className="h-3.5 w-3.5 text-green-400" />;
|
||||
if (status === "rejected") return <XCircle className="h-3.5 w-3.5 text-red-400" />;
|
||||
if (status === "revision_requested") return <Clock className="h-3.5 w-3.5 text-amber-400" />;
|
||||
if (status === "pending") return <Clock className="h-3.5 w-3.5 text-yellow-400" />;
|
||||
return null;
|
||||
}
|
||||
|
||||
export function ApprovalCard({
|
||||
approval,
|
||||
requesterAgent,
|
||||
onApprove,
|
||||
onReject,
|
||||
onOpen,
|
||||
isPending,
|
||||
}: {
|
||||
approval: Approval;
|
||||
requesterAgent: Agent | null;
|
||||
onApprove: () => void;
|
||||
onReject: () => void;
|
||||
onOpen: () => void;
|
||||
isPending: boolean;
|
||||
}) {
|
||||
const Icon = typeIcon[approval.type] ?? defaultTypeIcon;
|
||||
const label = typeLabel[approval.type] ?? approval.type;
|
||||
|
||||
return (
|
||||
<div className="border border-border rounded-lg p-4 space-y-0">
|
||||
{/* Header */}
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<Icon className="h-4 w-4 text-muted-foreground shrink-0" />
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-medium text-sm">{label}</span>
|
||||
{requesterAgent && (
|
||||
<span className="text-xs text-muted-foreground">
|
||||
requested by <Identity name={requesterAgent.name} size="sm" className="inline-flex" />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 shrink-0">
|
||||
{statusIcon(approval.status)}
|
||||
<span className="text-xs text-muted-foreground capitalize">{approval.status}</span>
|
||||
<span className="text-xs text-muted-foreground">· {timeAgo(approval.createdAt)}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Payload */}
|
||||
<ApprovalPayloadRenderer type={approval.type} payload={approval.payload} />
|
||||
|
||||
{/* Decision note */}
|
||||
{approval.decisionNote && (
|
||||
<div className="mt-3 text-xs text-muted-foreground italic border-t border-border pt-2">
|
||||
Note: {approval.decisionNote}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Actions */}
|
||||
{(approval.status === "pending" || approval.status === "revision_requested") && (
|
||||
<div className="flex gap-2 mt-4 pt-3 border-t border-border">
|
||||
<Button
|
||||
size="sm"
|
||||
className="bg-green-700 hover:bg-green-600 text-white"
|
||||
onClick={onApprove}
|
||||
disabled={isPending}
|
||||
>
|
||||
Approve
|
||||
</Button>
|
||||
<Button
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
onClick={onReject}
|
||||
disabled={isPending}
|
||||
>
|
||||
Reject
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
<div className="mt-3">
|
||||
<Button variant="ghost" size="sm" className="text-xs px-0" onClick={onOpen}>
|
||||
View details
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ChevronsUpDown, Plus } from "lucide-react";
|
||||
import { ChevronsUpDown, Plus, Settings } from "lucide-react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useCompany } from "../context/CompanyContext";
|
||||
import {
|
||||
@@ -63,6 +63,10 @@ export function CompanySwitcher() {
|
||||
<DropdownMenuItem disabled>No companies</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onClick={() => navigate("/company/settings")}>
|
||||
<Settings className="h-4 w-4 mr-2" />
|
||||
Company Settings
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => navigate("/companies")}>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
Manage Companies
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
SquarePen,
|
||||
ListTodo,
|
||||
ShieldCheck,
|
||||
Building2,
|
||||
BookOpen,
|
||||
Paperclip,
|
||||
} from "lucide-react";
|
||||
@@ -73,6 +72,7 @@ export function Sidebar() {
|
||||
<ScrollArea className="flex-1">
|
||||
<nav className="flex flex-col gap-4 px-3 py-2">
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<SidebarNavItem to="/dashboard" label="Dashboard" icon={LayoutDashboard} />
|
||||
<SidebarNavItem
|
||||
to="/inbox"
|
||||
label="Inbox"
|
||||
@@ -89,7 +89,6 @@ export function Sidebar() {
|
||||
</SidebarSection>
|
||||
|
||||
<SidebarSection label="Company">
|
||||
<SidebarNavItem to="/dashboard" label="Dashboard" icon={LayoutDashboard} />
|
||||
<SidebarNavItem to="/agents" label="Agents" icon={Bot} />
|
||||
<SidebarNavItem
|
||||
to="/approvals"
|
||||
@@ -99,7 +98,6 @@ export function Sidebar() {
|
||||
/>
|
||||
<SidebarNavItem to="/costs" label="Costs" icon={DollarSign} />
|
||||
<SidebarNavItem to="/activity" label="Activity" icon={History} />
|
||||
<SidebarNavItem to="/companies" label="Companies" icon={Building2} />
|
||||
</SidebarSection>
|
||||
</nav>
|
||||
</ScrollArea>
|
||||
|
||||
Reference in New Issue
Block a user