"Deploy a live webapp dashboard for any BEUP Google Sheet/Excel template. Trigger when user says 'create dashboard webapp for [template]', 'deploy dashboard for [product]', 'add dashboard route', 'tạo webapp dashboard', or wants to turn an existing Claude artifact dashboard into a deployable Vercel webapp. This skill handles the full workflow: analyze template structure, generate template-specific dashboard component, register route, and prepare for Vercel deploy. Requires the dashboard-builder skill for data analysis and chart selection. Output is a deployable Vite+React project."
Resources
13Install
npx skillscat add beupspace-tool/beup-dashboards Install via the SkillsCat registry.
Dashboard Deploy Skill
Turn any BEUP Google Sheet/Excel template into a deployed live dashboard webapp at dashboard.beup.space/{template-id}.
Prerequisites
dashboard-builderskill installed (for data analysis, chart selection, design system)- Webapp project scaffold exists (see
references/webapp-scaffold/) - Vercel account connected
Workflow: Adding a New Template Dashboard
Step 1: Analyze the Template
Read the Google Sheet / Excel template to understand:
- Which sheets contain raw data (equivalent to BOOKINGS, EXPENSES in Airbnb)
- Column names and data types
- What KPIs are already computed in DASHBOARD/ANALYSIS sheets
- Domain (for accent color selection from
dashboard-builder/references/dashboard-design.md)
Use the dashboard-builder skill Phase 2 (PROFILE) for this.
Step 2: Define the Template Config
Create a config object:
// For a new template, define:
const TEMPLATE_CONFIG = {
id: 'art-gallery', // URL route: /art-gallery
name: 'Art Gallery Management',
icon: '🎨',
accent: 'violet', // from domain-adaptive palette
sheets: { // Google Sheets tab names
artworks: 'Kho Tranh',
artists: 'Hoạ sĩ',
transactions: 'Giao dịch',
},
buyUrl: 'https://beup.space/product/art-gallery',
};Step 3: Generate Dashboard Component
Copy src/dashboards/airbnb.jsx as template. Modify these sections:
- SHEET_NAMES — map to your template's sheet names
- parseFn — column name mapping (e.g.,
r['Tên tranh']instead ofr['Property']) - sN (short name function) — generate short display names for dimension values
- aggFn — aggregation logic specific to this domain's KPIs
- Render section — charts, KPIs, tabs specific to this domain
The dashboard-builder skill's Phase 3 (SUGGEST) helps decide which charts and KPIs to include.
Step 4: Register Route
In src/main.jsx, add import + route (1 line each):
import artGalleryConfig from './dashboards/art-gallery.config.js';
import ArtGalleryDashboard from './dashboards/ArtGalleryDashboard.jsx';
// in router:
<Route path="/art-gallery" element={<DashboardShell config={artGalleryConfig} Dashboard={ArtGalleryDashboard} />} />Add card in src/Home.jsx templates list.
Step 5: Test Locally
cd webapp-project
npm install
npm run dev
# Open http://localhost:5173/art-gallery
# Paste Google Sheet URL → verify dashboard renders
# Upload Excel file → verify dashboard rendersStep 6: Deploy
vercel --prodOr push to GitHub → Vercel auto-deploys.
Architecture Overview (Actual)
dashboard-deploy/
├── index.html ← loads DM Sans from Google Fonts
├── package.json ← React, Recharts, Papaparse, SheetJS
├── vite.config.js
├── vercel.json ← SPA rewrite
└── src/
├── main.jsx ← Router — 1 import + 1 Route per template
├── Home.jsx ← Landing page listing all dashboards
├── DashboardShell.jsx ← SHARED: setup screen + Google Sheets fetch + Excel upload
└── dashboards/
├── airbnb.config.js ← parse columns, aggregate fn, sheet names
├── AirbnbDashboard.jsx ← UI: charts, KPIs, filters, i18n
├── {name}.config.js ← add per template
└── {Name}Dashboard.jsx ← add per templateAdding a new template = 2 new files + 2 lines in main.jsx + 1 card in Home.jsx
What's Shared vs Template-Specific
| Shared | Template-specific |
|---|---|
DashboardShell.jsx — fetch, parse Excel, URL hash |
Column name mapping in config |
Home.jsx — template listing |
parseFn — field extraction |
main.jsx — routing |
aggFn — KPI & chart data computation |
| i18n pattern (copy LANGS from AirbnbDashboard) | Chart layout, KPI selection |
Data Flow
DashboardShell: user pastes Sheet URL or uploads file
↓ fetches CSV, parses Excel → calls config.parse(rawSheets)
↓ parsed = { bookings, expenses, properties, filters }
↓ passes parsed as prop to {Name}Dashboard
{Name}Dashboard:
useMemo → computeAgg(parsed, filters) → { mo, bp, bpl, kpi }
render: Header + LangSelector + Filters + Hero KPI + Charts + Footeri18n
All dashboard components support 6 languages via LANGS object: EN, VI, DE, FR, PT, ES.
- Copy
LANGSpattern fromAirbnbDashboard.jsx - Use fixed
dataKeyin Recharts (e.g."rev","exp"), translatednameprop only for legend labels
Privacy Architecture
User browser ──fetch──→ Google Sheets CSV endpoint
(direct, no proxy)
↓
All parsing + aggregation happens IN BROWSER
↓
Sheet ID stored in URL hash (#sheet=XXX)
Hash fragments are NEVER sent to server per HTTP spec
↓
BEUP server (Vercel) serves static HTML/JS only
BEUP never sees, stores, or processes any user dataLessons Learned (bugs fixed in Airbnb pilot)
| Bug | Root Cause | Fix |
|---|---|---|
| Property filter didn't update KPI cards | allProperties built from bookings + expenses — names could mismatch |
Build filter options from bookings only |
| Properties tab showed all props with 0 values when filtered | bp always built from all properties |
Filter bp to selected property when filters.prop !== 'all' |
| Revenue & Expenses bars same color | Recharts dataKey was dynamic translated string — color mapping broke on lang change |
Use fixed dataKey ("rev", "exp"), translated name for legend only |
kpi.props always showed total count |
properties.length not filtered |
Use 1 when prop filter active, else count unique props in filtered bookings |
Checklist: Before Deploying a New Dashboard
- All sheets fetch correctly (test with published Google Sheet)
- Excel upload parses correctly (test with .xlsx download of template)
- Column name matching handles edge cases (empty cells, extra whitespace)
- Filters work — each dimension chip filters all charts
- Dark/light mode renders correctly
- Mobile responsive (test at 375px width)
- Error states: unpublished sheet, wrong file format, empty data
- "Change source" button works — returns to setup screen
- Buy URL points to correct BEUP product page
- Footer shows "Powered by BEUP"
Currency / Locale Handling
| Template market | Currency | Number format | Use |
|---|---|---|---|
| English (US/intl) | USD ($) | 1,000.00 | fmt(n, '$') |
| Vietnamese | VND (₫) | 1.000.000 | fmtVND(n) |
| Spanish (LATAM) | USD ($) | 1,000.00 | fmt(n, '$') |
Detect from template language or add to TEMPLATE_CONFIG.