refactor(insights): share workbook export and ai defaults

This commit is contained in:
2026-03-31 22:53:53 +02:00
parent 05eeaab3f7
commit 160ba99b5c
8 changed files with 272 additions and 61 deletions
@@ -70,6 +70,12 @@ function entityLink(type: string, entityId: string): string {
return `/projects/${entityId}`;
}
type ProjectOption = {
id: string;
name: string;
shortCode: string | null;
};
// ─── Main component ──────────────────────────────────────────────────────────
export function InsightsPanel() {
@@ -111,7 +117,7 @@ export function InsightsPanel() {
});
const anomalies = anomaliesQuery.data ?? [];
const projects = projectsQuery.data?.projects ?? [];
const projects: ProjectOption[] = (projectsQuery.data?.projects ?? []) as ProjectOption[];
// Filter anomalies
const filteredAnomalies = narrativeFilter
@@ -6,7 +6,7 @@ import { PROFICIENCY_LABELS, proficiencyClasses, ProficiencyBadge } from "~/comp
import { SortableColumnHeader } from "~/components/ui/SortableColumnHeader.js";
import { useTableSort } from "~/hooks/useTableSort.js";
import { trpc } from "~/lib/trpc/client.js";
import * as XLSX from "xlsx";
import { downloadWorkbook } from "~/lib/workbook-export.js";
const SkillDistributionChart = dynamic(
() => import("~/components/analytics/SkillDistributionChart.js"),
@@ -60,18 +60,17 @@ export function SkillsAnalytics() {
async function exportXlsx() {
if (!data) return;
const XLSX = await import("xlsx");
const rows = data.aggregated.map((e) => ({
Skill: e.skill,
Category: e.category,
"# Resources": e.count,
"Avg Proficiency": e.avgProficiency,
Chapters: e.chapters.join(", "),
}));
const ws = XLSX.utils.json_to_sheet(rows);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Skills");
XLSX.writeFile(wb, `skills-analytics-${Date.now()}.xlsx`);
const rows = [
["Skill", "Category", "# Resources", "Avg Proficiency", "Chapters"],
...data.aggregated.map((entry) => [
entry.skill,
entry.category,
entry.count,
entry.avgProficiency,
entry.chapters.join(", "),
]),
];
await downloadWorkbook(`skills-analytics-${Date.now()}.xlsx`, "Skills", rows);
}
const allSkillNames = (data?.aggregated ?? []).map((e) => e.skill);
@@ -4,6 +4,7 @@ import { useState } from "react";
import dynamic from "next/dynamic";
import { SortableColumnHeader } from "~/components/ui/SortableColumnHeader.js";
import { useTableSort } from "~/hooks/useTableSort.js";
import { downloadWorkbook } from "~/lib/workbook-export.js";
import { ProficiencyBadge } from "./shared.js";
const SkillDistributionChart = dynamic(
@@ -44,18 +45,17 @@ export function OverviewTab({ aggregated, categories, totalResources, totalSkill
const gapCount = aggregated.filter((e) => e.count < 3 && e.avgProficiency >= 3).length;
async function exportXlsx() {
const XLSX = await import("xlsx");
const rows = sorted.map((e) => ({
Skill: e.skill,
Category: e.category,
"# Resources": e.count,
"Avg Proficiency": e.avgProficiency,
Chapters: e.chapters.join(", "),
}));
const ws = XLSX.utils.json_to_sheet(rows);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "Skills Overview");
XLSX.writeFile(wb, `skills-overview-${Date.now()}.xlsx`);
const rows = [
["Skill", "Category", "# Resources", "Avg Proficiency", "Chapters"],
...sorted.map((entry) => [
entry.skill,
entry.category,
entry.count,
entry.avgProficiency,
entry.chapters.join(", "),
]),
];
await downloadWorkbook(`skills-overview-${Date.now()}.xlsx`, "Skills Overview", rows);
}
return (
@@ -3,6 +3,7 @@
import { useState, useId } from "react";
import Link from "next/link";
import { trpc } from "~/lib/trpc/client.js";
import { downloadWorkbook } from "~/lib/workbook-export.js";
import { ProficiencyBadge, PROFICIENCY_LABELS, proficiencyClasses } from "./shared.js";
type SkillRule = { skill: string; minProficiency: number };
@@ -32,17 +33,16 @@ export function PeopleFinderTab({ allSkillNames, allChapters }: PeopleFinderTabP
async function exportXlsx() {
if (!results || results.length === 0) return;
const XLSX = await import("xlsx");
const rows = results.map((p) => ({
Name: p.displayName,
EID: p.eid ?? "",
Chapter: p.chapter ?? "",
"Matched Skills": p.matchedSkills.map((s) => `${s.skill} (${s.proficiency})`).join(", "),
}));
const ws = XLSX.utils.json_to_sheet(rows);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "People Finder");
XLSX.writeFile(wb, `people-finder-${Date.now()}.xlsx`);
const rows = [
["Name", "EID", "Chapter", "Matched Skills"],
...results.map((person) => [
person.displayName,
person.eid ?? "",
person.chapter ?? "",
person.matchedSkills.map((skill) => `${skill.skill} (${skill.proficiency})`).join(", "),
]),
];
await downloadWorkbook(`people-finder-${Date.now()}.xlsx`, "People Finder", rows);
}
return (