Skip to content

Commit 0e1af72

Browse files
sweetmantechRecoup Agentclaude
authored
agent: @U0AJM7X8FBR Admin + Docs + API - we want to update the /coding page in (#21)
* feat: display pull requests on coding agent /coding page - Add pull_requests: string[] to SlackTag type - Update getTagsByDate to track pull_request_count per date - Update AdminLineChart to support an optional second line (PRs) - Add Pull Requests column to SlackTagsTable - Wire chart and table to show tags vs PRs over time Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add total_pull_requests and tags_with_pull_requests to response type and UI Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: chart shows tags with PRs instead of total PRs, show repo name in PR column Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Recoup Agent <agent@recoupable.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 03ee6dd commit 0e1af72

5 files changed

Lines changed: 100 additions & 17 deletions

File tree

components/Admin/AdminLineChart.tsx

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,54 @@
33
import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts";
44
import {
55
ChartContainer,
6+
ChartLegend,
7+
ChartLegendContent,
68
ChartTooltip,
79
ChartTooltipContent,
810
type ChartConfig,
911
} from "@/components/ui/chart";
1012

13+
interface SecondLine {
14+
data: Array<{ date: string; count: number }>;
15+
label: string;
16+
color?: string;
17+
}
18+
1119
interface AdminLineChartProps {
1220
title: string;
1321
data: Array<{ date: string; count: number }>;
1422
label?: string;
23+
secondLine?: SecondLine;
1524
}
1625

17-
export default function AdminLineChart({ title, data, label = "Count" }: AdminLineChartProps) {
26+
export default function AdminLineChart({
27+
title,
28+
data,
29+
label = "Count",
30+
secondLine,
31+
}: AdminLineChartProps) {
1832
if (data.length === 0) return null;
1933

2034
const chartConfig = {
21-
count: {
22-
label,
23-
color: "#345A5D",
24-
},
35+
count: { label, color: "#345A5D" },
36+
...(secondLine
37+
? { count2: { label: secondLine.label, color: secondLine.color ?? "#6B8E93" } }
38+
: {}),
2539
} satisfies ChartConfig;
2640

41+
// Merge primary and secondary data by date
42+
const secondMap = new Map(secondLine?.data.map((d) => [d.date, d.count]) ?? []);
43+
const mergedData = data.map((d) => ({
44+
date: d.date,
45+
count: d.count,
46+
...(secondLine ? { count2: secondMap.get(d.date) ?? 0 } : {}),
47+
}));
48+
2749
return (
2850
<div className="mb-6 rounded-lg border p-4">
2951
<h2 className="mb-4 text-sm font-medium text-gray-700 dark:text-gray-300">{title}</h2>
3052
<ChartContainer config={chartConfig} className="h-[250px] w-full">
31-
<LineChart data={data} accessibilityLayer>
53+
<LineChart data={mergedData} accessibilityLayer>
3254
<CartesianGrid vertical={false} />
3355
<XAxis
3456
dataKey="date"
@@ -55,13 +77,23 @@ export default function AdminLineChart({ title, data, label = "Count" }: AdminLi
5577
/>
5678
}
5779
/>
80+
{secondLine && <ChartLegend content={<ChartLegendContent />} />}
5881
<Line
5982
dataKey="count"
6083
type="monotone"
6184
stroke="var(--color-count)"
6285
strokeWidth={2}
6386
dot={{ fill: "var(--color-count)", r: 4 }}
6487
/>
88+
{secondLine && (
89+
<Line
90+
dataKey="count2"
91+
type="monotone"
92+
stroke="var(--color-count2)"
93+
strokeWidth={2}
94+
dot={{ fill: "var(--color-count2)", r: 4 }}
95+
/>
96+
)}
6597
</LineChart>
6698
</ChartContainer>
6799
</div>

components/CodingAgentSlackTags/CodingAgentSlackTagsPage.tsx

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,27 @@ export default function CodingAgentSlackTagsPage() {
3434
<div className="mb-6 flex items-center gap-4">
3535
<PeriodSelector period={period} onPeriodChange={setPeriod} />
3636
{data && (
37-
<div className="text-sm text-gray-600 dark:text-gray-400">
38-
<span className="font-semibold text-gray-900 dark:text-gray-100">{data.total}</span>{" "}
39-
{data.total === 1 ? "tag" : "tags"} found
37+
<div className="flex gap-4 text-sm text-gray-600 dark:text-gray-400">
38+
<span>
39+
<span className="font-semibold text-gray-900 dark:text-gray-100">{data.total}</span>{" "}
40+
{data.total === 1 ? "tag" : "tags"}
41+
</span>
42+
<span>
43+
<span className="font-semibold text-gray-900 dark:text-gray-100">{data.tags_with_pull_requests}</span>{" "}
44+
with PRs
45+
</span>
46+
<span>
47+
<span className="font-semibold text-gray-900 dark:text-gray-100">{data.total_pull_requests}</span>{" "}
48+
total PRs
49+
</span>
4050
</div>
4151
)}
4252
</div>
4353

4454
{isLoading && (
4555
<>
4656
<ChartSkeleton />
47-
<TableSkeleton columns={["Tagged By", "Prompt", "Channel", "Timestamp"]} />
57+
<TableSkeleton columns={["Tagged By", "Prompt", "Channel", "Pull Requests", "Timestamp"]} />
4858
</>
4959
)}
5060

@@ -62,7 +72,18 @@ export default function CodingAgentSlackTagsPage() {
6272

6373
{!isLoading && !error && data && data.tags.length > 0 && (
6474
<>
65-
<AdminLineChart title="Tags Over Time" data={getTagsByDate(data.tags)} label="Tags" />
75+
<AdminLineChart
76+
title="Tags & Pull Requests Over Time"
77+
data={getTagsByDate(data.tags).map((d) => ({ date: d.date, count: d.count }))}
78+
label="Tags"
79+
secondLine={{
80+
data: getTagsByDate(data.tags).map((d) => ({
81+
date: d.date,
82+
count: d.pull_request_count,
83+
})),
84+
label: "Tags with PRs",
85+
}}
86+
/>
6687
<SlackTagsTable tags={data.tags} />
6788
</>
6889
)}

components/CodingAgentSlackTags/SlackTagsColumns.tsx

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,30 @@ export const slackTagsColumns: ColumnDef<SlackTag>[] = [
4141
<span className="text-sm text-gray-500 dark:text-gray-400">#{getValue<string>()}</span>
4242
),
4343
},
44+
{
45+
id: "pull_requests",
46+
accessorKey: "pull_requests",
47+
header: "Pull Requests",
48+
cell: ({ getValue }) => {
49+
const prs = getValue<string[]>();
50+
if (!prs?.length) return <span className="text-sm text-gray-400"></span>;
51+
return (
52+
<div className="flex flex-col gap-1">
53+
{prs.map((url) => (
54+
<a
55+
key={url}
56+
href={url}
57+
target="_blank"
58+
rel="noopener noreferrer"
59+
className="text-sm text-blue-600 hover:underline dark:text-blue-400"
60+
>
61+
{url.match(/github\.com\/[^/]+\/([^/]+)\/pull\/(\d+)/)?.slice(1).join("#")}
62+
</a>
63+
))}
64+
</div>
65+
);
66+
},
67+
},
4468
{
4569
id: "timestamp",
4670
accessorKey: "timestamp",

lib/coding-agent/getTagsByDate.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,28 @@
11
import type { SlackTag } from "@/types/coding-agent";
22

3-
interface TagsByDateEntry {
3+
export interface TagsByDateEntry {
44
date: string;
55
count: number;
6+
pull_request_count: number;
67
}
78

89
/**
9-
* Aggregates Slack tags by UTC date (YYYY-MM-DD) for charting.
10+
* Aggregates Slack tags and their associated pull requests by UTC date (YYYY-MM-DD) for charting.
1011
*
1112
* @param tags - Array of SlackTag objects
12-
* @returns Array of { date, count } sorted ascending by date
13+
* @returns Array of { date, count, pull_request_count } sorted ascending by date
1314
*/
1415
export function getTagsByDate(tags: SlackTag[]): TagsByDateEntry[] {
15-
const counts: Record<string, number> = {};
16+
const counts: Record<string, { count: number; pull_request_count: number }> = {};
1617

1718
for (const tag of tags) {
1819
const date = tag.timestamp.slice(0, 10); // "YYYY-MM-DD"
19-
counts[date] = (counts[date] ?? 0) + 1;
20+
if (!counts[date]) counts[date] = { count: 0, pull_request_count: 0 };
21+
counts[date].count += 1;
22+
counts[date].pull_request_count += (tag.pull_requests?.length ?? 0) > 0 ? 1 : 0;
2023
}
2124

2225
return Object.entries(counts)
23-
.map(([date, count]) => ({ date, count }))
26+
.map(([date, { count, pull_request_count }]) => ({ date, count, pull_request_count }))
2427
.sort((a, b) => a.date.localeCompare(b.date));
2528
}

types/coding-agent.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@ export interface SlackTag {
66
timestamp: string;
77
channel_id: string;
88
channel_name: string;
9+
pull_requests: string[];
910
}
1011

1112
export type { AdminPeriod as SlackTagsPeriod } from "./admin";
1213

1314
export interface SlackTagsResponse {
1415
status: "success";
1516
total: number;
17+
total_pull_requests: number;
18+
tags_with_pull_requests: number;
1619
tags: SlackTag[];
1720
}

0 commit comments

Comments
 (0)