# Scaffold Viewer

> Scaffold a new data viewer page for a database table

- Skill: `ankit4479/scaffold-viewer` (Agent Skill)
- Install (CLI): `npx skillmds@latest add ankit4479/scaffold-viewer`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ankit4479/scaffold-viewer/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: ankit4479 (https://skillmd.com/u/ankit4479)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/ankit4479/scaffold-viewer

---


# 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:
1. What is the SQL table name? (e.g., `entities`, `source_cache`)
2. Is it tenant-scoped (has `tenant_id`)? Or global?
3. 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`

