diff --git a/components/Admin/AdminPieChart.tsx b/components/Admin/AdminPieChart.tsx
new file mode 100644
index 0000000..8ec3d4f
--- /dev/null
+++ b/components/Admin/AdminPieChart.tsx
@@ -0,0 +1,74 @@
+"use client";
+
+import { Pie, PieChart, Cell } from "recharts";
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ ChartLegend,
+ ChartLegendContent,
+ type ChartConfig,
+} from "@/components/ui/chart";
+
+const COLORS = [
+ "#345A5D",
+ "#6B8E93",
+ "#4A90A4",
+ "#8FBCC4",
+ "#2A4648",
+ "#5BA3B5",
+ "#3D7A7E",
+ "#97C4CC",
+];
+
+export interface PieChartSlice {
+ name: string;
+ value: number;
+}
+
+interface AdminPieChartProps {
+ title: string;
+ data: PieChartSlice[];
+}
+
+export default function AdminPieChart({ title, data }: AdminPieChartProps) {
+ if (data.length === 0) return null;
+
+ const chartConfig = Object.fromEntries(
+ data.map((slice, i) => [
+ slice.name,
+ { label: slice.name, color: COLORS[i % COLORS.length] },
+ ]),
+ ) satisfies ChartConfig;
+
+ return (
+
+
+ {title}
+
+
+
+ } />
+
+ {data.map((slice, i) => (
+ |
+ ))}
+
+ } />
+
+
+
+ );
+}
diff --git a/components/ContentSlack/ContentSlackPage.tsx b/components/ContentSlack/ContentSlackPage.tsx
index 07e251d..9d18ab0 100644
--- a/components/ContentSlack/ContentSlackPage.tsx
+++ b/components/ContentSlack/ContentSlackPage.tsx
@@ -8,9 +8,12 @@ import ContentSlackTable from "@/components/ContentSlack/ContentSlackTable";
import ContentSlackStats from "@/components/ContentSlack/ContentSlackStats";
import PeriodSelector from "@/components/Admin/PeriodSelector";
import AdminLineChart from "@/components/Admin/AdminLineChart";
+import AdminPieChart from "@/components/Admin/AdminPieChart";
import TableSkeleton from "@/components/Sandboxes/TableSkeleton";
import ChartSkeleton from "@/components/PrivyLogins/ChartSkeleton";
+import PieChartSkeleton from "@/components/ContentSlack/PieChartSkeleton";
import { getTagsByDate } from "@/lib/coding-agent/getTagsByDate";
+import { getTagsByUser } from "@/lib/contentSlack/getTagsByUser";
import type { AdminPeriod } from "@/types/admin";
export default function ContentSlackPage() {
@@ -25,6 +28,7 @@ export default function ContentSlackPage() {
})),
)
: [];
+ const tagsByUser = data ? getTagsByUser(data.tags) : [];
return (
@@ -49,6 +53,7 @@ export default function ContentSlackPage() {
{isLoading && (
<>
+
>
)}
@@ -76,6 +81,9 @@ export default function ContentSlackPage() {
label: "Tags with Videos",
}}
/>
+
>
)}
diff --git a/components/ContentSlack/PieChartSkeleton.tsx b/components/ContentSlack/PieChartSkeleton.tsx
new file mode 100644
index 0000000..ed71bce
--- /dev/null
+++ b/components/ContentSlack/PieChartSkeleton.tsx
@@ -0,0 +1,10 @@
+export default function PieChartSkeleton() {
+ return (
+
+ );
+}
diff --git a/lib/contentSlack/getTagsByUser.ts b/lib/contentSlack/getTagsByUser.ts
new file mode 100644
index 0000000..ed3ae26
--- /dev/null
+++ b/lib/contentSlack/getTagsByUser.ts
@@ -0,0 +1,19 @@
+import type { ContentSlackTag } from "@/types/contentSlack";
+import type { PieChartSlice } from "@/components/Admin/AdminPieChart";
+
+/**
+ * Aggregates content slack tags by user_name for pie chart display.
+ * Returns slices sorted descending by count.
+ */
+export function getTagsByUser(tags: ContentSlackTag[]): PieChartSlice[] {
+ const counts = new Map();
+
+ for (const tag of tags) {
+ const name = tag.user_name || "Unknown";
+ counts.set(name, (counts.get(name) ?? 0) + 1);
+ }
+
+ return Array.from(counts.entries())
+ .map(([name, value]) => ({ name, value }))
+ .sort((a, b) => b.value - a.value);
+}