Skip to main content
ClaudeWave
Skill5.5k repo starsupdated 11d ago

build-dashboard

build-dashboard generates the client-side UI layer for holaOS dashboard applications using TanStack Start and the @holaboss/ui component library. Use this skill after app-builder-sdk has wired SDK primitives into app.ts and the app needs a functional src/client/ directory to display queues, tables, kanban boards, forms, or calendars, not for marketing pages or static HTML reports.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/holaboss-ai/holaOS /tmp/build-dashboard && cp -r /tmp/build-dashboard/runtime/harnesses/src/embedded-skills/build-dashboard ~/.claude/skills/build-dashboard
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

# build-dashboard

The agent before you reliably produces ugly dashboards because it starts from a blank page and reaches for default shapes (single-column full-width cards, KPI strips that don't fit, sidebars that don't earn their space). This skill exists to **bypass that default**. The visual decisions are already made — your job is to fill in the data and copy.

## When to use

- The user asked for a dashboard, workspace pane, list view, kanban, calendar, or "let me see my X"
- The app's `app.ts` already declares `resource(...)` rows via the SDK (set up via the `app-builder-sdk` skill first)
- The app dir needs a `src/client/` directory

**Skip this skill when:**
- Integration-only module (Slack/Discord/Stripe-style MCP-only) — those use only `app-builder-sdk`
- Marketing landing page → use `frontend-design`
- One-off static HTML report → not this product class

## The two non-negotiables

1. **Copy the bundled reference. Don't invent.** `reference/messaging-dashboard/src/client/` is the canonical starting point. Three files below are verbatim across every dashboard; the rest gets customized per shape.
2. **Two style imports, not one.** `@holaboss/ui/styles.css` only bakes in utilities used inside the library. Every Tailwind class your `src/client/` writes needs your own app-side compile pass. The register-time lint `workspace_app_missing_tailwind_compile` rejects apps missing this.

## The shape catalog — pick one

Look at the user's data, NOT at "what dashboards usually have". Most apps are shape 1.

| # | Shape | Pick when the data is… | Template |
|---|---|---|---|
| 1 | **Queue / feed** | scheduled items, drafts, an action queue, an activity log, anything time-ordered | `reference/messaging-dashboard/` (full, ready to copy) |
| 2 | **Dense table** | flat records (CRM contacts, log rows, ticket list) that the user scans like a spreadsheet | Replace shape-1's `messages-table.tsx` with the `<Table>` primitive (see snippet below) |
| 3 | **Kanban** | rows that move between named statuses; user drags between columns | Replace shape-1's main column with horizontal status columns (see snippet below) |
| 4 | **Detail / form** | a single resource the user edits or watches in depth | Replace shape-1's main column with `<Field>` form (see snippet below) |
| 5 | **Calendar week** | rows with `start_time` + duration that pin to a day-grid | Replace shape-1's main column with `@holaboss/ui`'s `Calendar` primitive |

Shapes 2–5 still keep shape 1's header, app.css, connection pill, status badge, and tokens. **Only the main content area changes.**

ASCII for shape 1 (the most common, read this even if you're using another shape):

```
                ┌────────────────────────────────────────────┐
                │  Outgoing ●            ● Connected · @jot  │  ← header
                │  5 queued · agent will send on schedule    │
                │                                            │
                │  NEEDS ATTENTION                           │
                │  ┌──────────────────────────────────┐     │  ← attention strip
                │  │ #ops · Failed · 3h ago           │     │    (warning-bordered,
                │  │  Composio retry exhausted…       │     │     always-visible Retry)
                │  │                  [Retry] [Edit]  │     │
                │  └──────────────────────────────────┘     │
                │                                            │
                │  TODAY ───────────────────────────── 02   │  ← day divider
                │  NOW · 08:42 ─────────────────────────    │  ← "now" cursor on rail
                │  09:00 ● #general · ● Scheduled            │  ← next-up marker
                │        Heads-up: pricing page goes live…  │
                │  17:00 · #growth · ● Draft                 │
                │        Weekly recap — KPI strip…           │
                │                                            │
                │  TOMORROW ────────────────────────── 02   │
                │  …                                         │
                └────────────────────────────────────────────┘
                       (max-w-3xl centered on bg-background)
```

## Foundation — paste verbatim into every dashboard

These five files do NOT vary per shape. Copy them exactly.

### `src/client/app.css` (13 lines)

```css
/* App-local Tailwind compile entry.
 *
 * `@holaboss/ui/styles.css` only bakes in utilities used INSIDE the library.
 * Every Tailwind class your `src/client/` writes (max-w-3xl, grid-cols-*,
 * text-fg-48, bg-card, flex-1, etc.) needs an app-side compile pass to land
 * in the bundle. Without it the page renders mostly unstyled.
 *
 * Required by the register-time lint `workspace_app_missing_tailwind_compile`.
 */

@import "tailwindcss";

@source "../client";
```

### `src/client/routes/__root.tsx`

```tsx
import "@holaboss/ui/styles.css"
import "../app.css"

import type { ReactNode } from "react"

export function RootLayout({ children }: { children: ReactNode }) {
  return (
    <html lang="en" data-theme="holaos-light">
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width,initial-scale=1" />
        <title>Your App — holaOS</title>
      </head>
      <body className="antialiased">{children}</body>
    </html>
  )
}
```

### `src/client/components/connection-pill.tsx`

```tsx
import { StatusDot } from "@holaboss/ui"

type Props = {
  state: "ready" | "needs_connect" | "needs_reauth" | "checking"
  handle?: string
}

const COPY: Record<Props["state"], { label: string; tone: "success" | "warning" | "muted" }> = {
  ready: { label: "Connected", tone: "success" },
  needs_connect: { label: "Not connected", tone: "warning" },
  needs_reauth: { label: "Reauth required", tone: "warning" },
  checking: { label: "Checking…", tone: "muted" },
}

export function ConnectionPill({ state, handle }: Props) {
  const { label, tone } = COPY[state]
  return (
    <span className="inline-f