From e8b18276388ab4c6d88367537e9997d8d6acf060 Mon Sep 17 00:00:00 2001 From: asepharyana Date: Thu, 2 Jul 2026 00:36:30 +0700 Subject: [PATCH] docs: add Astro migration design spec --- .../2026-07-02-astro-migration-design.md | 359 ++++++++++++++++++ 1 file changed, 359 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-02-astro-migration-design.md diff --git a/docs/superpowers/specs/2026-07-02-astro-migration-design.md b/docs/superpowers/specs/2026-07-02-astro-migration-design.md new file mode 100644 index 0000000..84b58ff --- /dev/null +++ b/docs/superpowers/specs/2026-07-02-astro-migration-design.md @@ -0,0 +1,359 @@ +# Astro Migration — BETE Design System Implementation + +> Migrasi frontend React SPA ke Astro SSG dengan React islands, mengikuti design system di `design/`. + +**Date:** 2026-07-02 +**Status:** Draft +**Owner:** @asephs + +--- + +## 🎯 Ringkasan + +Migrasi frontend dari React 19 + Vite (+ Tailwind 4) ke **Astro SSG** dengan React islands untuk komponen interaktif. Mengganti `services/frontend` yang sekarang dengan struktur Astro yang baru. Semua desain dari `design/` (16 dokumen) diimplementasikan sebagai fondasi visual. + +--- + +## 🔗 Referensi Desain + +Dokumen desain yang menjadi acuan implementasi: + +| Dokumen | Konsep Kunci | +|---------|-------------| +| `core/01-color-system.md` | OKLCH colors, glassmorphism, semantic tokens | +| `core/02-typography.md` | Fluid type scale (clamp), Outfit + JetBrains Mono | +| `core/03-spatial-system.md` | 4px baseline grid, spacing/radius/z-index tokens | +| `core/04-motion-system.md` | Easing curves, duration tokens, micro-interactions | +| `core/05-component-architecture.md` | Atomic design taxonomy | +| `patterns/06-interaction-patterns.md` | Interaction feedback matrix, keyboard shortcuts | +| `patterns/09-state-machines.md` | Quad-state pattern (loading/error/empty/success) | +| `patterns/10-responsive-system.md` | Breakpoints, mobile-nav, container queries | +| `services/11-frontend-ui.md` | Tech stack, Tailwind config, glass utilities | +| `system/15-theme-architecture.md` | CSS vars, dark/light theme switching | + +--- + +## 🛠️ Tech Stack + +| Tool | Versi | Peran | +|------|-------|-------| +| Astro | 5.x | Meta-framework, SSG, routing, component model | +| React | 19.x | Interactive islands (WebSocket, canvas, state) | +| TypeScript | 5.x | Type safety | +| Tailwind CSS | 4.x | Utility-first CSS (`@theme` block) | +| Zustand | 5.x | Client state (UI, voice, messages) | +| TanStack Query | 5.x | Server state (API data fetching) | +| Radix UI | — | Headless primitives (Modal, Dropdown, Tabs) | +| Framer Motion | 11.x | Complex animations (islands only) | +| Three.js | 0.170+ | Particle background (deferred island) | + +--- + +## 🚧 Pendekatan: Astro-First + React Islands + +**Prinsip:** +- Semua komponen **non-interaktif** → `.astro` (zero JavaScript) +- Komponen **interaktif** → React islands (`src/islands/`) dengan client directives +- Routing → file-based Astro pages +- Data fetching → REST API dari browser (SSG tidak punya backend runtime) + +### React Islands per Fitur + +| Island | Client Directive | Alasan | +|--------|-----------------|--------| +| AuthGuard | `client:load` | Cek auth state on mount | +| VoiceControls | `client:load` | WebSocket + voice interaction | +| AudioVisualizer | `client:idle` | Canvas-based, bukan prioritas awal | +| MessageFeed | `client:load` | WebSocket + infinite scroll | +| MascotChat | `client:idle` | Secondary feature | +| ThemeToggle | `client:idle` | Non-critical UI | +| Particles | `client:idle` | Background decoration | +| ActiveSpeakers | `client:idle` | WS-driven list | +| NowPlaying | `client:idle` | Media state | + +--- + +## 📁 Struktur Proyek + +``` +services/frontend/ +├── astro.config.ts +├── package.json +├── tsconfig.json +├── src/ +│ ├── pages/ +│ │ ├── index.astro → Dashboard (Live tab sebagai default) +│ │ ├── live.astro → Live panel +│ │ ├── messages.astro → Message feed +│ │ ├── settings.astro → Settings +│ │ ├── login.astro → Auth page +│ │ ├── recordings.astro → Recordings +│ │ └── 404.astro +│ │ +│ ├── layouts/ +│ │ ├── DashboardLayout.astro # Sidebar + Header + +│ │ └── AuthLayout.astro # Minimal, centered card +│ │ +│ ├── components/ # Astro components — zero JS +│ │ ├── ui/ +│ │ │ ├── Button.astro +│ │ │ ├── Badge.astro +│ │ │ ├── SeverityBadge.astro +│ │ │ ├── Card.astro +│ │ │ ├── Skeleton.astro +│ │ │ └── Spinner.astro +│ │ ├── sidebar/ +│ │ │ ├── Sidebar.astro +│ │ │ └── NavItem.astro +│ │ ├── header/ +│ │ │ └── Header.astro +│ │ └── states/ +│ │ ├── EmptyState.astro +│ │ ├── ErrorState.astro +│ │ └── LoadingSkeleton.astro +│ │ +│ ├── islands/ # React components — interactive +│ │ ├── AuthGuard.tsx # client:load +│ │ ├── VoiceControls.tsx # client:load +│ │ ├── AudioVisualizer.tsx # client:idle +│ │ ├── MessageFeed.tsx # client:load +│ │ ├── MascotChat.tsx # client:idle +│ │ ├── ThemeToggle.tsx # client:idle +│ │ ├── Particles.tsx # client:idle +│ │ ├── ActiveSpeakers.tsx # client:idle +│ │ └── NowPlaying.tsx # client:idle +│ │ +│ ├── stores/ # Zustand stores +│ │ ├── ui-store.ts +│ │ ├── voice-store.ts +│ │ └── message-store.ts +│ │ +│ ├── shared/ +│ │ ├── api/client.ts # REST API calls +│ │ ├── ws/socket.ts # WebSocket manager +│ │ ├── hooks/ +│ │ │ ├── useMessages.ts +│ │ │ ├── useVoiceStatus.ts +│ │ │ ├── useMediaControl.ts +│ │ │ ├── useUIState.ts +│ │ │ └── useReducedMotion.ts +│ │ └── lib/utils.ts # cn(), formatters +│ │ +│ └── styles/ +│ └── base.css # Tailwind + design tokens + glass +│ +└── public/ + └── favicon.svg +``` + +--- + +## 🎨 CSS Architecture + +### Layer Stack + +``` +base.css + ├── @import "tailwindcss" + ├── @theme {} → Tailwind 4 semantic tokens + ├── :root {} → Dark theme CSS vars (OKLCH) + ├── [data-theme="light"] {} → Light theme overrides + ├── @layer base → Reset, font-face, scrollbar styling + ├── @layer components → Glass, gradient-text, typography classes + └── @layer utilities → Animations keyframes +``` + +### CSS Variables: Prefix Categories + +| Kategori | Prefix | Contoh | +|----------|--------|--------| +| Warna | `--clr-*` | `--clr-surface-base`, `--clr-primary` | +| Spacing | `--sp-*` | `--sp-3` (16px), `--sp-5` (32px) | +| Typography | `--fs-*`, `--fw-*`, `--lh-*` | `--fs-base`, `--fw-semibold` | +| Radius | `--rd-*` | `--rd-md` (8px), `--rd-xl` (16px) | +| Shadow | `--sh-*` | `--sh-card`, `--sh-modal` | +| Z-index | `--z-*` | `--z-header` (30), `--z-modal` (60) | +| Timing | `--dur-*` | `--dur-fast` (150ms) | +| Easing | `--ease-*` | `--ease-out`, `--ease-out-quint` | + +### Glass Utility Classes + +```css +.glass { + background: oklch(from var(--clr-surface-elevated) l c h / 0.6); + backdrop-filter: blur(16px); + border: 1px solid oklch(from var(--clr-border) l c h / 0.2); +} +.gradient-text { /* ... */ } +``` + +--- + +## 🧩 Component Design (Key Components) + +### Button (Astro) + +```astro +--- +export interface Props { + variant?: 'primary' | 'secondary' | 'destructive' | 'outline' | 'ghost'; + size?: 'sm' | 'default' | 'lg' | 'icon'; + disabled?: boolean; + class?: string; +} + +const { variant = 'primary', size = 'default', disabled = false, class: className = '' } = Astro.props; +--- + + + + +``` + +### Card (Astro — Slot-based) + +```astro +--- +export interface Props { + variant?: 'default' | 'elevated' | 'glass' | 'interactive'; + padding?: 'sm' | 'default' | 'lg' | 'none'; + class?: string; +} +--- +
+ +
+ + +``` + +### MessageFeed (React Island — most complex) + +```tsx +// islands/MessageFeed.tsx +// Keeps the existing MessageFeed logic but imports types from @bete/shared +// Uses useInfiniteQuery for cursor pagination +// Subscribes to WebSocket via SocketManager for real-time updates +// Integrates with Zustand message-store for optimistic updates +// Renders: cards with severity badges, action buttons, attachment previews +// States: loading → skeleton, error → retry, empty → mascot, success → list + +interface MessageFeedProps { + channelId?: string; +} +``` + +--- + +## 🔌 Data Flow + +### WebSocket Architecture + +``` +Browser → WebSocket → Backend (Express + ws) + ↑ ↓ + └─────────────────────────┘ + (real-time events via SocketManager) +``` + +- `SocketManager` singleton dengan exponential backoff reconnect +- Events dipetakan ke Zustand stores → trigger React re-render +- REST API untuk initial data fetch (TanStack Query) + +### Authentication + +- Password disimpan di localStorage +- `X-Admin-Password` header di semua API calls +- AuthGuard island: cek localStorage, verify via `/api/health`, redirect ke login + +--- + +## 📱 Responsive Behavior + +| Viewport | Sidebar | Header | Grid | Navigation | +|----------|---------|--------|------|------------| +| < 640px | Bottom tab (56px) | Compact | 1 col | Tab bar | +| 640–768px | Bottom tab | Compact | 1-2 col | Tab bar | +| 768–1024px | Icon (64px) | Standard | 2-3 col | Sidebar icon | +| 1024–1280px | Full (256px) | Standard | 3 col | Sidebar full | +| 1280px+ | Full (256px) | Full | 3-4 col | Sidebar full | + +--- + +## 📦 Fase Migrasi (5 Fase) + +### Fase 1: Foundation 🔧 +- Inisialisasi Astro + React integration +- `base.css` dengan design tokens + Tailwind 4 `@theme` +- DashboardLayout, Sidebar, Header (Astro komponen) +- Theme switching (island) + +### Fase 2: UI Component Library 🧱 +- Semua Astro components: Button, Badge, Card, Skeleton, SeverityBadge +- State components: EmptyState, ErrorState, LoadingSkeleton +- Glass utilities + animation keyframes + +### Fase 3: Auth & Messages 📨 +- AuthGuard island (login, localStorage) +- MessageFeed island (TanStack Query + WebSocket) +- Infinite scroll + real-time updates + +### Fase 4: Live / Voice 🎤 +- VoiceControls island (connect/disconnect) +- ActiveSpeakers, AudioVisualizer, NowPlaying islands +- Recordings page + +### Fase 5: Polish ✨ +- 3D Particles (three.js, deferred) +- MascotChat island +- View Transitions +- Settings page +- Mobile bottom tab bar + +--- + +## ⚠️ Known Risks & Mitigations + +| Risk | Dampak | Mitigasi | +|------|--------|----------| +| WebSocket reconnect di SSG | Koneksi terputus saat navigasi | SocketManager global singleton persist across islands | +| Bundle size React islands | JS besar di halaman interaktif | Split per halaman, gunakan `client:idle` bila memungkinkan | +| Auth check blocking render | Flash of login page | AuthGuard render spinner dulu, baru cek localStorage | +| Design token mismatch | Warna/spasi berbeda dari desain | CSS variables sebagai single source of truth, verifikasi visual tiap fase | +| react-three-fiber compatibility | Mungkin perlu workaround SSR | Island di-defer via `client:idle`, Three.js murni client-side | + +--- + +## ✅ Spec Self-Review + +- **Placeholder scan:** All sections filled. No TBD/TODO left. +- **Consistency:** All design tokens reference the same CSS variable naming from `design/`. Component architecture matches atomic taxonomy in `05-component-architecture.md`. State patterns follow `09-state-machines.md`. +- **Scope:** Focused on frontend migration only. Backend/gateway unchanged. All existing features preserved. +- **Ambiguity resolved:** + - Astro output mode: SSG (confirmed) + - React islands scope: only truly interactive components (confirmed) + - All existing features retained (confirmed) + +--- + +*Spec ditulis berdasarkan brainstorming dan persetujuan 7 section design.*