Scaffold Data Viewer
Interactive skill that scaffolds a new data viewer page for the data server.
Process
Step 1 -- Gather Table Info
Ask the user:
- What is the SQL table name? (e.g.,
entities, source_cache)
- Is it tenant-scoped (has
tenant_id)? Or global?
- What is the date filter column? (e.g.,
created_at, fetched_at, or none)
Step 2 -- Read the Schema
Read the Drizzle schema file for the table (in src/db/schema/). Extract all column names, types, and constraints.
Step 3 -- Generate ColumnDef Array
For each column in the schema, generate a ColumnDef entry following the conventions in ${CLAUDE_SKILL_DIR}/../../imp_doc/data-viewer/patterns.md and the example in ${CLAUDE_SKILL_DIR}/../../templates/column-defs.ts:
key: snake_case column name
label: Title Case display name
select: SQL expression (raw column name or transform like LEFT(col::text, 8))
dbColumn: raw column name for WHERE clauses
filterable: true for status/category columns with <20 distinct values
sortable: true for most columns
searchable: true for name/URL/text columns
mono: true for IDs, URLs, keys
numeric: true for counts, scores
date: true for timestamp columns
truncate: set for long text fields
link: true for URL columns
w: width in pixels (70 for IDs, 120 for status, 200 for names, 250 for URLs, 160 for dates)
Write the ColumnDef array to server/lib/column-defs.ts (append to existing file or add new export).
Step 4 -- Add DATE_FILTER_COLUMNS Entry
If the table has a date filter column, add an entry to DATE_FILTER_COLUMNS in server/lib/column-defs.ts.
Step 5 -- Create Route File
Create server/routes/{table-name}.ts using ${CLAUDE_SKILL_DIR}/../../templates/viewer-route.ts as the starting template. Also reference ${CLAUDE_SKILL_DIR}/../../imp_doc/data-viewer/patterns.md Section 10:
- Data endpoint:
GET /api/{table-name} using queryPaginated() from ${CLAUDE_SKILL_DIR}/../../templates/query-builder.ts
- Stats endpoint:
GET /api/{table-name}/stats using queryStats() with 5-min cache
- Zod schema for query params (page, limit, sort, order, search, filter[], date_from, date_to)
- Global table handling if not tenant-scoped
Step 6 -- Mount Route
Add app.route("/api/{table-name}", route) in server/index.ts.
Step 7 -- Create HTML Page
Create server/public/{table-name}.html from ${CLAUDE_SKILL_DIR}/../../templates/data-viewer-page.html:
- Set
apiPath, title, columns in VIEWER_CONFIG
- Configure funnel chart if the table has a natural funnel flow (optional)
- Page loads
shared.js (${CLAUDE_SKILL_DIR}/../../templates/shared.js) and shared.css (${CLAUDE_SKILL_DIR}/../../templates/shared.css)
Step 8 -- Update Sidebar
Add the table to the sidebar navigation in server/public/shared.js (see ${CLAUDE_SKILL_DIR}/../../templates/shared.js for framework). Add entry to the appropriate nav group.
Step 9 -- Update Dashboard Stats
Add the table to the dashboard stats registry in server/routes/dashboard.ts.
Step 10 -- Report
Show the user:
- Files created/modified
- How to test:
npm run server:dev then open http://localhost:3100/{table-name}.html
- Suggest running the schema drift test to verify column alignment
Rules
- Always read the existing column-defs.ts and route files first to match patterns
- Use the same Zod schema pattern as existing routes
- ColumnDef keys must match SQL column names (converted to snake_case)
- Global tables (no tenant_id) use a different Zod schema without
tenant_id
- Test the page loads before reporting success
- Cross-ref:
${CLAUDE_SKILL_DIR}/../../imp_doc/data-viewer/patterns.md, ${CLAUDE_SKILL_DIR}/../../templates/data-viewer-server.ts, ${CLAUDE_SKILL_DIR}/../../templates/query-builder.ts, ${CLAUDE_SKILL_DIR}/../../templates/column-defs.ts, ${CLAUDE_SKILL_DIR}/../../templates/viewer-route.ts, ${CLAUDE_SKILL_DIR}/../../templates/shared.js, ${CLAUDE_SKILL_DIR}/../../templates/shared.css, ${CLAUDE_SKILL_DIR}/../../templates/data-viewer-page.html
1---2name: scaffold-viewer3description: Scaffold a new data viewer page for a database table4---56# Scaffold Data Viewer78Interactive skill that scaffolds a new data viewer page for the data server.910## Process1112### Step 1 -- Gather Table Info1314Ask the user:151. What is the SQL table name? (e.g., `entities`, `source_cache`)162. Is it tenant-scoped (has `tenant_id`)? Or global?173. What is the date filter column? (e.g., `created_at`, `fetched_at`, or none)1819### Step 2 -- Read the Schema2021Read the Drizzle schema file for the table (in `src/db/schema/`). Extract all column names, types, and constraints.2223### Step 3 -- Generate ColumnDef Array2425For each column in the schema, generate a `ColumnDef` entry following the conventions in `${CLAUDE_SKILL_DIR}/../../imp_doc/data-viewer/patterns.md` and the example in `${CLAUDE_SKILL_DIR}/../../templates/column-defs.ts`:2627- `key`: snake_case column name28- `label`: Title Case display name29- `select`: SQL expression (raw column name or transform like `LEFT(col::text, 8)`)30- `dbColumn`: raw column name for WHERE clauses31- `filterable`: true for status/category columns with <20 distinct values32- `sortable`: true for most columns33- `searchable`: true for name/URL/text columns34- `mono`: true for IDs, URLs, keys35- `numeric`: true for counts, scores36- `date`: true for timestamp columns37- `truncate`: set for long text fields38- `link`: true for URL columns39- `w`: width in pixels (70 for IDs, 120 for status, 200 for names, 250 for URLs, 160 for dates)4041Write the ColumnDef array to `server/lib/column-defs.ts` (append to existing file or add new export).4243### Step 4 -- Add DATE_FILTER_COLUMNS Entry4445If the table has a date filter column, add an entry to `DATE_FILTER_COLUMNS` in `server/lib/column-defs.ts`.4647### Step 5 -- Create Route File4849Create `server/routes/{table-name}.ts` using `${CLAUDE_SKILL_DIR}/../../templates/viewer-route.ts` as the starting template. Also reference `${CLAUDE_SKILL_DIR}/../../imp_doc/data-viewer/patterns.md` Section 10:50- Data endpoint: `GET /api/{table-name}` using `queryPaginated()` from `${CLAUDE_SKILL_DIR}/../../templates/query-builder.ts`51- Stats endpoint: `GET /api/{table-name}/stats` using `queryStats()` with 5-min cache52- Zod schema for query params (page, limit, sort, order, search, filter[], date_from, date_to)53- Global table handling if not tenant-scoped5455### Step 6 -- Mount Route5657Add `app.route("/api/{table-name}", route)` in `server/index.ts`.5859### Step 7 -- Create HTML Page6061Create `server/public/{table-name}.html` from `${CLAUDE_SKILL_DIR}/../../templates/data-viewer-page.html`:62- Set `apiPath`, `title`, `columns` in VIEWER_CONFIG63- Configure funnel chart if the table has a natural funnel flow (optional)64- Page loads `shared.js` (`${CLAUDE_SKILL_DIR}/../../templates/shared.js`) and `shared.css` (`${CLAUDE_SKILL_DIR}/../../templates/shared.css`)6566### Step 8 -- Update Sidebar6768Add the table to the sidebar navigation in `server/public/shared.js` (see `${CLAUDE_SKILL_DIR}/../../templates/shared.js` for framework). Add entry to the appropriate nav group.6970### Step 9 -- Update Dashboard Stats7172Add the table to the dashboard stats registry in `server/routes/dashboard.ts`.7374### Step 10 -- Report7576Show the user:77- Files created/modified78- How to test: `npm run server:dev` then open `http://localhost:3100/{table-name}.html`79- Suggest running the schema drift test to verify column alignment8081## Rules8283- Always read the existing column-defs.ts and route files first to match patterns84- Use the same Zod schema pattern as existing routes85- ColumnDef keys must match SQL column names (converted to snake_case)86- Global tables (no tenant_id) use a different Zod schema without `tenant_id`87- Test the page loads before reporting success88- Cross-ref: `${CLAUDE_SKILL_DIR}/../../imp_doc/data-viewer/patterns.md`, `${CLAUDE_SKILL_DIR}/../../templates/data-viewer-server.ts`, `${CLAUDE_SKILL_DIR}/../../templates/query-builder.ts`, `${CLAUDE_SKILL_DIR}/../../templates/column-defs.ts`, `${CLAUDE_SKILL_DIR}/../../templates/viewer-route.ts`, `${CLAUDE_SKILL_DIR}/../../templates/shared.js`, `${CLAUDE_SKILL_DIR}/../../templates/shared.css`, `${CLAUDE_SKILL_DIR}/../../templates/data-viewer-page.html`