Overhaul UI with shadcn components and new pages
Add shadcn/ui components (badge, button, card, input, select, separator). Add company context provider. New pages: Activity, Approvals, Companies, Costs, Org chart. Restyle existing pages (Dashboard, Agents, Issues, Goals, Projects) with shadcn components and dark theme. Update layout, sidebar navigation, and routing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
6
ui/src/api/activity.ts
Normal file
6
ui/src/api/activity.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import type { ActivityEvent } from "@paperclip/shared";
|
||||
import { api } from "./client";
|
||||
|
||||
export const activityApi = {
|
||||
list: (companyId: string) => api.get<ActivityEvent[]>(`/companies/${companyId}/activity`),
|
||||
};
|
||||
@@ -1,10 +1,24 @@
|
||||
import type { Agent } from "@paperclip/shared";
|
||||
import type { Agent, AgentKeyCreated, HeartbeatRun } from "@paperclip/shared";
|
||||
import { api } from "./client";
|
||||
|
||||
export interface OrgNode {
|
||||
id: string;
|
||||
name: string;
|
||||
role: string;
|
||||
status: string;
|
||||
reports: OrgNode[];
|
||||
}
|
||||
|
||||
export const agentsApi = {
|
||||
list: () => api.get<Agent[]>("/agents"),
|
||||
list: (companyId: string) => api.get<Agent[]>(`/companies/${companyId}/agents`),
|
||||
org: (companyId: string) => api.get<OrgNode[]>(`/companies/${companyId}/org`),
|
||||
get: (id: string) => api.get<Agent>(`/agents/${id}`),
|
||||
create: (data: Partial<Agent>) => api.post<Agent>("/agents", data),
|
||||
update: (id: string, data: Partial<Agent>) => api.patch<Agent>(`/agents/${id}`, data),
|
||||
remove: (id: string) => api.delete<Agent>(`/agents/${id}`),
|
||||
create: (companyId: string, data: Record<string, unknown>) =>
|
||||
api.post<Agent>(`/companies/${companyId}/agents`, data),
|
||||
update: (id: string, data: Record<string, unknown>) => api.patch<Agent>(`/agents/${id}`, 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`, {}),
|
||||
createKey: (id: string, name: string) => api.post<AgentKeyCreated>(`/agents/${id}/keys`, { name }),
|
||||
invoke: (id: string) => api.post<HeartbeatRun>(`/agents/${id}/heartbeat/invoke`, {}),
|
||||
};
|
||||
|
||||
15
ui/src/api/approvals.ts
Normal file
15
ui/src/api/approvals.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import type { Approval } from "@paperclip/shared";
|
||||
import { api } from "./client";
|
||||
|
||||
export const approvalsApi = {
|
||||
list: (companyId: string, status?: string) =>
|
||||
api.get<Approval[]>(
|
||||
`/companies/${companyId}/approvals${status ? `?status=${encodeURIComponent(status)}` : ""}`,
|
||||
),
|
||||
create: (companyId: string, data: Record<string, unknown>) =>
|
||||
api.post<Approval>(`/companies/${companyId}/approvals`, data),
|
||||
approve: (id: string, decisionNote?: string) =>
|
||||
api.post<Approval>(`/approvals/${id}/approve`, { decisionNote }),
|
||||
reject: (id: string, decisionNote?: string) =>
|
||||
api.post<Approval>(`/approvals/${id}/reject`, { decisionNote }),
|
||||
};
|
||||
14
ui/src/api/companies.ts
Normal file
14
ui/src/api/companies.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { Company } from "@paperclip/shared";
|
||||
import { api } from "./client";
|
||||
|
||||
export const companiesApi = {
|
||||
list: () => api.get<Company[]>("/companies"),
|
||||
get: (companyId: string) => api.get<Company>(`/companies/${companyId}`),
|
||||
create: (data: { name: string; description?: string | null; budgetMonthlyCents?: number }) =>
|
||||
api.post<Company>("/companies", data),
|
||||
update: (
|
||||
companyId: string,
|
||||
data: Partial<Pick<Company, "name" | "description" | "status" | "budgetMonthlyCents">>,
|
||||
) => api.patch<Company>(`/companies/${companyId}`, data),
|
||||
archive: (companyId: string) => api.post<Company>(`/companies/${companyId}/archive`, {}),
|
||||
};
|
||||
18
ui/src/api/costs.ts
Normal file
18
ui/src/api/costs.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { CostSummary } from "@paperclip/shared";
|
||||
import { api } from "./client";
|
||||
|
||||
export interface CostByEntity {
|
||||
agentId?: string | null;
|
||||
projectId?: string | null;
|
||||
costCents: number;
|
||||
inputTokens: number;
|
||||
outputTokens: number;
|
||||
}
|
||||
|
||||
export const costsApi = {
|
||||
summary: (companyId: string) => api.get<CostSummary>(`/companies/${companyId}/costs/summary`),
|
||||
byAgent: (companyId: string) =>
|
||||
api.get<CostByEntity[]>(`/companies/${companyId}/costs/by-agent`),
|
||||
byProject: (companyId: string) =>
|
||||
api.get<CostByEntity[]>(`/companies/${companyId}/costs/by-project`),
|
||||
};
|
||||
6
ui/src/api/dashboard.ts
Normal file
6
ui/src/api/dashboard.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import type { DashboardSummary } from "@paperclip/shared";
|
||||
import { api } from "./client";
|
||||
|
||||
export const dashboardApi = {
|
||||
summary: (companyId: string) => api.get<DashboardSummary>(`/companies/${companyId}/dashboard`),
|
||||
};
|
||||
@@ -2,9 +2,10 @@ import type { Goal } from "@paperclip/shared";
|
||||
import { api } from "./client";
|
||||
|
||||
export const goalsApi = {
|
||||
list: () => api.get<Goal[]>("/goals"),
|
||||
list: (companyId: string) => api.get<Goal[]>(`/companies/${companyId}/goals`),
|
||||
get: (id: string) => api.get<Goal>(`/goals/${id}`),
|
||||
create: (data: Partial<Goal>) => api.post<Goal>("/goals", data),
|
||||
update: (id: string, data: Partial<Goal>) => api.patch<Goal>(`/goals/${id}`, data),
|
||||
create: (companyId: string, data: Record<string, unknown>) =>
|
||||
api.post<Goal>(`/companies/${companyId}/goals`, data),
|
||||
update: (id: string, data: Record<string, unknown>) => api.patch<Goal>(`/goals/${id}`, data),
|
||||
remove: (id: string) => api.delete<Goal>(`/goals/${id}`),
|
||||
};
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
export { api } from "./client";
|
||||
export { companiesApi } from "./companies";
|
||||
export { agentsApi } from "./agents";
|
||||
export { projectsApi } from "./projects";
|
||||
export { issuesApi } from "./issues";
|
||||
export { goalsApi } from "./goals";
|
||||
export { approvalsApi } from "./approvals";
|
||||
export { costsApi } from "./costs";
|
||||
export { activityApi } from "./activity";
|
||||
export { dashboardApi } from "./dashboard";
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
import type { Issue } from "@paperclip/shared";
|
||||
import type { Issue, IssueComment } from "@paperclip/shared";
|
||||
import { api } from "./client";
|
||||
|
||||
export const issuesApi = {
|
||||
list: () => api.get<Issue[]>("/issues"),
|
||||
list: (companyId: string) => api.get<Issue[]>(`/companies/${companyId}/issues`),
|
||||
get: (id: string) => api.get<Issue>(`/issues/${id}`),
|
||||
create: (data: Partial<Issue>) => api.post<Issue>("/issues", data),
|
||||
update: (id: string, data: Partial<Issue>) => api.patch<Issue>(`/issues/${id}`, data),
|
||||
create: (companyId: string, data: Record<string, unknown>) =>
|
||||
api.post<Issue>(`/companies/${companyId}/issues`, data),
|
||||
update: (id: string, data: Record<string, unknown>) => api.patch<Issue>(`/issues/${id}`, data),
|
||||
remove: (id: string) => api.delete<Issue>(`/issues/${id}`),
|
||||
checkout: (id: string, agentId: string) =>
|
||||
api.post<Issue>(`/issues/${id}/checkout`, {
|
||||
agentId,
|
||||
expectedStatuses: ["todo", "backlog", "blocked"],
|
||||
}),
|
||||
release: (id: string) => api.post<Issue>(`/issues/${id}/release`, {}),
|
||||
listComments: (id: string) => api.get<IssueComment[]>(`/issues/${id}/comments`),
|
||||
addComment: (id: string, body: string) => api.post<IssueComment>(`/issues/${id}/comments`, { body }),
|
||||
};
|
||||
|
||||
@@ -2,9 +2,10 @@ import type { Project } from "@paperclip/shared";
|
||||
import { api } from "./client";
|
||||
|
||||
export const projectsApi = {
|
||||
list: () => api.get<Project[]>("/projects"),
|
||||
list: (companyId: string) => api.get<Project[]>(`/companies/${companyId}/projects`),
|
||||
get: (id: string) => api.get<Project>(`/projects/${id}`),
|
||||
create: (data: Partial<Project>) => api.post<Project>("/projects", data),
|
||||
update: (id: string, data: Partial<Project>) => api.patch<Project>(`/projects/${id}`, data),
|
||||
create: (companyId: string, data: Record<string, unknown>) =>
|
||||
api.post<Project>(`/companies/${companyId}/projects`, data),
|
||||
update: (id: string, data: Record<string, unknown>) => api.patch<Project>(`/projects/${id}`, data),
|
||||
remove: (id: string) => api.delete<Project>(`/projects/${id}`),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user