forked from farhoodlabs/paperclip
Add React UI with Vite
Dashboard, agents, goals, issues, and projects pages with sidebar navigation. API client layer, custom hooks, and shared layout components. Built with Vite and TypeScript. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import { useAgents } from "../hooks/useAgents";
|
||||
import { StatusBadge } from "../components/StatusBadge";
|
||||
import { formatCents } from "../lib/utils";
|
||||
|
||||
export function Agents() {
|
||||
const { data: agents, loading, error } = useAgents();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4">Agents</h2>
|
||||
{loading && <p className="text-gray-500">Loading...</p>}
|
||||
{error && <p className="text-red-600">{error.message}</p>}
|
||||
{agents && agents.length === 0 && <p className="text-gray-500">No agents yet.</p>}
|
||||
{agents && agents.length > 0 && (
|
||||
<div className="grid gap-4">
|
||||
{agents.map((agent) => (
|
||||
<div key={agent.id} className="bg-white rounded-lg border border-gray-200 p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 className="font-semibold">{agent.name}</h3>
|
||||
<p className="text-sm text-gray-500">{agent.role}</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-sm text-gray-500">
|
||||
{formatCents(agent.spentCents)} / {formatCents(agent.budgetCents)}
|
||||
</span>
|
||||
<StatusBadge status={agent.status} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export function Dashboard() {
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4">Dashboard</h2>
|
||||
<p className="text-gray-600">Welcome to Paperclip. Select a section from the sidebar.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { useCallback } from "react";
|
||||
import { goalsApi } from "../api/goals";
|
||||
import { useApi } from "../hooks/useApi";
|
||||
import { cn } from "../lib/utils";
|
||||
|
||||
const levelColors: Record<string, string> = {
|
||||
company: "bg-purple-100 text-purple-800",
|
||||
team: "bg-blue-100 text-blue-800",
|
||||
agent: "bg-indigo-100 text-indigo-800",
|
||||
task: "bg-gray-100 text-gray-600",
|
||||
};
|
||||
|
||||
export function Goals() {
|
||||
const fetcher = useCallback(() => goalsApi.list(), []);
|
||||
const { data: goals, loading, error } = useApi(fetcher);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4">Goals</h2>
|
||||
{loading && <p className="text-gray-500">Loading...</p>}
|
||||
{error && <p className="text-red-600">{error.message}</p>}
|
||||
{goals && goals.length === 0 && <p className="text-gray-500">No goals yet.</p>}
|
||||
{goals && goals.length > 0 && (
|
||||
<div className="grid gap-4">
|
||||
{goals.map((goal) => (
|
||||
<div key={goal.id} className="bg-white rounded-lg border border-gray-200 p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 className="font-semibold">{goal.title}</h3>
|
||||
{goal.description && (
|
||||
<p className="text-sm text-gray-500 mt-1">{goal.description}</p>
|
||||
)}
|
||||
</div>
|
||||
<span
|
||||
className={cn(
|
||||
"inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium",
|
||||
levelColors[goal.level] ?? "bg-gray-100 text-gray-600"
|
||||
)}
|
||||
>
|
||||
{goal.level}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import { useCallback } from "react";
|
||||
import { issuesApi } from "../api/issues";
|
||||
import { useApi } from "../hooks/useApi";
|
||||
import { StatusBadge } from "../components/StatusBadge";
|
||||
import { cn } from "../lib/utils";
|
||||
|
||||
const priorityColors: Record<string, string> = {
|
||||
critical: "text-red-700 bg-red-50",
|
||||
high: "text-orange-700 bg-orange-50",
|
||||
medium: "text-yellow-700 bg-yellow-50",
|
||||
low: "text-gray-600 bg-gray-50",
|
||||
};
|
||||
|
||||
export function Issues() {
|
||||
const fetcher = useCallback(() => issuesApi.list(), []);
|
||||
const { data: issues, loading, error } = useApi(fetcher);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4">Issues</h2>
|
||||
{loading && <p className="text-gray-500">Loading...</p>}
|
||||
{error && <p className="text-red-600">{error.message}</p>}
|
||||
{issues && issues.length === 0 && <p className="text-gray-500">No issues yet.</p>}
|
||||
{issues && issues.length > 0 && (
|
||||
<div className="grid gap-4">
|
||||
{issues.map((issue) => (
|
||||
<div key={issue.id} className="bg-white rounded-lg border border-gray-200 p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 className="font-semibold">{issue.title}</h3>
|
||||
{issue.description && (
|
||||
<p className="text-sm text-gray-500 mt-1 line-clamp-1">
|
||||
{issue.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<span
|
||||
className={cn(
|
||||
"inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium",
|
||||
priorityColors[issue.priority] ?? "text-gray-600 bg-gray-50"
|
||||
)}
|
||||
>
|
||||
{issue.priority}
|
||||
</span>
|
||||
<StatusBadge status={issue.status} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { useCallback } from "react";
|
||||
import { projectsApi } from "../api/projects";
|
||||
import { useApi } from "../hooks/useApi";
|
||||
import { formatDate } from "../lib/utils";
|
||||
|
||||
export function Projects() {
|
||||
const fetcher = useCallback(() => projectsApi.list(), []);
|
||||
const { data: projects, loading, error } = useApi(fetcher);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4">Projects</h2>
|
||||
{loading && <p className="text-gray-500">Loading...</p>}
|
||||
{error && <p className="text-red-600">{error.message}</p>}
|
||||
{projects && projects.length === 0 && <p className="text-gray-500">No projects yet.</p>}
|
||||
{projects && projects.length > 0 && (
|
||||
<div className="grid gap-4">
|
||||
{projects.map((project) => (
|
||||
<div key={project.id} className="bg-white rounded-lg border border-gray-200 p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 className="font-semibold">{project.name}</h3>
|
||||
{project.description && (
|
||||
<p className="text-sm text-gray-500 mt-1">{project.description}</p>
|
||||
)}
|
||||
</div>
|
||||
<span className="text-sm text-gray-400">{formatDate(project.createdAt)}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user