stagent 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +191 -0
- package/README.md +399 -0
- package/components.json +21 -0
- package/dist/cli.js +171 -0
- package/drizzle.config.ts +12 -0
- package/next.config.mjs +15 -0
- package/package.json +114 -0
- package/postcss.config.mjs +8 -0
- package/public/icon-512.png +0 -0
- package/public/icon.svg +13 -0
- package/public/readme/home-workspace.png +0 -0
- package/public/readme/inbox-approvals.png +0 -0
- package/public/readme/workflow-blueprints.png +0 -0
- package/public/stagent-s-128.png +0 -0
- package/public/stagent-s-64.png +0 -0
- package/src/app/api/blueprints/[id]/instantiate/route.ts +27 -0
- package/src/app/api/blueprints/[id]/route.ts +39 -0
- package/src/app/api/blueprints/import/route.ts +68 -0
- package/src/app/api/blueprints/route.ts +29 -0
- package/src/app/api/command-palette/recent/route.ts +31 -0
- package/src/app/api/data/clear/route.ts +22 -0
- package/src/app/api/data/seed/route.ts +22 -0
- package/src/app/api/documents/[id]/file/route.ts +44 -0
- package/src/app/api/documents/[id]/route.ts +123 -0
- package/src/app/api/documents/route.ts +59 -0
- package/src/app/api/logs/stream/route.ts +101 -0
- package/src/app/api/notifications/[id]/route.ts +36 -0
- package/src/app/api/notifications/mark-all-read/route.ts +13 -0
- package/src/app/api/notifications/pending-approvals/route.ts +10 -0
- package/src/app/api/notifications/pending-approvals/stream/route.ts +101 -0
- package/src/app/api/notifications/route.ts +34 -0
- package/src/app/api/permissions/route.ts +46 -0
- package/src/app/api/profiles/[id]/route.ts +79 -0
- package/src/app/api/profiles/[id]/test/route.ts +42 -0
- package/src/app/api/profiles/import/route.ts +108 -0
- package/src/app/api/profiles/route.ts +50 -0
- package/src/app/api/projects/[id]/route.ts +72 -0
- package/src/app/api/projects/route.ts +53 -0
- package/src/app/api/schedules/[id]/route.ts +185 -0
- package/src/app/api/schedules/route.ts +117 -0
- package/src/app/api/settings/budgets/route.ts +24 -0
- package/src/app/api/settings/openai/route.ts +24 -0
- package/src/app/api/settings/route.ts +21 -0
- package/src/app/api/settings/test/route.ts +26 -0
- package/src/app/api/tasks/[id]/cancel/route.ts +21 -0
- package/src/app/api/tasks/[id]/execute/route.ts +90 -0
- package/src/app/api/tasks/[id]/logs/route.ts +95 -0
- package/src/app/api/tasks/[id]/output/route.ts +47 -0
- package/src/app/api/tasks/[id]/respond/route.ts +64 -0
- package/src/app/api/tasks/[id]/resume/route.ts +76 -0
- package/src/app/api/tasks/[id]/route.ts +77 -0
- package/src/app/api/tasks/assist/route.ts +35 -0
- package/src/app/api/tasks/route.ts +82 -0
- package/src/app/api/uploads/[id]/route.ts +81 -0
- package/src/app/api/uploads/cleanup/route.ts +7 -0
- package/src/app/api/uploads/route.ts +66 -0
- package/src/app/api/workflows/[id]/execute/route.ts +82 -0
- package/src/app/api/workflows/[id]/route.ts +133 -0
- package/src/app/api/workflows/[id]/status/route.ts +54 -0
- package/src/app/api/workflows/[id]/steps/[stepId]/retry/route.ts +22 -0
- package/src/app/api/workflows/route.ts +61 -0
- package/src/app/apple-icon.tsx +31 -0
- package/src/app/costs/page.tsx +256 -0
- package/src/app/dashboard/page.tsx +44 -0
- package/src/app/documents/[id]/page.tsx +46 -0
- package/src/app/documents/page.tsx +45 -0
- package/src/app/error.tsx +26 -0
- package/src/app/global-error.tsx +23 -0
- package/src/app/globals.css +733 -0
- package/src/app/icon.tsx +30 -0
- package/src/app/inbox/loading.tsx +15 -0
- package/src/app/inbox/page.tsx +35 -0
- package/src/app/layout.tsx +78 -0
- package/src/app/manifest.ts +32 -0
- package/src/app/monitor/page.tsx +37 -0
- package/src/app/page.tsx +162 -0
- package/src/app/profiles/[id]/edit/page.tsx +39 -0
- package/src/app/profiles/[id]/page.tsx +33 -0
- package/src/app/profiles/new/page.tsx +22 -0
- package/src/app/profiles/page.tsx +19 -0
- package/src/app/projects/[id]/page.tsx +134 -0
- package/src/app/projects/loading.tsx +17 -0
- package/src/app/projects/page.tsx +32 -0
- package/src/app/schedules/[id]/page.tsx +47 -0
- package/src/app/schedules/page.tsx +18 -0
- package/src/app/settings/loading.tsx +24 -0
- package/src/app/settings/page.tsx +27 -0
- package/src/app/tasks/[id]/page.tsx +45 -0
- package/src/app/tasks/new/page.tsx +27 -0
- package/src/app/workflows/[id]/edit/page.tsx +66 -0
- package/src/app/workflows/[id]/page.tsx +37 -0
- package/src/app/workflows/blueprints/[id]/page.tsx +40 -0
- package/src/app/workflows/blueprints/new/page.tsx +20 -0
- package/src/app/workflows/blueprints/page.tsx +11 -0
- package/src/app/workflows/new/page.tsx +36 -0
- package/src/app/workflows/page.tsx +18 -0
- package/src/components/charts/donut-ring.tsx +64 -0
- package/src/components/charts/mini-bar.tsx +75 -0
- package/src/components/charts/sparkline.tsx +107 -0
- package/src/components/costs/cost-dashboard.tsx +877 -0
- package/src/components/costs/cost-filters.tsx +179 -0
- package/src/components/dashboard/activity-feed.tsx +95 -0
- package/src/components/dashboard/greeting.tsx +30 -0
- package/src/components/dashboard/priority-queue.tsx +79 -0
- package/src/components/dashboard/quick-actions.tsx +62 -0
- package/src/components/dashboard/recent-projects.tsx +79 -0
- package/src/components/dashboard/stats-cards.tsx +114 -0
- package/src/components/documents/document-browser.tsx +235 -0
- package/src/components/documents/document-detail-view.tsx +367 -0
- package/src/components/documents/document-grid.tsx +78 -0
- package/src/components/documents/document-preview.tsx +68 -0
- package/src/components/documents/document-table.tsx +119 -0
- package/src/components/documents/document-upload-dialog.tsx +153 -0
- package/src/components/documents/types.ts +6 -0
- package/src/components/documents/utils.ts +57 -0
- package/src/components/monitoring/connection-indicator.tsx +14 -0
- package/src/components/monitoring/log-entry.tsx +79 -0
- package/src/components/monitoring/log-filters.tsx +57 -0
- package/src/components/monitoring/log-stream.tsx +144 -0
- package/src/components/monitoring/monitor-overview-wrapper.tsx +64 -0
- package/src/components/monitoring/monitor-overview.tsx +119 -0
- package/src/components/notifications/failure-action.tsx +38 -0
- package/src/components/notifications/inbox-list.tsx +165 -0
- package/src/components/notifications/message-response.tsx +196 -0
- package/src/components/notifications/notification-item.tsx +250 -0
- package/src/components/notifications/pending-approval-host.tsx +478 -0
- package/src/components/notifications/permission-action.tsx +37 -0
- package/src/components/notifications/permission-response-actions.tsx +126 -0
- package/src/components/notifications/unread-badge.tsx +35 -0
- package/src/components/profiles/profile-browser.tsx +117 -0
- package/src/components/profiles/profile-card.tsx +78 -0
- package/src/components/profiles/profile-detail-view.tsx +564 -0
- package/src/components/profiles/profile-form-view.tsx +480 -0
- package/src/components/profiles/profile-import-dialog.tsx +113 -0
- package/src/components/projects/project-card.tsx +58 -0
- package/src/components/projects/project-create-dialog.tsx +140 -0
- package/src/components/projects/project-detail.tsx +68 -0
- package/src/components/projects/project-edit-dialog.tsx +219 -0
- package/src/components/projects/project-list.tsx +108 -0
- package/src/components/schedules/schedule-create-dialog.tsx +403 -0
- package/src/components/schedules/schedule-detail-view.tsx +274 -0
- package/src/components/schedules/schedule-list.tsx +242 -0
- package/src/components/schedules/schedule-status-badge.tsx +16 -0
- package/src/components/settings/api-key-form.tsx +141 -0
- package/src/components/settings/auth-config-section.tsx +141 -0
- package/src/components/settings/auth-method-selector.tsx +67 -0
- package/src/components/settings/auth-status-badge.tsx +40 -0
- package/src/components/settings/auth-status-dot.tsx +59 -0
- package/src/components/settings/budget-guardrails-section.tsx +842 -0
- package/src/components/settings/data-management-section.tsx +141 -0
- package/src/components/settings/openai-runtime-section.tsx +104 -0
- package/src/components/settings/permissions-section.tsx +91 -0
- package/src/components/shared/app-sidebar.tsx +123 -0
- package/src/components/shared/card-skeleton.tsx +42 -0
- package/src/components/shared/command-palette.tsx +250 -0
- package/src/components/shared/confirm-dialog.tsx +52 -0
- package/src/components/shared/empty-state.tsx +24 -0
- package/src/components/shared/error-state.tsx +32 -0
- package/src/components/shared/form-section-card.tsx +33 -0
- package/src/components/shared/section-heading.tsx +14 -0
- package/src/components/shared/stagent-logo.tsx +21 -0
- package/src/components/shared/theme-toggle.tsx +46 -0
- package/src/components/tasks/ai-assist-panel.tsx +210 -0
- package/src/components/tasks/content-preview.tsx +89 -0
- package/src/components/tasks/empty-board.tsx +12 -0
- package/src/components/tasks/file-upload.tsx +120 -0
- package/src/components/tasks/kanban-board.tsx +275 -0
- package/src/components/tasks/kanban-column.tsx +75 -0
- package/src/components/tasks/skeleton-board.tsx +21 -0
- package/src/components/tasks/task-attachments.tsx +114 -0
- package/src/components/tasks/task-card.tsx +101 -0
- package/src/components/tasks/task-create-panel.tsx +360 -0
- package/src/components/tasks/task-detail-view.tsx +356 -0
- package/src/components/ui/alert-dialog.tsx +196 -0
- package/src/components/ui/badge.tsx +50 -0
- package/src/components/ui/button.tsx +71 -0
- package/src/components/ui/card.tsx +92 -0
- package/src/components/ui/checkbox.tsx +32 -0
- package/src/components/ui/command.tsx +184 -0
- package/src/components/ui/dialog.tsx +158 -0
- package/src/components/ui/dropdown-menu.tsx +257 -0
- package/src/components/ui/form.tsx +167 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/label.tsx +24 -0
- package/src/components/ui/popover.tsx +89 -0
- package/src/components/ui/progress.tsx +31 -0
- package/src/components/ui/radio-group.tsx +45 -0
- package/src/components/ui/scroll-area.tsx +58 -0
- package/src/components/ui/select.tsx +190 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/sheet.tsx +143 -0
- package/src/components/ui/sidebar.tsx +726 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/slider.tsx +63 -0
- package/src/components/ui/sonner.tsx +36 -0
- package/src/components/ui/switch.tsx +35 -0
- package/src/components/ui/table.tsx +116 -0
- package/src/components/ui/tabs.tsx +91 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/tooltip.tsx +57 -0
- package/src/components/workflows/blueprint-editor.tsx +109 -0
- package/src/components/workflows/blueprint-gallery.tsx +155 -0
- package/src/components/workflows/blueprint-preview.tsx +240 -0
- package/src/components/workflows/loop-status-view.tsx +272 -0
- package/src/components/workflows/swarm-dashboard.tsx +185 -0
- package/src/components/workflows/workflow-form-view.tsx +1376 -0
- package/src/components/workflows/workflow-list.tsx +230 -0
- package/src/components/workflows/workflow-status-view.tsx +477 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/instrumentation.ts +7 -0
- package/src/lib/agents/claude-agent.ts +737 -0
- package/src/lib/agents/execution-manager.ts +27 -0
- package/src/lib/agents/profiles/assignment-validation.ts +75 -0
- package/src/lib/agents/profiles/builtins/code-reviewer/SKILL.md +21 -0
- package/src/lib/agents/profiles/builtins/code-reviewer/profile.yaml +28 -0
- package/src/lib/agents/profiles/builtins/data-analyst/SKILL.md +25 -0
- package/src/lib/agents/profiles/builtins/data-analyst/profile.yaml +27 -0
- package/src/lib/agents/profiles/builtins/devops-engineer/SKILL.md +34 -0
- package/src/lib/agents/profiles/builtins/devops-engineer/profile.yaml +27 -0
- package/src/lib/agents/profiles/builtins/document-writer/SKILL.md +16 -0
- package/src/lib/agents/profiles/builtins/document-writer/profile.yaml +27 -0
- package/src/lib/agents/profiles/builtins/general/SKILL.md +13 -0
- package/src/lib/agents/profiles/builtins/general/profile.yaml +18 -0
- package/src/lib/agents/profiles/builtins/health-fitness-coach/SKILL.md +34 -0
- package/src/lib/agents/profiles/builtins/health-fitness-coach/profile.yaml +26 -0
- package/src/lib/agents/profiles/builtins/learning-coach/SKILL.md +35 -0
- package/src/lib/agents/profiles/builtins/learning-coach/profile.yaml +26 -0
- package/src/lib/agents/profiles/builtins/project-manager/SKILL.md +26 -0
- package/src/lib/agents/profiles/builtins/project-manager/profile.yaml +26 -0
- package/src/lib/agents/profiles/builtins/researcher/SKILL.md +15 -0
- package/src/lib/agents/profiles/builtins/researcher/profile.yaml +27 -0
- package/src/lib/agents/profiles/builtins/shopping-assistant/SKILL.md +34 -0
- package/src/lib/agents/profiles/builtins/shopping-assistant/profile.yaml +26 -0
- package/src/lib/agents/profiles/builtins/technical-writer/SKILL.md +31 -0
- package/src/lib/agents/profiles/builtins/technical-writer/profile.yaml +29 -0
- package/src/lib/agents/profiles/builtins/travel-planner/SKILL.md +23 -0
- package/src/lib/agents/profiles/builtins/travel-planner/profile.yaml +26 -0
- package/src/lib/agents/profiles/builtins/wealth-manager/SKILL.md +24 -0
- package/src/lib/agents/profiles/builtins/wealth-manager/profile.yaml +26 -0
- package/src/lib/agents/profiles/compatibility.ts +109 -0
- package/src/lib/agents/profiles/registry.ts +293 -0
- package/src/lib/agents/profiles/test-runner.ts +18 -0
- package/src/lib/agents/profiles/test-types.ts +20 -0
- package/src/lib/agents/profiles/types.ts +43 -0
- package/src/lib/agents/router.ts +56 -0
- package/src/lib/agents/runtime/catalog.ts +85 -0
- package/src/lib/agents/runtime/claude-sdk.ts +12 -0
- package/src/lib/agents/runtime/claude.ts +370 -0
- package/src/lib/agents/runtime/codex-app-server-client.ts +289 -0
- package/src/lib/agents/runtime/index.ts +167 -0
- package/src/lib/agents/runtime/openai-codex.ts +1089 -0
- package/src/lib/agents/runtime/task-assist-types.ts +8 -0
- package/src/lib/agents/runtime/types.ts +30 -0
- package/src/lib/constants/settings.ts +13 -0
- package/src/lib/constants/status-colors.ts +44 -0
- package/src/lib/constants/task-status.ts +49 -0
- package/src/lib/data/clear.ts +63 -0
- package/src/lib/data/seed-data/documents.ts +715 -0
- package/src/lib/data/seed-data/logs.ts +195 -0
- package/src/lib/data/seed-data/notifications.ts +141 -0
- package/src/lib/data/seed-data/profiles.ts +175 -0
- package/src/lib/data/seed-data/projects.ts +61 -0
- package/src/lib/data/seed-data/schedules.ts +108 -0
- package/src/lib/data/seed-data/tasks.ts +341 -0
- package/src/lib/data/seed-data/usage-ledger.ts +130 -0
- package/src/lib/data/seed-data/workflows.ts +213 -0
- package/src/lib/data/seed.ts +129 -0
- package/src/lib/db/index.ts +221 -0
- package/src/lib/db/migrations/0000_aromatic_gargoyle.sql +59 -0
- package/src/lib/db/migrations/0001_first_iron_patriot.sql +6 -0
- package/src/lib/db/migrations/0002_add_resume_count.sql +1 -0
- package/src/lib/db/migrations/0003_add_settings.sql +5 -0
- package/src/lib/db/migrations/0004_add_documents.sql +20 -0
- package/src/lib/db/migrations/0005_add_document_preprocessing.sql +4 -0
- package/src/lib/db/migrations/0006_add_agent_profile.sql +2 -0
- package/src/lib/db/migrations/0007_add_usage_metering_ledger.sql +30 -0
- package/src/lib/db/migrations/0008_add_document_version.sql +1 -0
- package/src/lib/db/migrations/meta/0000_snapshot.json +416 -0
- package/src/lib/db/migrations/meta/0001_snapshot.json +461 -0
- package/src/lib/db/migrations/meta/0002_snapshot.json +469 -0
- package/src/lib/db/migrations/meta/_journal.json +27 -0
- package/src/lib/db/schema.ts +227 -0
- package/src/lib/documents/cleanup.ts +50 -0
- package/src/lib/documents/context-builder.ts +75 -0
- package/src/lib/documents/output-scanner.ts +166 -0
- package/src/lib/documents/processor.ts +120 -0
- package/src/lib/documents/processors/image.ts +21 -0
- package/src/lib/documents/processors/office.ts +36 -0
- package/src/lib/documents/processors/pdf.ts +12 -0
- package/src/lib/documents/processors/spreadsheet.ts +18 -0
- package/src/lib/documents/processors/text.ts +8 -0
- package/src/lib/documents/registry.ts +25 -0
- package/src/lib/notifications/actionable.ts +108 -0
- package/src/lib/notifications/permissions.ts +169 -0
- package/src/lib/queries/chart-data.ts +184 -0
- package/src/lib/schedules/interval-parser.ts +110 -0
- package/src/lib/schedules/scheduler.ts +220 -0
- package/src/lib/settings/auth.ts +98 -0
- package/src/lib/settings/budget-guardrails.ts +590 -0
- package/src/lib/settings/helpers.ts +23 -0
- package/src/lib/settings/openai-auth.ts +80 -0
- package/src/lib/settings/permissions.ts +102 -0
- package/src/lib/usage/ledger.ts +489 -0
- package/src/lib/usage/pricing.ts +68 -0
- package/src/lib/utils/crypto.ts +90 -0
- package/src/lib/utils/format-timestamp.ts +46 -0
- package/src/lib/utils/session-cleanup.ts +26 -0
- package/src/lib/utils/stagent-paths.ts +18 -0
- package/src/lib/utils.ts +6 -0
- package/src/lib/validators/blueprint.ts +43 -0
- package/src/lib/validators/profile.ts +64 -0
- package/src/lib/validators/project.ts +17 -0
- package/src/lib/validators/settings.ts +57 -0
- package/src/lib/validators/task.ts +30 -0
- package/src/lib/workflows/blueprints/builtins/code-review-pipeline.yaml +72 -0
- package/src/lib/workflows/blueprints/builtins/documentation-generation.yaml +62 -0
- package/src/lib/workflows/blueprints/builtins/investment-research.yaml +81 -0
- package/src/lib/workflows/blueprints/builtins/meal-planning.yaml +73 -0
- package/src/lib/workflows/blueprints/builtins/product-research.yaml +72 -0
- package/src/lib/workflows/blueprints/builtins/research-report.yaml +77 -0
- package/src/lib/workflows/blueprints/builtins/sprint-planning.yaml +77 -0
- package/src/lib/workflows/blueprints/builtins/travel-planning.yaml +80 -0
- package/src/lib/workflows/blueprints/instantiator.ts +131 -0
- package/src/lib/workflows/blueprints/registry.ts +128 -0
- package/src/lib/workflows/blueprints/template.ts +58 -0
- package/src/lib/workflows/blueprints/types.ts +38 -0
- package/src/lib/workflows/definition-validation.ts +121 -0
- package/src/lib/workflows/engine.ts +1113 -0
- package/src/lib/workflows/loop-executor.ts +270 -0
- package/src/lib/workflows/parallel.ts +55 -0
- package/src/lib/workflows/swarm.ts +97 -0
- package/src/lib/workflows/types.ts +112 -0
- package/tsconfig.json +41 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useTransition } from "react";
|
|
4
|
+
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
|
5
|
+
import { ListFilter, Loader2, RotateCcw } from "lucide-react";
|
|
6
|
+
import { Button } from "@/components/ui/button";
|
|
7
|
+
import {
|
|
8
|
+
Select,
|
|
9
|
+
SelectContent,
|
|
10
|
+
SelectItem,
|
|
11
|
+
SelectTrigger,
|
|
12
|
+
SelectValue,
|
|
13
|
+
} from "@/components/ui/select";
|
|
14
|
+
|
|
15
|
+
interface RuntimeOption {
|
|
16
|
+
id: string;
|
|
17
|
+
label: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface CostFiltersProps {
|
|
21
|
+
dateRange: string;
|
|
22
|
+
runtimeId: string;
|
|
23
|
+
status: string;
|
|
24
|
+
activityType: string;
|
|
25
|
+
runtimeOptions: RuntimeOption[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const DATE_RANGE_OPTIONS = [
|
|
29
|
+
{ value: "7d", label: "Last 7 days" },
|
|
30
|
+
{ value: "30d", label: "Last 30 days" },
|
|
31
|
+
{ value: "90d", label: "Last 90 days" },
|
|
32
|
+
{ value: "all", label: "All time" },
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
const STATUS_OPTIONS = [
|
|
36
|
+
{ value: "all", label: "All statuses" },
|
|
37
|
+
{ value: "completed", label: "Completed" },
|
|
38
|
+
{ value: "failed", label: "Failed" },
|
|
39
|
+
{ value: "cancelled", label: "Cancelled" },
|
|
40
|
+
{ value: "blocked", label: "Blocked" },
|
|
41
|
+
{ value: "unknown_pricing", label: "Unknown pricing" },
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
const ACTIVITY_OPTIONS = [
|
|
45
|
+
{ value: "all", label: "All activities" },
|
|
46
|
+
{ value: "task_run", label: "Task runs" },
|
|
47
|
+
{ value: "task_resume", label: "Task resumes" },
|
|
48
|
+
{ value: "workflow_step", label: "Workflow steps" },
|
|
49
|
+
{ value: "scheduled_firing", label: "Scheduled firings" },
|
|
50
|
+
{ value: "task_assist", label: "Task assist" },
|
|
51
|
+
{ value: "profile_test", label: "Profile tests" },
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
export function CostFilters({
|
|
55
|
+
dateRange,
|
|
56
|
+
runtimeId,
|
|
57
|
+
status,
|
|
58
|
+
activityType,
|
|
59
|
+
runtimeOptions,
|
|
60
|
+
}: CostFiltersProps) {
|
|
61
|
+
const pathname = usePathname();
|
|
62
|
+
const router = useRouter();
|
|
63
|
+
const searchParams = useSearchParams();
|
|
64
|
+
const [isPending, startTransition] = useTransition();
|
|
65
|
+
|
|
66
|
+
function replaceFilters(next: URLSearchParams) {
|
|
67
|
+
const href = next.size > 0 ? `${pathname}?${next.toString()}` : pathname;
|
|
68
|
+
startTransition(() => {
|
|
69
|
+
router.replace(href, { scroll: false });
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function setFilter(
|
|
74
|
+
key: "range" | "runtime" | "status" | "activity",
|
|
75
|
+
value: string
|
|
76
|
+
) {
|
|
77
|
+
const next = new URLSearchParams(searchParams.toString());
|
|
78
|
+
const defaultValue = key === "range" ? "30d" : "all";
|
|
79
|
+
if (value === defaultValue) {
|
|
80
|
+
next.delete(key);
|
|
81
|
+
} else {
|
|
82
|
+
next.set(key, value);
|
|
83
|
+
}
|
|
84
|
+
replaceFilters(next);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function resetFilters() {
|
|
88
|
+
replaceFilters(new URLSearchParams());
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const hasCustomFilters =
|
|
92
|
+
dateRange !== "30d" ||
|
|
93
|
+
runtimeId !== "all" ||
|
|
94
|
+
status !== "all" ||
|
|
95
|
+
activityType !== "all";
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<div className="surface-panel flex flex-col gap-3 rounded-2xl p-4">
|
|
99
|
+
<div className="flex items-center justify-between gap-3">
|
|
100
|
+
<div className="flex items-center gap-2 text-xs font-semibold uppercase tracking-[0.16em] text-muted-foreground">
|
|
101
|
+
<ListFilter className="h-3.5 w-3.5" />
|
|
102
|
+
<span>Audit Filters</span>
|
|
103
|
+
</div>
|
|
104
|
+
<div className="flex items-center gap-2">
|
|
105
|
+
{isPending ? (
|
|
106
|
+
<span className="inline-flex items-center gap-1 text-xs text-muted-foreground">
|
|
107
|
+
<Loader2 className="h-3.5 w-3.5 animate-spin" />
|
|
108
|
+
Updating
|
|
109
|
+
</span>
|
|
110
|
+
) : null}
|
|
111
|
+
{hasCustomFilters ? (
|
|
112
|
+
<Button variant="ghost" size="sm" onClick={resetFilters}>
|
|
113
|
+
<RotateCcw className="h-3.5 w-3.5" />
|
|
114
|
+
Reset
|
|
115
|
+
</Button>
|
|
116
|
+
) : null}
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
<div className="flex flex-col gap-3 lg:flex-row lg:flex-wrap">
|
|
121
|
+
<Select value={dateRange} onValueChange={(value) => setFilter("range", value)}>
|
|
122
|
+
<SelectTrigger className="surface-control w-full justify-between lg:w-[170px]">
|
|
123
|
+
<SelectValue placeholder="Date range" />
|
|
124
|
+
</SelectTrigger>
|
|
125
|
+
<SelectContent>
|
|
126
|
+
{DATE_RANGE_OPTIONS.map((option) => (
|
|
127
|
+
<SelectItem key={option.value} value={option.value}>
|
|
128
|
+
{option.label}
|
|
129
|
+
</SelectItem>
|
|
130
|
+
))}
|
|
131
|
+
</SelectContent>
|
|
132
|
+
</Select>
|
|
133
|
+
|
|
134
|
+
<Select value={runtimeId} onValueChange={(value) => setFilter("runtime", value)}>
|
|
135
|
+
<SelectTrigger className="surface-control w-full justify-between lg:w-[210px]">
|
|
136
|
+
<SelectValue placeholder="All runtimes" />
|
|
137
|
+
</SelectTrigger>
|
|
138
|
+
<SelectContent>
|
|
139
|
+
<SelectItem value="all">All runtimes</SelectItem>
|
|
140
|
+
{runtimeOptions.map((runtime) => (
|
|
141
|
+
<SelectItem key={runtime.id} value={runtime.id}>
|
|
142
|
+
{runtime.label}
|
|
143
|
+
</SelectItem>
|
|
144
|
+
))}
|
|
145
|
+
</SelectContent>
|
|
146
|
+
</Select>
|
|
147
|
+
|
|
148
|
+
<Select value={status} onValueChange={(value) => setFilter("status", value)}>
|
|
149
|
+
<SelectTrigger className="surface-control w-full justify-between lg:w-[180px]">
|
|
150
|
+
<SelectValue placeholder="All statuses" />
|
|
151
|
+
</SelectTrigger>
|
|
152
|
+
<SelectContent>
|
|
153
|
+
{STATUS_OPTIONS.map((option) => (
|
|
154
|
+
<SelectItem key={option.value} value={option.value}>
|
|
155
|
+
{option.label}
|
|
156
|
+
</SelectItem>
|
|
157
|
+
))}
|
|
158
|
+
</SelectContent>
|
|
159
|
+
</Select>
|
|
160
|
+
|
|
161
|
+
<Select
|
|
162
|
+
value={activityType}
|
|
163
|
+
onValueChange={(value) => setFilter("activity", value)}
|
|
164
|
+
>
|
|
165
|
+
<SelectTrigger className="surface-control w-full justify-between lg:w-[190px]">
|
|
166
|
+
<SelectValue placeholder="All activities" />
|
|
167
|
+
</SelectTrigger>
|
|
168
|
+
<SelectContent>
|
|
169
|
+
{ACTIVITY_OPTIONS.map((option) => (
|
|
170
|
+
<SelectItem key={option.value} value={option.value}>
|
|
171
|
+
{option.label}
|
|
172
|
+
</SelectItem>
|
|
173
|
+
))}
|
|
174
|
+
</SelectContent>
|
|
175
|
+
</Select>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import Link from "next/link";
|
|
4
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
5
|
+
import { Button } from "@/components/ui/button";
|
|
6
|
+
import { ArrowRight } from "lucide-react";
|
|
7
|
+
import { MiniBar } from "@/components/charts/mini-bar";
|
|
8
|
+
|
|
9
|
+
export interface ActivityEntry {
|
|
10
|
+
id: string;
|
|
11
|
+
event: string;
|
|
12
|
+
payload: string | null;
|
|
13
|
+
timestamp: string;
|
|
14
|
+
taskTitle?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface ActivityFeedProps {
|
|
18
|
+
entries: ActivityEntry[];
|
|
19
|
+
hourlyActivity?: number[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const eventColors: Record<string, string> = {
|
|
23
|
+
tool_start: "bg-status-running",
|
|
24
|
+
content_start: "bg-status-completed",
|
|
25
|
+
content_delta: "bg-status-completed/70",
|
|
26
|
+
completed: "bg-status-completed",
|
|
27
|
+
error: "bg-status-failed",
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export function ActivityFeed({ entries, hourlyActivity }: ActivityFeedProps) {
|
|
31
|
+
return (
|
|
32
|
+
<Card className="surface-card">
|
|
33
|
+
<CardHeader className="pb-3">
|
|
34
|
+
<CardTitle className="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
35
|
+
Live Agent Activity
|
|
36
|
+
</CardTitle>
|
|
37
|
+
</CardHeader>
|
|
38
|
+
<CardContent>
|
|
39
|
+
{hourlyActivity && hourlyActivity.some((v) => v > 0) && (
|
|
40
|
+
<div className="mb-3 pb-3 border-b border-border/50">
|
|
41
|
+
<p className="text-[10px] text-muted-foreground mb-1.5">24h activity</p>
|
|
42
|
+
<MiniBar
|
|
43
|
+
data={hourlyActivity.map((value) => ({ value }))}
|
|
44
|
+
width={200}
|
|
45
|
+
height={28}
|
|
46
|
+
defaultColor="var(--chart-1)"
|
|
47
|
+
label="Agent activity over last 24 hours"
|
|
48
|
+
className="w-full"
|
|
49
|
+
/>
|
|
50
|
+
</div>
|
|
51
|
+
)}
|
|
52
|
+
{entries.length === 0 ? (
|
|
53
|
+
<p
|
|
54
|
+
className="text-sm text-muted-foreground py-4 text-center"
|
|
55
|
+
aria-live="polite"
|
|
56
|
+
>
|
|
57
|
+
No recent agent activity.
|
|
58
|
+
</p>
|
|
59
|
+
) : (
|
|
60
|
+
<div className="space-y-1" aria-live="polite">
|
|
61
|
+
{entries.map((entry) => (
|
|
62
|
+
<div
|
|
63
|
+
key={entry.id}
|
|
64
|
+
className="flex items-start gap-3 py-2.5 border-b border-border/50 last:border-b-0"
|
|
65
|
+
>
|
|
66
|
+
<div
|
|
67
|
+
className={`h-2 w-2 rounded-full mt-1.5 flex-shrink-0 ${eventColors[entry.event] ?? "bg-muted-foreground"}`}
|
|
68
|
+
/>
|
|
69
|
+
<div className="flex-1 min-w-0">
|
|
70
|
+
<p className="text-sm truncate">
|
|
71
|
+
<span className="font-medium">{entry.event}</span>
|
|
72
|
+
{entry.taskTitle && (
|
|
73
|
+
<span className="text-muted-foreground"> — {entry.taskTitle}</span>
|
|
74
|
+
)}
|
|
75
|
+
</p>
|
|
76
|
+
<p className="text-xs text-muted-foreground">
|
|
77
|
+
{new Date(entry.timestamp).toLocaleTimeString()}
|
|
78
|
+
{entry.payload && ` · ${entry.payload.slice(0, 60)}${entry.payload.length > 60 ? "..." : ""}`}
|
|
79
|
+
</p>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
))}
|
|
83
|
+
</div>
|
|
84
|
+
)}
|
|
85
|
+
<div className="mt-3">
|
|
86
|
+
<Link href="/monitor">
|
|
87
|
+
<Button variant="outline" size="sm" className="w-full">
|
|
88
|
+
Open monitor <ArrowRight className="h-3 w-3 ml-1" />
|
|
89
|
+
</Button>
|
|
90
|
+
</Link>
|
|
91
|
+
</div>
|
|
92
|
+
</CardContent>
|
|
93
|
+
</Card>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
interface GreetingProps {
|
|
2
|
+
runningCount: number;
|
|
3
|
+
awaitingCount: number;
|
|
4
|
+
failedCount: number;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function getGreeting(): string {
|
|
8
|
+
const hour = new Date().getHours();
|
|
9
|
+
if (hour < 12) return "Good morning";
|
|
10
|
+
if (hour < 18) return "Good afternoon";
|
|
11
|
+
return "Good evening";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function Greeting({ runningCount, awaitingCount, failedCount }: GreetingProps) {
|
|
15
|
+
const parts: string[] = [];
|
|
16
|
+
if (runningCount > 0) parts.push(`${runningCount} task${runningCount !== 1 ? "s" : ""} running`);
|
|
17
|
+
if (awaitingCount > 0) parts.push(`${awaitingCount} awaiting your review`);
|
|
18
|
+
if (failedCount > 0) parts.push(`${failedCount} failed task${failedCount !== 1 ? "s" : ""} to address`);
|
|
19
|
+
|
|
20
|
+
const summary = parts.length > 0
|
|
21
|
+
? `You have ${parts.join(", ")}.`
|
|
22
|
+
: "All clear — no tasks need your attention.";
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div className="glass-card rounded-xl p-5 mb-6">
|
|
26
|
+
<h2 className="text-xl font-bold">{getGreeting()}</h2>
|
|
27
|
+
<p className="text-sm text-muted-foreground mt-1">{summary}</p>
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import Link from "next/link";
|
|
4
|
+
import { Badge } from "@/components/ui/badge";
|
|
5
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
6
|
+
import { Button } from "@/components/ui/button";
|
|
7
|
+
import { AlertTriangle, Clock, Shield, ArrowRight } from "lucide-react";
|
|
8
|
+
import { taskStatusVariant } from "@/lib/constants/status-colors";
|
|
9
|
+
|
|
10
|
+
export interface PriorityTask {
|
|
11
|
+
id: string;
|
|
12
|
+
title: string;
|
|
13
|
+
status: string;
|
|
14
|
+
priority: number;
|
|
15
|
+
projectName?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface PriorityQueueProps {
|
|
19
|
+
tasks: PriorityTask[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const statusIcon: Record<string, typeof AlertTriangle> = {
|
|
23
|
+
failed: AlertTriangle,
|
|
24
|
+
running: Clock,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const priorityColors = ["text-priority-critical", "text-priority-high", "text-status-warning", "text-muted-foreground"];
|
|
28
|
+
|
|
29
|
+
export function PriorityQueue({ tasks }: PriorityQueueProps) {
|
|
30
|
+
return (
|
|
31
|
+
<Card className="surface-card">
|
|
32
|
+
<CardHeader className="pb-3">
|
|
33
|
+
<CardTitle className="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
34
|
+
Needs Attention
|
|
35
|
+
</CardTitle>
|
|
36
|
+
</CardHeader>
|
|
37
|
+
<CardContent>
|
|
38
|
+
{tasks.length === 0 ? (
|
|
39
|
+
<p
|
|
40
|
+
className="text-sm text-muted-foreground py-4 text-center"
|
|
41
|
+
aria-live="polite"
|
|
42
|
+
>
|
|
43
|
+
No tasks need attention right now.
|
|
44
|
+
</p>
|
|
45
|
+
) : (
|
|
46
|
+
<div className="space-y-1" aria-live="polite">
|
|
47
|
+
{tasks.map((task) => {
|
|
48
|
+
const Icon = statusIcon[task.status] ?? Shield;
|
|
49
|
+
return (
|
|
50
|
+
<div
|
|
51
|
+
key={task.id}
|
|
52
|
+
className="flex items-center gap-3 py-2.5 border-b border-border/50 last:border-b-0"
|
|
53
|
+
>
|
|
54
|
+
<Icon className={`h-4 w-4 flex-shrink-0 ${priorityColors[task.priority] ?? priorityColors[3]}`} />
|
|
55
|
+
<div className="flex-1 min-w-0">
|
|
56
|
+
<p className="text-sm font-medium truncate">{task.title}</p>
|
|
57
|
+
{task.projectName && (
|
|
58
|
+
<p className="text-xs text-muted-foreground">{task.projectName}</p>
|
|
59
|
+
)}
|
|
60
|
+
</div>
|
|
61
|
+
<Badge variant={taskStatusVariant[task.status] ?? "secondary"} className="text-xs">
|
|
62
|
+
{task.status}
|
|
63
|
+
</Badge>
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
})}
|
|
67
|
+
</div>
|
|
68
|
+
)}
|
|
69
|
+
<div className="mt-3">
|
|
70
|
+
<Link href="/dashboard">
|
|
71
|
+
<Button variant="outline" size="sm" className="w-full">
|
|
72
|
+
View all tasks <ArrowRight className="h-3 w-3 ml-1" />
|
|
73
|
+
</Button>
|
|
74
|
+
</Link>
|
|
75
|
+
</div>
|
|
76
|
+
</CardContent>
|
|
77
|
+
</Card>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useRouter } from "next/navigation";
|
|
4
|
+
import { Card, CardContent } from "@/components/ui/card";
|
|
5
|
+
import { Plus, FolderPlus, Inbox, Activity } from "lucide-react";
|
|
6
|
+
import { SectionHeading } from "@/components/shared/section-heading";
|
|
7
|
+
|
|
8
|
+
export function QuickActions() {
|
|
9
|
+
const router = useRouter();
|
|
10
|
+
|
|
11
|
+
const actions = [
|
|
12
|
+
{
|
|
13
|
+
label: "New Task",
|
|
14
|
+
description: "Create & queue an agent task",
|
|
15
|
+
icon: Plus,
|
|
16
|
+
onClick: () => router.push("/tasks/new"),
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
label: "New Project",
|
|
20
|
+
description: "Organize tasks into a project",
|
|
21
|
+
icon: FolderPlus,
|
|
22
|
+
onClick: () => router.push("/projects"),
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
label: "Open Inbox",
|
|
26
|
+
description: "Review pending notifications",
|
|
27
|
+
icon: Inbox,
|
|
28
|
+
onClick: () => router.push("/inbox"),
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
label: "Open Monitor",
|
|
32
|
+
description: "Watch live agent logs",
|
|
33
|
+
icon: Activity,
|
|
34
|
+
onClick: () => router.push("/monitor"),
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<div className="mb-6">
|
|
40
|
+
<SectionHeading>Quick Actions</SectionHeading>
|
|
41
|
+
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
|
42
|
+
{actions.map((action) => (
|
|
43
|
+
<Card
|
|
44
|
+
key={action.label}
|
|
45
|
+
tabIndex={0}
|
|
46
|
+
className="surface-card cursor-pointer transition-colors hover:bg-accent/50 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 rounded-xl"
|
|
47
|
+
onClick={action.onClick}
|
|
48
|
+
onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); action.onClick(); } }}
|
|
49
|
+
>
|
|
50
|
+
<CardContent className="flex flex-col items-center justify-center py-5 gap-2">
|
|
51
|
+
<action.icon className="h-8 w-8 text-muted-foreground" />
|
|
52
|
+
<span className="text-sm font-medium">{action.label}</span>
|
|
53
|
+
<span className="text-xs text-muted-foreground text-center">
|
|
54
|
+
{action.description}
|
|
55
|
+
</span>
|
|
56
|
+
</CardContent>
|
|
57
|
+
</Card>
|
|
58
|
+
))}
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import Link from "next/link";
|
|
4
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
5
|
+
import { Button } from "@/components/ui/button";
|
|
6
|
+
import { Progress } from "@/components/ui/progress";
|
|
7
|
+
import { FolderPlus } from "lucide-react";
|
|
8
|
+
import { SectionHeading } from "@/components/shared/section-heading";
|
|
9
|
+
import { DonutRing } from "@/components/charts/donut-ring";
|
|
10
|
+
|
|
11
|
+
export interface RecentProject {
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
totalTasks: number;
|
|
15
|
+
completedTasks: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface RecentProjectsProps {
|
|
19
|
+
projects: RecentProject[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function RecentProjects({ projects }: RecentProjectsProps) {
|
|
23
|
+
if (projects.length === 0) {
|
|
24
|
+
return (
|
|
25
|
+
<div>
|
|
26
|
+
<SectionHeading>Recent Projects</SectionHeading>
|
|
27
|
+
<Card className="surface-card p-6 text-center">
|
|
28
|
+
<FolderPlus className="h-8 w-8 mx-auto text-muted-foreground mb-2" />
|
|
29
|
+
<p className="text-sm text-muted-foreground mb-3">No active projects yet.</p>
|
|
30
|
+
<Link href="/projects">
|
|
31
|
+
<Button variant="outline" size="sm">Create your first project</Button>
|
|
32
|
+
</Link>
|
|
33
|
+
</Card>
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<div>
|
|
40
|
+
<div className="flex items-baseline justify-between mb-3">
|
|
41
|
+
<SectionHeading className="mb-0">Recent Projects</SectionHeading>
|
|
42
|
+
<Link href="/projects" className="text-xs text-muted-foreground underline hover:text-foreground">
|
|
43
|
+
View all
|
|
44
|
+
</Link>
|
|
45
|
+
</div>
|
|
46
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
47
|
+
{projects.map((project) => {
|
|
48
|
+
const pct = project.totalTasks > 0
|
|
49
|
+
? Math.round((project.completedTasks / project.totalTasks) * 100)
|
|
50
|
+
: 0;
|
|
51
|
+
return (
|
|
52
|
+
<Link key={project.id} href={`/projects/${project.id}`}>
|
|
53
|
+
<Card className="surface-card cursor-pointer transition-colors hover:bg-accent/50 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 rounded-xl">
|
|
54
|
+
<CardHeader className="pb-2">
|
|
55
|
+
<div className="flex items-center gap-2">
|
|
56
|
+
<DonutRing
|
|
57
|
+
value={pct}
|
|
58
|
+
size={28}
|
|
59
|
+
strokeWidth={3}
|
|
60
|
+
color="var(--chart-2)"
|
|
61
|
+
label={`${project.name}: ${pct}% complete`}
|
|
62
|
+
/>
|
|
63
|
+
<CardTitle className="text-base font-medium">{project.name}</CardTitle>
|
|
64
|
+
</div>
|
|
65
|
+
</CardHeader>
|
|
66
|
+
<CardContent>
|
|
67
|
+
<Progress value={pct} className="h-1.5 mb-2" />
|
|
68
|
+
<p className="text-xs text-muted-foreground">
|
|
69
|
+
{project.completedTasks}/{project.totalTasks} tasks completed
|
|
70
|
+
</p>
|
|
71
|
+
</CardContent>
|
|
72
|
+
</Card>
|
|
73
|
+
</Link>
|
|
74
|
+
);
|
|
75
|
+
})}
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import Link from "next/link";
|
|
4
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
5
|
+
import { Activity, CheckCircle, MessageSquare, FolderKanban } from "lucide-react";
|
|
6
|
+
import { Sparkline } from "@/components/charts/sparkline";
|
|
7
|
+
|
|
8
|
+
interface StatsCardsProps {
|
|
9
|
+
runningCount: number;
|
|
10
|
+
completedToday: number;
|
|
11
|
+
completedAllTime: number;
|
|
12
|
+
awaitingReview: number;
|
|
13
|
+
activeProjects: number;
|
|
14
|
+
sparklines?: {
|
|
15
|
+
completions: number[];
|
|
16
|
+
creations: number[];
|
|
17
|
+
projects: number[];
|
|
18
|
+
notifications: number[];
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function StatsCards({
|
|
23
|
+
runningCount,
|
|
24
|
+
completedToday,
|
|
25
|
+
completedAllTime,
|
|
26
|
+
awaitingReview,
|
|
27
|
+
activeProjects,
|
|
28
|
+
sparklines,
|
|
29
|
+
}: StatsCardsProps) {
|
|
30
|
+
const stats = [
|
|
31
|
+
{
|
|
32
|
+
title: "Tasks Running",
|
|
33
|
+
value: runningCount,
|
|
34
|
+
subtitle: "Currently active",
|
|
35
|
+
icon: Activity,
|
|
36
|
+
color: "text-status-running",
|
|
37
|
+
chartColor: "var(--chart-1)",
|
|
38
|
+
href: "/monitor",
|
|
39
|
+
destination: "Monitor",
|
|
40
|
+
sparklineData: sparklines?.creations,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
title: "Completed Today",
|
|
44
|
+
value: completedToday,
|
|
45
|
+
subtitle: `All-time: ${completedAllTime}`,
|
|
46
|
+
icon: CheckCircle,
|
|
47
|
+
color: "text-status-completed",
|
|
48
|
+
chartColor: "var(--chart-2)",
|
|
49
|
+
href: "/dashboard",
|
|
50
|
+
destination: "Dashboard",
|
|
51
|
+
sparklineData: sparklines?.completions,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
title: "Awaiting Review",
|
|
55
|
+
value: awaitingReview,
|
|
56
|
+
subtitle: "Human-loop pending",
|
|
57
|
+
icon: MessageSquare,
|
|
58
|
+
color: "text-status-warning",
|
|
59
|
+
chartColor: "var(--chart-3)",
|
|
60
|
+
href: "/inbox",
|
|
61
|
+
destination: "Inbox",
|
|
62
|
+
sparklineData: sparklines?.notifications,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
title: "Active Projects",
|
|
66
|
+
value: activeProjects,
|
|
67
|
+
subtitle: "In progress",
|
|
68
|
+
icon: FolderKanban,
|
|
69
|
+
color: "text-primary",
|
|
70
|
+
chartColor: "var(--chart-4)",
|
|
71
|
+
href: "/projects",
|
|
72
|
+
destination: "Projects",
|
|
73
|
+
sparklineData: sparklines?.projects,
|
|
74
|
+
},
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
|
|
79
|
+
{stats.map((s) => (
|
|
80
|
+
<Link key={s.title} href={s.href}>
|
|
81
|
+
<Card className="surface-card glass-shimmer cursor-pointer transition-colors hover:bg-accent/50 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 rounded-xl">
|
|
82
|
+
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
|
83
|
+
<CardTitle className="text-sm font-medium text-muted-foreground">
|
|
84
|
+
{s.title}
|
|
85
|
+
</CardTitle>
|
|
86
|
+
<s.icon className={`h-4 w-4 ${s.color}`} />
|
|
87
|
+
</CardHeader>
|
|
88
|
+
<CardContent>
|
|
89
|
+
<div className="text-2xl font-bold">{s.value}</div>
|
|
90
|
+
{s.sparklineData && (
|
|
91
|
+
<div className="hidden sm:block mt-1">
|
|
92
|
+
<Sparkline
|
|
93
|
+
data={s.sparklineData}
|
|
94
|
+
width={100}
|
|
95
|
+
height={24}
|
|
96
|
+
color={s.chartColor}
|
|
97
|
+
label={`${s.title} 7-day trend`}
|
|
98
|
+
className="w-full"
|
|
99
|
+
/>
|
|
100
|
+
</div>
|
|
101
|
+
)}
|
|
102
|
+
<p className="text-xs text-muted-foreground mt-1">{s.subtitle}</p>
|
|
103
|
+
{s.destination && (
|
|
104
|
+
<p className="text-[10px] text-muted-foreground/60 mt-0.5">
|
|
105
|
+
→ {s.destination}
|
|
106
|
+
</p>
|
|
107
|
+
)}
|
|
108
|
+
</CardContent>
|
|
109
|
+
</Card>
|
|
110
|
+
</Link>
|
|
111
|
+
))}
|
|
112
|
+
</div>
|
|
113
|
+
);
|
|
114
|
+
}
|