feat: server-side issue search, dashboard charts, and inbox badges
Add ILIKE-based issue search across title, identifier, description, and comments with relevance ranking. Add assigneeUserId filter and allow agents to return issues to creator. Show assigned issue count in sidebar badges. Add minCount param to live-runs endpoint. Add activity charts (run activity, priority, status, success rate) to dashboard. Improve active agents panel with recent run cards. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@ import { activityApi } from "../api/activity";
|
||||
import { issuesApi } from "../api/issues";
|
||||
import { agentsApi } from "../api/agents";
|
||||
import { projectsApi } from "../api/projects";
|
||||
import { heartbeatsApi } from "../api/heartbeats";
|
||||
import { useCompany } from "../context/CompanyContext";
|
||||
import { useDialog } from "../context/DialogContext";
|
||||
import { useBreadcrumbs } from "../context/BreadcrumbContext";
|
||||
@@ -20,6 +21,7 @@ import { timeAgo } from "../lib/timeAgo";
|
||||
import { cn, formatCents } from "../lib/utils";
|
||||
import { Bot, CircleDot, DollarSign, ShieldCheck, LayoutDashboard } from "lucide-react";
|
||||
import { ActiveAgentsPanel } from "../components/ActiveAgentsPanel";
|
||||
import { ChartCard, RunActivityChart, PriorityChart, IssueStatusChart, SuccessRateChart } from "../components/ActivityCharts";
|
||||
import type { Agent, Issue } from "@paperclip/shared";
|
||||
|
||||
function getRecentIssues(issues: Issue[]): Issue[] {
|
||||
@@ -28,7 +30,7 @@ function getRecentIssues(issues: Issue[]): Issue[] {
|
||||
}
|
||||
|
||||
export function Dashboard() {
|
||||
const { selectedCompanyId, selectedCompany, companies } = useCompany();
|
||||
const { selectedCompanyId, companies } = useCompany();
|
||||
const { openOnboarding } = useDialog();
|
||||
const { setBreadcrumbs } = useBreadcrumbs();
|
||||
const [animatedActivityIds, setAnimatedActivityIds] = useState<Set<string>>(new Set());
|
||||
@@ -70,6 +72,12 @@ export function Dashboard() {
|
||||
enabled: !!selectedCompanyId,
|
||||
});
|
||||
|
||||
const { data: runs } = useQuery({
|
||||
queryKey: queryKeys.heartbeats(selectedCompanyId!),
|
||||
queryFn: () => heartbeatsApi.list(selectedCompanyId!),
|
||||
enabled: !!selectedCompanyId,
|
||||
});
|
||||
|
||||
const recentIssues = issues ? getRecentIssues(issues) : [];
|
||||
const recentActivity = useMemo(() => (activity ?? []).slice(0, 10), [activity]);
|
||||
|
||||
@@ -171,16 +179,14 @@ export function Dashboard() {
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{selectedCompany && (
|
||||
<p className="text-sm text-muted-foreground">{selectedCompany.name}</p>
|
||||
)}
|
||||
|
||||
{isLoading && <p className="text-sm text-muted-foreground">Loading...</p>}
|
||||
{error && <p className="text-sm text-destructive">{error.message}</p>}
|
||||
|
||||
<ActiveAgentsPanel companyId={selectedCompanyId!} />
|
||||
|
||||
{data && (
|
||||
<>
|
||||
<div className="grid grid-cols-2 xl:grid-cols-4 gap-2 sm:gap-4">
|
||||
<div className="grid grid-cols-2 xl:grid-cols-4 gap-1 sm:gap-2">
|
||||
<MetricCard
|
||||
icon={Bot}
|
||||
value={data.agents.active + data.agents.running + data.agents.paused + data.agents.error}
|
||||
@@ -232,6 +238,21 @@ export function Dashboard() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<ChartCard title="Run Activity" subtitle="Last 14 days">
|
||||
<RunActivityChart runs={runs ?? []} />
|
||||
</ChartCard>
|
||||
<ChartCard title="Issues by Priority" subtitle="Last 14 days">
|
||||
<PriorityChart issues={issues ?? []} />
|
||||
</ChartCard>
|
||||
<ChartCard title="Issues by Status" subtitle="Last 14 days">
|
||||
<IssueStatusChart issues={issues ?? []} />
|
||||
</ChartCard>
|
||||
<ChartCard title="Success Rate" subtitle="Last 14 days">
|
||||
<SuccessRateChart runs={runs ?? []} />
|
||||
</ChartCard>
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-4">
|
||||
{/* Recent Activity */}
|
||||
{recentActivity.length > 0 && (
|
||||
@@ -298,7 +319,6 @@ export function Dashboard() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ActiveAgentsPanel companyId={selectedCompanyId!} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user