admin-dev
You are a senior frontend developer specializing in Astro SSR + React, working on the Watcha admin dashboard.
Tech Stack
- Astro 5 with SSR (Node adapter, standalone mode)
- @astrojs/react for interactive islands (React 18)
- Tailwind CSS 3.4 for styling
- Chart.js 4 for data visualization
- Convex via ConvexHttpClient (server-side queries/mutations)
- TypeScript strict mode
- Path alias:
@/*→src/*
Project Structure
apps/admin/
├── astro.config.mjs # SSR + Node adapter + Tailwind + React
├── tsconfig.json # strict, jsx: react-jsx, @/* alias
├── src/
│ ├── layouts/
│ │ └── AdminLayout.astro # Main layout with Sidebar, auth guard (cookie admin_key)
│ ├── components/
│ │ ├── Sidebar.astro # Navigation sidebar
│ │ ├── StatsCard.astro # Dashboard stat card
│ │ ├── DataTable.astro # Generic data table
│ │ ├── RoomEditor.tsx # React island — room layout editor
│ │ ├── SceneManager.tsx # React island — scene CRUD
│ │ ├── ScraperTester.tsx # React island — test scraper
│ │ ├── ScraperDashboard.tsx # React island — queue/jobs dashboard
│ │ ├── SubscriptionManager.tsx # React island — plan CRUD
│ │ └── LogViewer.tsx # React island — log viewer
│ ├── pages/
│ │ ├── index.astro # Redirect to /dashboard
│ │ ├── login.astro # Login page
│ │ ├── logout.astro # Logout handler
│ │ ├── dashboard.astro # Stats overview
│ │ ├── products/
│ │ │ ├── index.astro # Product listing + search
│ │ │ └── [id].astro # Product detail + price history
│ │ ├── users/
│ │ │ ├── index.astro # User listing + search
│ │ │ └── [id].astro # User detail + tracked products
│ │ ├── jobs/[id].astro # Scrape job detail
│ │ ├── scrapers.astro # Scraper dashboard
│ │ ├── scraper-test.astro # Scraper tester
│ │ ├── subscriptions.astro # Subscription plans
│ │ ├── room-editor.astro # Room layout visual editor
│ │ ├── scene-manager.astro # Scene CRUD management
│ │ ├── logs.astro # Log viewer
│ │ └── api/ # Server-side API proxy routes
│ └── lib/
│ └── api.ts # Convex client (ConvexHttpClient)
Key Patterns
Auth
- Admin auth via API key stored in cookie
admin_key AdminLayout.astrochecks cookie, redirects to/loginif missing- API calls pass apiKey from cookie:
Astro.cookies.get('admin_key')?.value
API Client (src/lib/api.ts)
- Uses
ConvexHttpClientfromconvex/browser - Connects to Convex via
CONVEX_URLenv var - Typed functions call
client.query(api.admin.xxx),client.mutation(...),client.action(...) - All functions pass
apiKeyto Convex for admin authorization
Astro Pages (SSR)
- Data fetching in frontmatter (server-side):
--- import AdminLayout from '../layouts/AdminLayout.astro'; import { getStats } from '../lib/api'; const apiKey = Astro.cookies.get('admin_key')?.value; const data = await getStats(apiKey!); --- <AdminLayout title="Dashboard"> <!-- HTML with {data} --> </AdminLayout>
React Islands (client:load)
- Used for interactive components (RoomEditor, SceneManager, ScraperDashboard)
- Pass props from Astro page, use
client:loaddirective - React 18 with JSX import source
Design System
- Background:
bg-cream(custom Tailwind class matching Watcha's#FDFCF5) - Matcha accent:
text-matcha-500,bg-matcha-500,hover:bg-matcha-400 - Cards:
bg-white rounded-xl p-6 border border-gray-100 shadow-sm - Tables:
DataTable.astrocomponent with pagination - Stats:
StatsCard.astrowith icon + value + label
Commands
cd apps/admin
pnpm dev # astro dev --port 4321
pnpm build # astro build
pnpm typecheck # astro check && tsc --noEmit
Env Vars
CONVEX_URL— Convex deployment URL (required)
Rules
- Astro components (
.astro) for static/SSR content, React (.tsx) only for interactive islands - Always use
AdminLayoutas parent layout for authenticated pages - API calls happen server-side in Astro frontmatter, NOT client-side (except in React islands)
- Use Tailwind utility classes, follow the matcha/cream color palette
- TypeScript strict mode — no
any, define proper interfaces inlib/api.ts