add-department
Scaffold a full department with route, tabs, migration, and hub entry
SKILL.md.claude/skills/add-department/SKILL.md
GitHubAdd Department Skill
Purpose
Scaffold a complete new department in the Arch Systems portal, including route folder, tab pages, database migration, and hub registration.
Process
1. Ask for department details
- name — URL slug (kebab-case, e.g.
surveying) - displayName — Human-readable name (e.g.
Surveying) - icon — Lucide icon name (e.g.
Compass) - description — One-line description for the hub card
- color — Tailwind color:
amber,emerald,blue,violet,red,orange,cyan, orindigo - tabs — List of tab names and slugs (e.g.
[{ name: "dashboard", label: "Dashboard", icon: "BarChart2" }, ...]) - restricted — Is this department restricted to specific roles? (default: no)
2. Update apps/portal/lib/departments.ts
Add the department to the DEPARTMENTS array:
{
name: "<name>",
displayName: "<displayName>",
icon: "<icon>",
description: "<description>",
color: "<color>",
status: "active",
stats: { label: "<statLabel>", value: "<statValue>" },
}
Add the tab configuration constant:
export const <NAME>_TABS = [
{ name: "dashboard", label: "Dashboard", icon: "BarChart2" },
// ... tabs from user input
] as const;
Add the condition in getDepartmentTabs():
if (departmentName === "<name>") {
return <NAME>_TABS;
}
Add the type export:
export type <Name>Tab = typeof <NAME>_TABS[number];
3. Update the hub sidebar
In apps/portal/app/(hub)/layout.tsx, add the department to the sidebar nav if needed (most departments appear automatically via the DEPARTMENTS array).
If the department should appear in the "Advanced Systems" section instead of "Departments", add it manually like the satellite-monitoring entry.
4. Create route folder and page stubs
Create apps/portal/app/(departments)/[department]/ page files for each tab:
Dashboard page (apps/portal/app/(departments)/[department]/page.tsx):
import { createServerSupabaseClient } from "@repo/supabase/server";
import { GlassCard } from "@repo/ui/GlassCard";
import { DEPARTMENTS } from "~/lib/departments";
import { notFound } from "next/navigation";
export default async function <Name>DashboardPage({
params,
}: {
params: { department: string };
}) {
const dept = DEPARTMENTS.find((d) => d.name === params.department);
if (!dept) notFound();
const supabase = await createServerSupabaseClient();
const { data: department } = await supabase
.from("departments")
.select("id")
.eq("name", params.department)
.single();
if (!department) notFound();
const deptId = department.id;
const today = new Date().toISOString().split("T")[0];
return (
<div className="space-y-6">
<h2 className="text-2xl font-medium text-[var(--text-heading)]">{dept.displayName} Dashboard</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<GlassCard>
<p className="text-[var(--text-muted)] text-sm">Status</p>
<p className="text-2xl font-medium text-[var(--accent-green)] mt-1">Operational</p>
</GlassCard>
</div>
</div>
);
}
Sub-page (e.g. daily-log/page.tsx):
import { createServerSupabaseClient } from "@repo/supabase/server";
import { DEPARTMENTS } from "~/lib/departments";
import { notFound } from "next/navigation";
export default async function <Name><Tab>Page({
params,
}: {
params: { department: string };
}) {
const dept = DEPARTMENTS.find((d) => d.name === params.department);
if (!dept) notFound();
const supabase = await createServerSupabaseClient();
const { data: department } = await supabase
.from("departments")
.select("id")
.eq("name", params.department)
.single();
if (!department) notFound();
const deptId = department.id;
return (
<div className="space-y-6">
<h2 className="text-2xl font-medium text-[var(--text-heading)]"><TabLabel></h2>
{/* Add content here */}
</div>
);
}
5. Create database migration
Create a migration file in packages/database/migrations/:
-- Insert department
INSERT INTO departments (name, display_name, description)
VALUES ('<name>', '<displayName>', '<description>')
ON CONFLICT (name) DO NOTHING;
If the department needs new tables, use the create-migration skill to generate them with proper RLS policies.
6. Post-creation steps
- Run
pnpm --filter @repo/database supabase:pushto apply the migration - Verify the department appears in the portal at
/<name> - Verify tab navigation works
- Commit all changes
Design System Reminders
- Theme: Light-only macOS Sonoma. Never use dark hex colors (
#171717,#363636,#fafafa). - Text:
text-[var(--text-heading)]for headings,text-[var(--text-primary)]for body,text-[var(--text-secondary)]for labels,text-[var(--text-muted)]for metadata. - Backgrounds:
bg-white/70 backdrop-blur-xlfor glass surfaces. Cards use theGlassCardcomponent. - Shadows: Use
shadow-card/shadow-diffusion-*tokens. Never use Tailwindshadow-*or rawbox-shadow. - Never use
font-boldorfont-semibold— usefont-mediumfor emphasis. - Accent:
var(--accent-blue)(#007aff). Never use green accent colors. - Always use
cn()from@repo/ui/lib/utilsfor class merging. - Import Supabase clients from
@repo/supabase/*, never@supabase/supabase-js.