feat: update ProjectE application

This commit is contained in:
2026-07-18 19:05:52 -04:00
parent 8f55626e03
commit 4c1cc50231
68 changed files with 1586 additions and 697 deletions
+105 -30
View File
@@ -43,7 +43,14 @@ export default function AgentsPage() {
const [agents, setAgents] = useState<Agent[]>([]);
const [activity, setActivity] = useState<AgentActivity[]>([]);
const [agentTasks, setAgentTasks] = useState<AgentTask[]>([]);
const [loading, setLoading] = useState(true);
const [agentsLoading, setAgentsLoading] = useState(true);
const [activityLoading, setActivityLoading] = useState(true);
const [agentTasksLoading, setAgentTasksLoading] = useState(true);
const [agentsError, setAgentsError] = useState<string | null>(null);
const [activityError, setActivityError] = useState<string | null>(null);
const [agentTasksError, setAgentTasksError] = useState<string | null>(null);
const [undoingActivityId, setUndoingActivityId] = useState<string | null>(null);
const [feedback, setFeedback] = useState<{ type: 'success' | 'error'; message: string } | null>(null);
const [selectedAgent, setSelectedAgent] = useState<Agent | null>(null);
useEffect(() => {
@@ -53,51 +60,76 @@ export default function AgentsPage() {
}, []);
async function fetchAgents() {
setAgentsLoading(true);
setAgentsError(null);
try {
const response = await fetch('/api/agents');
if (response.ok) {
const data = await response.json();
setAgents(data.items || []);
if (!response.ok) {
throw new Error('Unable to load agents.');
}
const data = await response.json();
setAgents(data.items || []);
} catch (error) {
console.error('Failed to fetch agents:', error);
setAgentsError('Unable to load agents. Please try again.');
} finally {
setLoading(false);
setAgentsLoading(false);
}
}
async function fetchActivity() {
setActivityLoading(true);
setActivityError(null);
try {
const response = await fetch('/api/agent-activity?sort=-created&perPage=50');
if (response.ok) {
const data = await response.json();
setActivity(data.items || []);
if (!response.ok) {
throw new Error('Unable to load agent activity.');
}
const data = await response.json();
setActivity(data.items || []);
} catch (error) {
console.error('Failed to fetch activity:', error);
setActivityError('Unable to load agent activity. Please try again.');
} finally {
setActivityLoading(false);
}
}
async function fetchAgentTasks() {
setAgentTasksLoading(true);
setAgentTasksError(null);
try {
const response = await fetch('/api/agent-tasks?sort=-created&perPage=50');
if (response.ok) {
const data = await response.json();
setAgentTasks(data.items || []);
if (!response.ok) {
throw new Error('Unable to load agent tasks.');
}
const data = await response.json();
setAgentTasks(data.items || []);
} catch (error) {
console.error('Failed to fetch agent tasks:', error);
setAgentTasksError('Unable to load agent tasks. Please try again.');
} finally {
setAgentTasksLoading(false);
}
}
async function undoActivity(activityId: string) {
setUndoingActivityId(activityId);
setFeedback(null);
try {
await fetch(`/api/agent-activity/${activityId}/undo`, {
const response = await fetch(`/api/agent-activity/${activityId}/undo`, {
method: 'POST',
});
fetchActivity();
if (!response.ok) {
throw new Error('Unable to undo this activity.');
}
setFeedback({ type: 'success', message: 'Activity undone successfully.' });
await fetchActivity();
} catch (error) {
console.error('Failed to undo activity:', error);
setFeedback({ type: 'error', message: 'Unable to undo this activity. Please try again.' });
} finally {
setUndoingActivityId(null);
}
}
@@ -130,10 +162,6 @@ export default function AgentsPage() {
return labels[action] || action;
}
if (loading) {
return <p className="text-muted-foreground">Loading agent activity...</p>;
}
return (
<div>
<div className="mb-6">
@@ -141,6 +169,16 @@ export default function AgentsPage() {
<p className="mt-1 text-muted-foreground">
Every agent action, visible and reversible.
</p>
{feedback && (
<p
className={`mt-3 text-sm ${
feedback.type === 'success' ? 'text-green-600' : 'text-red-600'
}`}
role={feedback.type === 'error' ? 'alert' : 'status'}
>
{feedback.message}
</p>
)}
</div>
<div className="grid grid-cols-1 gap-6 lg:grid-cols-[300px_1fr]">
@@ -150,10 +188,22 @@ export default function AgentsPage() {
<CardTitle className="text-base">Agents</CardTitle>
</CardHeader>
<CardContent>
{agents.length === 0 ? (
<p className="py-8 text-center text-sm text-muted-foreground">
No agents configured
</p>
{agentsLoading ? (
<p className="py-8 text-center text-sm text-muted-foreground">Loading agents...</p>
) : agentsError ? (
<div className="py-8 text-center">
<p className="text-sm text-red-600" role="alert">{agentsError}</p>
<Button variant="outline" size="sm" className="mt-3" onClick={fetchAgents}>
Retry
</Button>
</div>
) : agents.length === 0 ? (
<div className="py-8 text-center">
<p className="text-sm text-muted-foreground">No agents configured</p>
<Button variant="outline" size="sm" className="mt-3" onClick={fetchAgents}>
Refresh agents
</Button>
</div>
) : (
<div className="space-y-2">
{agents.map((agent) => (
@@ -217,10 +267,22 @@ export default function AgentsPage() {
</TabsList>
<TabsContent value="activity" className="mt-4">
{activity.length === 0 ? (
<p className="py-8 text-center text-muted-foreground">
No agent activity yet
</p>
{activityLoading ? (
<p className="py-8 text-center text-muted-foreground">Loading agent activity...</p>
) : activityError ? (
<div className="py-8 text-center">
<p className="text-sm text-red-600" role="alert">{activityError}</p>
<Button variant="outline" size="sm" className="mt-3" onClick={fetchActivity}>
Retry
</Button>
</div>
) : activity.length === 0 ? (
<div className="py-8 text-center">
<p className="text-muted-foreground">No agent activity yet</p>
<Button variant="outline" size="sm" className="mt-3" onClick={fetchActivity}>
Refresh activity
</Button>
</div>
) : (
<div className="space-y-3">
{activity.map((item) => (
@@ -277,11 +339,12 @@ export default function AgentsPage() {
variant="ghost"
size="sm"
onClick={() => undoActivity(item.id)}
disabled={undoingActivityId !== null}
className="shrink-0"
aria-label={`Undo activity: ${getActionLabel(item.action)} ${item.entity_type}`}
>
<RotateCcw className="mr-1 h-3 w-3" aria-hidden="true" />
Undo
{undoingActivityId === item.id ? 'Undoing...' : 'Undo'}
</Button>
</div>
</div>
@@ -291,10 +354,22 @@ export default function AgentsPage() {
</TabsContent>
<TabsContent value="tasks" className="mt-4">
{agentTasks.length === 0 ? (
<p className="py-8 text-center text-muted-foreground">
No agent tasks yet
</p>
{agentTasksLoading ? (
<p className="py-8 text-center text-muted-foreground">Loading agent tasks...</p>
) : agentTasksError ? (
<div className="py-8 text-center">
<p className="text-sm text-red-600" role="alert">{agentTasksError}</p>
<Button variant="outline" size="sm" className="mt-3" onClick={fetchAgentTasks}>
Retry
</Button>
</div>
) : agentTasks.length === 0 ? (
<div className="py-8 text-center">
<p className="text-muted-foreground">No agent tasks yet</p>
<Button variant="outline" size="sm" className="mt-3" onClick={fetchAgentTasks}>
Refresh tasks
</Button>
</div>
) : (
<div className="space-y-3">
{agentTasks.map((task) => (