diff --git a/components/Admin/AdminLineChart.tsx b/components/Admin/AdminLineChart.tsx
index c5d1d5e..984e811 100644
--- a/components/Admin/AdminLineChart.tsx
+++ b/components/Admin/AdminLineChart.tsx
@@ -3,32 +3,54 @@
import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts";
import {
ChartContainer,
+ ChartLegend,
+ ChartLegendContent,
ChartTooltip,
ChartTooltipContent,
type ChartConfig,
} from "@/components/ui/chart";
+interface SecondLine {
+ data: Array<{ date: string; count: number }>;
+ label: string;
+ color?: string;
+}
+
interface AdminLineChartProps {
title: string;
data: Array<{ date: string; count: number }>;
label?: string;
+ secondLine?: SecondLine;
}
-export default function AdminLineChart({ title, data, label = "Count" }: AdminLineChartProps) {
+export default function AdminLineChart({
+ title,
+ data,
+ label = "Count",
+ secondLine,
+}: AdminLineChartProps) {
if (data.length === 0) return null;
const chartConfig = {
- count: {
- label,
- color: "#345A5D",
- },
+ count: { label, color: "#345A5D" },
+ ...(secondLine
+ ? { count2: { label: secondLine.label, color: secondLine.color ?? "#6B8E93" } }
+ : {}),
} satisfies ChartConfig;
+ // Merge primary and secondary data by date
+ const secondMap = new Map(secondLine?.data.map((d) => [d.date, d.count]) ?? []);
+ const mergedData = data.map((d) => ({
+ date: d.date,
+ count: d.count,
+ ...(secondLine ? { count2: secondMap.get(d.date) ?? 0 } : {}),
+ }));
+
return (
{title}
-
+
}
/>
+ {secondLine && } />}
+ {secondLine && (
+
+ )}
diff --git a/components/CodingAgentSlackTags/CodingAgentSlackTagsPage.tsx b/components/CodingAgentSlackTags/CodingAgentSlackTagsPage.tsx
index 4b529b2..cdd09d5 100644
--- a/components/CodingAgentSlackTags/CodingAgentSlackTagsPage.tsx
+++ b/components/CodingAgentSlackTags/CodingAgentSlackTagsPage.tsx
@@ -34,9 +34,19 @@ export default function CodingAgentSlackTagsPage() {
{data && (
-
-
{data.total}{" "}
- {data.total === 1 ? "tag" : "tags"} found
+
+
+ {data.total}{" "}
+ {data.total === 1 ? "tag" : "tags"}
+
+
+ {data.tags_with_pull_requests}{" "}
+ with PRs
+
+
+ {data.total_pull_requests}{" "}
+ total PRs
+
)}
@@ -44,7 +54,7 @@ export default function CodingAgentSlackTagsPage() {
{isLoading && (
<>
-
+
>
)}
@@ -62,7 +72,18 @@ export default function CodingAgentSlackTagsPage() {
{!isLoading && !error && data && data.tags.length > 0 && (
<>
-
+
({ date: d.date, count: d.count }))}
+ label="Tags"
+ secondLine={{
+ data: getTagsByDate(data.tags).map((d) => ({
+ date: d.date,
+ count: d.pull_request_count,
+ })),
+ label: "Tags with PRs",
+ }}
+ />
>
)}
diff --git a/components/CodingAgentSlackTags/SlackTagsColumns.tsx b/components/CodingAgentSlackTags/SlackTagsColumns.tsx
index 8dad5a3..a89a7a6 100644
--- a/components/CodingAgentSlackTags/SlackTagsColumns.tsx
+++ b/components/CodingAgentSlackTags/SlackTagsColumns.tsx
@@ -41,6 +41,30 @@ export const slackTagsColumns: ColumnDef[] = [
#{getValue()}
),
},
+ {
+ id: "pull_requests",
+ accessorKey: "pull_requests",
+ header: "Pull Requests",
+ cell: ({ getValue }) => {
+ const prs = getValue();
+ if (!prs?.length) return —;
+ return (
+
+ );
+ },
+ },
{
id: "timestamp",
accessorKey: "timestamp",
diff --git a/lib/coding-agent/getTagsByDate.ts b/lib/coding-agent/getTagsByDate.ts
index 2222be3..70db9ad 100644
--- a/lib/coding-agent/getTagsByDate.ts
+++ b/lib/coding-agent/getTagsByDate.ts
@@ -1,25 +1,28 @@
import type { SlackTag } from "@/types/coding-agent";
-interface TagsByDateEntry {
+export interface TagsByDateEntry {
date: string;
count: number;
+ pull_request_count: number;
}
/**
- * Aggregates Slack tags by UTC date (YYYY-MM-DD) for charting.
+ * Aggregates Slack tags and their associated pull requests by UTC date (YYYY-MM-DD) for charting.
*
* @param tags - Array of SlackTag objects
- * @returns Array of { date, count } sorted ascending by date
+ * @returns Array of { date, count, pull_request_count } sorted ascending by date
*/
export function getTagsByDate(tags: SlackTag[]): TagsByDateEntry[] {
- const counts: Record = {};
+ const counts: Record = {};
for (const tag of tags) {
const date = tag.timestamp.slice(0, 10); // "YYYY-MM-DD"
- counts[date] = (counts[date] ?? 0) + 1;
+ if (!counts[date]) counts[date] = { count: 0, pull_request_count: 0 };
+ counts[date].count += 1;
+ counts[date].pull_request_count += (tag.pull_requests?.length ?? 0) > 0 ? 1 : 0;
}
return Object.entries(counts)
- .map(([date, count]) => ({ date, count }))
+ .map(([date, { count, pull_request_count }]) => ({ date, count, pull_request_count }))
.sort((a, b) => a.date.localeCompare(b.date));
}
diff --git a/types/coding-agent.ts b/types/coding-agent.ts
index 26e9ffd..65ae517 100644
--- a/types/coding-agent.ts
+++ b/types/coding-agent.ts
@@ -6,6 +6,7 @@ export interface SlackTag {
timestamp: string;
channel_id: string;
channel_name: string;
+ pull_requests: string[];
}
export type { AdminPeriod as SlackTagsPeriod } from "./admin";
@@ -13,5 +14,7 @@ export type { AdminPeriod as SlackTagsPeriod } from "./admin";
export interface SlackTagsResponse {
status: "success";
total: number;
+ total_pull_requests: number;
+ tags_with_pull_requests: number;
tags: SlackTag[];
}