+ Belajar mengenali penyakit daun jagung dengan alur digital yang rapi. +
++ Scaffold ini menyiapkan fondasi aplikasi ZeaVis Edu untuk antarmuka edukasi, + API, dan integrasi model machine learning berikutnya. +
+From 99285d1663858602f071701c95414fee0c7b00ac Mon Sep 17 00:00:00 2001 From: Asep Haryana Saputra <90584806+MythEclipse@users.noreply.github.com> Date: Fri, 22 May 2026 13:05:59 +0000 Subject: [PATCH] Finalize scaffold lockfile and implementation plan --- bun.lock | 18 +- .../2026-05-22-fullstack-monorepo-scaffold.md | 1325 +++++++++++++++++ package.json | 8 +- 3 files changed, 1337 insertions(+), 14 deletions(-) mode change 100644 => 100755 bun.lock create mode 100644 docs/superpowers/plans/2026-05-22-fullstack-monorepo-scaffold.md diff --git a/bun.lock b/bun.lock old mode 100644 new mode 100755 index 827ea9f..bf7dbd9 --- a/bun.lock +++ b/bun.lock @@ -1,9 +1,7 @@ { - "lockfileVersion": 1, - "configVersion": 1, + "lockfileVersion": 0, "workspaces": { "": { - "name": "zeavis-edu", "devDependencies": { "@moonrepo/cli": "^1.32.4", "typescript": "^5.6.3", @@ -13,7 +11,7 @@ "name": "@zeavis/api", "version": "0.1.0", "dependencies": { - "@zeavis/shared": "workspace:*", + "@zeavis/shared": "packages/shared", "drizzle-orm": "^0.36.4", "elysia": "^1.1.25", "postgres": "^3.4.5", @@ -31,7 +29,7 @@ "@radix-ui/react-slot": "^1.1.0", "@tanstack/react-query": "^5.59.16", "@vitejs/plugin-react": "^4.3.3", - "@zeavis/shared": "workspace:*", + "@zeavis/shared": "packages/shared", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "lucide-react": "^0.468.0", @@ -282,11 +280,11 @@ "@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="], - "@zeavis/api": ["@zeavis/api@workspace:apps/api"], + "@zeavis/api": ["@zeavis/api@workspace:apps/api", { "dependencies": { "@zeavis/shared": "packages/shared", "drizzle-orm": "^0.36.4", "elysia": "^1.1.25", "postgres": "^3.4.5" }, "devDependencies": { "@types/bun": "latest", "drizzle-kit": "^0.27.1", "typescript": "^5.6.3" } }], - "@zeavis/shared": ["@zeavis/shared@workspace:packages/shared"], + "@zeavis/shared": ["@zeavis/shared@workspace:packages/shared", { "devDependencies": { "typescript": "^5.6.3" } }], - "@zeavis/web": ["@zeavis/web@workspace:apps/web"], + "@zeavis/web": ["@zeavis/web@workspace:apps/web", { "dependencies": { "@radix-ui/react-slot": "^1.1.0", "@tanstack/react-query": "^5.59.16", "@vitejs/plugin-react": "^4.3.3", "@zeavis/shared": "packages/shared", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "lucide-react": "^0.468.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.28.0", "tailwind-merge": "^2.5.4", "zustand": "^5.0.1" }, "devDependencies": { "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "autoprefixer": "^10.4.20", "postcss": "^8.4.49", "tailwindcss": "^3.4.15", "typescript": "^5.6.3", "vite": "^6.0.1" } }], "any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="], @@ -328,7 +326,7 @@ "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], - "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], @@ -338,7 +336,7 @@ "drizzle-kit": ["drizzle-kit@0.27.2", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.19.7", "esbuild-register": "^3.5.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-F6cFZ1wxa9XzFyeeQsp/0/lIzUbDuQjS8/njpYBDWa+wdWmXuY+Z/X2hHFK/9PGHZkv3c9mER+mVWfKlp/B6Vw=="], - "drizzle-orm": ["drizzle-orm@0.36.4", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=3", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/react": ">=18", "@types/sql.js": "*", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "prisma": "*", "react": ">=18", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/react", "@types/sql.js", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "knex", "kysely", "mysql2", "pg", "postgres", "prisma", "react", "sql.js", "sqlite3"] }, "sha512-1OZY3PXD7BR00Gl61UUOFihslDldfH4NFRH2MbP54Yxi0G/PKn4HfO65JYZ7c16DeP3SpM3Aw+VXVG9j6CRSXA=="], + "drizzle-orm": ["drizzle-orm@0.36.4", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=3", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/react": ">=18", "@types/sql.js": "*", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "react": ">=18", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/react", "@types/sql.js", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "knex", "kysely", "mysql2", "pg", "postgres", "react", "sql.js", "sqlite3"] }, "sha512-1OZY3PXD7BR00Gl61UUOFihslDldfH4NFRH2MbP54Yxi0G/PKn4HfO65JYZ7c16DeP3SpM3Aw+VXVG9j6CRSXA=="], "electron-to-chromium": ["electron-to-chromium@1.5.361", "", {}, "sha512-Q6Hts7N9FnJc5LeGRINFvLhCI9xZmNtTDe5ZbcVezQz7cU4a8Aua3GH1b8J2XY8Al9PF+OCwYqhgsOOheMdvkA=="], diff --git a/docs/superpowers/plans/2026-05-22-fullstack-monorepo-scaffold.md b/docs/superpowers/plans/2026-05-22-fullstack-monorepo-scaffold.md new file mode 100644 index 0000000..d86df5f --- /dev/null +++ b/docs/superpowers/plans/2026-05-22-fullstack-monorepo-scaffold.md @@ -0,0 +1,1325 @@ +# Fullstack Monorepo Scaffold Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Build the Bun + Moon fullstack TypeScript scaffold for ZeaVis Edu beside the existing `Machine_Learning/` pipeline. + +**Architecture:** Add a root Bun workspace managed by Moon with three projects: `apps/web`, `apps/api`, and `packages/shared`. The frontend is a React/Vite app with landing and dashboard routes; the backend is an Elysia API with health/status endpoints and Drizzle/PostgreSQL configuration that does not require a live database for startup. + +**Tech Stack:** Bun, Moon, TypeScript, React, Vite, React Router, TanStack Query, Zustand, Tailwind CSS, shadcn/ui-style components, Elysia, Drizzle ORM, PostgreSQL. + +--- + +## File Structure + +Create these root files: + +- `package.json`: Bun workspaces, package manager marker, root scripts delegating to Moon. +- `bunfig.toml`: Bun install/runtime defaults. +- `tsconfig.base.json`: shared strict TypeScript compiler options. +- `.env.example`: app ports and `DATABASE_URL` documentation. +- `.gitignore`: ignore JS/TS dependency, build, env, and Moon cache outputs while preserving existing ML files. +- `.moon/workspace.yml`: Moon workspace configuration. +- `.moon/toolchain.yml`: Bun/Node toolchain configuration. + +Create `packages/shared`: + +- `packages/shared/package.json`: package metadata and export paths. +- `packages/shared/tsconfig.json`: TypeScript config extending root base. +- `packages/shared/src/index.ts`: shared exports. +- `packages/shared/src/app-status.ts`: shared app status response type and factory. + +Create `apps/api`: + +- `apps/api/package.json`: API dependencies and scripts. +- `apps/api/tsconfig.json`: API TypeScript config. +- `apps/api/drizzle.config.ts`: Drizzle Kit config using `DATABASE_URL`. +- `apps/api/src/index.ts`: Elysia server entry. +- `apps/api/src/config/env.ts`: env helpers for port and database URL. +- `apps/api/src/routes/health.ts`: `/health` route. +- `apps/api/src/routes/status.ts`: `/api/v1/status` route. +- `apps/api/src/db/schema.ts`: minimal schema placeholder. +- `apps/api/src/db/client.ts`: Drizzle client factory that is not used at server boot. + +Create `apps/web`: + +- `apps/web/package.json`: frontend dependencies and scripts. +- `apps/web/tsconfig.json`: app TypeScript config. +- `apps/web/tsconfig.node.json`: Vite config TypeScript config. +- `apps/web/vite.config.ts`: Vite React config. +- `apps/web/index.html`: Vite HTML entry. +- `apps/web/postcss.config.js`: PostCSS config for Tailwind. +- `apps/web/tailwind.config.ts`: Tailwind content/theme config. +- `apps/web/src/main.tsx`: React root render. +- `apps/web/src/app.tsx`: providers and router wiring. +- `apps/web/src/index.css`: Tailwind layers and theme variables. +- `apps/web/src/lib/utils.ts`: `cn` utility. +- `apps/web/src/store/ui-store.ts`: small Zustand UI store. +- `apps/web/src/components/ui/button.tsx`: shadcn-style button. +- `apps/web/src/components/ui/card.tsx`: shadcn-style card. +- `apps/web/src/pages/landing-page.tsx`: landing route. +- `apps/web/src/pages/dashboard-page.tsx`: placeholder dashboard route. + +Create Moon project files: + +- `apps/web/moon.yml`: web tasks. +- `apps/api/moon.yml`: api tasks. +- `packages/shared/moon.yml`: shared tasks. + +--- + +### Task 1: Root Workspace and Moon Configuration + +**Files:** +- Create: `package.json` +- Create: `bunfig.toml` +- Create: `tsconfig.base.json` +- Create: `.env.example` +- Create: `.gitignore` +- Create: `.moon/workspace.yml` +- Create: `.moon/toolchain.yml` + +- [ ] **Step 1: Create root package configuration** + +Create `package.json`: + +```json +{ + "name": "zeavis-edu", + "private": true, + "packageManager": "bun@1.1.42", + "workspaces": [ + "apps/*", + "packages/*" + ], + "scripts": { + "dev": "moon run :dev", + "build": "moon run :build", + "typecheck": "moon run :typecheck" + }, + "devDependencies": { + "@moonrepo/cli": "^1.32.4", + "typescript": "^5.6.3" + } +} +``` + +- [ ] **Step 2: Create Bun config** + +Create `bunfig.toml`: + +```toml +[install] +exact = true +``` + +- [ ] **Step 3: Create shared TypeScript base config** + +Create `tsconfig.base.json`: + +```json +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "baseUrl": ".", + "paths": { + "@zeavis/shared": ["packages/shared/src/index.ts"], + "@zeavis/shared/*": ["packages/shared/src/*"] + } + } +} +``` + +- [ ] **Step 4: Create environment example** + +Create `.env.example`: + +```dotenv +WEB_PORT=5173 +API_PORT=3000 +DATABASE_URL=postgres://postgres:postgres@localhost:5432/zeavis_edu +``` + +- [ ] **Step 5: Create root gitignore** + +Create `.gitignore`: + +```gitignore +node_modules/ +.bun/ +.moon/cache/ +.env +.env.* +!.env.example + +dist/ +build/ +coverage/ +*.tsbuildinfo + +.DS_Store +``` + +- [ ] **Step 6: Create Moon workspace config** + +Create `.moon/workspace.yml`: + +```yaml +projects: + web: apps/web + api: apps/api + shared: packages/shared +``` + +- [ ] **Step 7: Create Moon toolchain config** + +Create `.moon/toolchain.yml`: + +```yaml +node: + packageManager: bun +``` + +- [ ] **Step 8: Verify root config files are present** + +Run: `test -f package.json && test -f .moon/workspace.yml && test -f tsconfig.base.json` + +Expected: command exits with status 0 and no output. + +- [ ] **Step 9: Commit root workspace config** + +```bash +git add package.json bunfig.toml tsconfig.base.json .env.example .gitignore .moon/workspace.yml .moon/toolchain.yml +git commit -m "Add Bun and Moon workspace configuration" +``` + +--- + +### Task 2: Shared Package + +**Files:** +- Create: `packages/shared/package.json` +- Create: `packages/shared/tsconfig.json` +- Create: `packages/shared/src/index.ts` +- Create: `packages/shared/src/app-status.ts` +- Create: `packages/shared/moon.yml` + +- [ ] **Step 1: Create shared package metadata** + +Create `packages/shared/package.json`: + +```json +{ + "name": "@zeavis/shared", + "version": "0.1.0", + "private": true, + "type": "module", + "exports": { + ".": "./src/index.ts", + "./app-status": "./src/app-status.ts" + }, + "scripts": { + "typecheck": "tsc --project tsconfig.json" + }, + "devDependencies": { + "typescript": "^5.6.3" + } +} +``` + +- [ ] **Step 2: Create shared TypeScript config** + +Create `packages/shared/tsconfig.json`: + +```json +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "lib": ["ES2022"], + "types": [] + }, + "include": ["src"] +} +``` + +- [ ] **Step 3: Create shared app status type** + +Create `packages/shared/src/app-status.ts`: + +```ts +export type AppStatus = { + name: 'ZeaVis Edu'; + status: 'ok'; + version: string; +}; + +export function createAppStatus(version = '0.1.0'): AppStatus { + return { + name: 'ZeaVis Edu', + status: 'ok', + version, + }; +} +``` + +- [ ] **Step 4: Create shared package export** + +Create `packages/shared/src/index.ts`: + +```ts +export type { AppStatus } from './app-status'; +export { createAppStatus } from './app-status'; +``` + +- [ ] **Step 5: Create shared Moon tasks** + +Create `packages/shared/moon.yml`: + +```yaml +type: library +language: typescript + +tasks: + typecheck: + command: bun run typecheck + inputs: + - src/**/* + - tsconfig.json + - package.json +``` + +- [ ] **Step 6: Run shared package typecheck after dependencies are installed** + +Run after Task 5 Step 1 has installed dependencies: `bun --cwd packages/shared run typecheck` + +Expected: TypeScript exits with status 0. + +- [ ] **Step 7: Commit shared package** + +```bash +git add packages/shared +git commit -m "Add shared TypeScript package" +``` + +--- + +### Task 3: Elysia API Scaffold + +**Files:** +- Create: `apps/api/package.json` +- Create: `apps/api/tsconfig.json` +- Create: `apps/api/drizzle.config.ts` +- Create: `apps/api/src/index.ts` +- Create: `apps/api/src/config/env.ts` +- Create: `apps/api/src/routes/health.ts` +- Create: `apps/api/src/routes/status.ts` +- Create: `apps/api/src/db/schema.ts` +- Create: `apps/api/src/db/client.ts` +- Create: `apps/api/moon.yml` + +- [ ] **Step 1: Create API package metadata** + +Create `apps/api/package.json`: + +```json +{ + "name": "@zeavis/api", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "dev": "bun --watch src/index.ts", + "start": "bun src/index.ts", + "typecheck": "tsc --project tsconfig.json", + "db:generate": "drizzle-kit generate", + "db:migrate": "drizzle-kit migrate" + }, + "dependencies": { + "@zeavis/shared": "workspace:*", + "drizzle-orm": "^0.36.4", + "elysia": "^1.1.25", + "postgres": "^3.4.5" + }, + "devDependencies": { + "drizzle-kit": "^0.27.1", + "typescript": "^5.6.3" + } +} +``` + +- [ ] **Step 2: Create API TypeScript config** + +Create `apps/api/tsconfig.json`: + +```json +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "lib": ["ES2022"], + "types": ["bun"], + "noEmit": true + }, + "include": ["src", "drizzle.config.ts"] +} +``` + +- [ ] **Step 3: Create API environment helper** + +Create `apps/api/src/config/env.ts`: + +```ts +export const env = { + port: Number(Bun.env.API_PORT ?? 3000), + databaseUrl: Bun.env.DATABASE_URL, +}; +``` + +- [ ] **Step 4: Create health route** + +Create `apps/api/src/routes/health.ts`: + +```ts +import { Elysia } from 'elysia'; + +export const healthRoutes = new Elysia().get('/health', () => ({ + status: 'ok' as const, +})); +``` + +- [ ] **Step 5: Create status route using shared package** + +Create `apps/api/src/routes/status.ts`: + +```ts +import { createAppStatus } from '@zeavis/shared'; +import { Elysia } from 'elysia'; + +export const statusRoutes = new Elysia({ prefix: '/api/v1' }).get('/status', () => + createAppStatus(), +); +``` + +- [ ] **Step 6: Create Drizzle schema placeholder** + +Create `apps/api/src/db/schema.ts`: + +```ts +import { pgTable, timestamp, uuid, varchar } from 'drizzle-orm/pg-core'; + +export const appEvents = pgTable('app_events', { + id: uuid('id').primaryKey().defaultRandom(), + name: varchar('name', { length: 120 }).notNull(), + createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), +}); +``` + +- [ ] **Step 7: Create Drizzle client factory** + +Create `apps/api/src/db/client.ts`: + +```ts +import { drizzle } from 'drizzle-orm/postgres-js'; +import postgres from 'postgres'; +import { env } from '../config/env'; +import * as schema from './schema'; + +export function createDbClient() { + if (!env.databaseUrl) { + throw new Error('DATABASE_URL is required to create a database client'); + } + + const client = postgres(env.databaseUrl); + return drizzle(client, { schema }); +} +``` + +- [ ] **Step 8: Create Drizzle config** + +Create `apps/api/drizzle.config.ts`: + +```ts +import { defineConfig } from 'drizzle-kit'; + +export default defineConfig({ + schema: './src/db/schema.ts', + out: './drizzle', + dialect: 'postgresql', + dbCredentials: { + url: process.env.DATABASE_URL ?? 'postgres://postgres:postgres@localhost:5432/zeavis_edu', + }, +}); +``` + +- [ ] **Step 9: Create Elysia server entry** + +Create `apps/api/src/index.ts`: + +```ts +import { Elysia } from 'elysia'; +import { env } from './config/env'; +import { healthRoutes } from './routes/health'; +import { statusRoutes } from './routes/status'; + +const app = new Elysia().use(healthRoutes).use(statusRoutes).listen(env.port); + +console.log(`ZeaVis Edu API running at http://${app.server?.hostname}:${app.server?.port}`); + +export type App = typeof app; +``` + +- [ ] **Step 10: Create API Moon tasks** + +Create `apps/api/moon.yml`: + +```yaml +type: application +language: typescript + +tasks: + dev: + command: bun run dev + local: true + build: + command: bun build src/index.ts --outdir dist --target bun + inputs: + - src/**/* + - package.json + - tsconfig.json + - drizzle.config.ts + outputs: + - dist + typecheck: + command: bun run typecheck + inputs: + - src/**/* + - package.json + - tsconfig.json + - drizzle.config.ts +``` + +- [ ] **Step 11: Commit API scaffold** + +```bash +git add apps/api +git commit -m "Add Elysia API scaffold" +``` + +--- + +### Task 4: React Web Scaffold + +**Files:** +- Create: `apps/web/package.json` +- Create: `apps/web/tsconfig.json` +- Create: `apps/web/tsconfig.node.json` +- Create: `apps/web/vite.config.ts` +- Create: `apps/web/index.html` +- Create: `apps/web/postcss.config.js` +- Create: `apps/web/tailwind.config.ts` +- Create: `apps/web/src/main.tsx` +- Create: `apps/web/src/app.tsx` +- Create: `apps/web/src/index.css` +- Create: `apps/web/src/lib/utils.ts` +- Create: `apps/web/src/store/ui-store.ts` +- Create: `apps/web/src/components/ui/button.tsx` +- Create: `apps/web/src/components/ui/card.tsx` +- Create: `apps/web/src/pages/landing-page.tsx` +- Create: `apps/web/src/pages/dashboard-page.tsx` +- Create: `apps/web/moon.yml` + +- [ ] **Step 1: Create web package metadata** + +Create `apps/web/package.json`: + +```json +{ + "name": "@zeavis/web", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite --host 0.0.0.0", + "build": "tsc --project tsconfig.json && vite build", + "preview": "vite preview --host 0.0.0.0", + "typecheck": "tsc --project tsconfig.json" + }, + "dependencies": { + "@tanstack/react-query": "^5.59.16", + "@vitejs/plugin-react": "^4.3.3", + "@zeavis/shared": "workspace:*", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "lucide-react": "^0.468.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^6.28.0", + "tailwind-merge": "^2.5.4", + "zustand": "^5.0.1" + }, + "devDependencies": { + "autoprefixer": "^10.4.20", + "postcss": "^8.4.49", + "tailwindcss": "^3.4.15", + "typescript": "^5.6.3", + "vite": "^6.0.1" + } +} +``` + +- [ ] **Step 2: Create web TypeScript config** + +Create `apps/web/tsconfig.json`: + +```json +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "types": ["vite/client"], + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"], + "@zeavis/shared": ["../../packages/shared/src/index.ts"], + "@zeavis/shared/*": ["../../packages/shared/src/*"] + } + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} +``` + +- [ ] **Step 3: Create Vite TypeScript config** + +Create `apps/web/tsconfig.node.json`: + +```json +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "types": ["node"] + }, + "include": ["vite.config.ts", "tailwind.config.ts"] +} +``` + +- [ ] **Step 4: Create Vite config** + +Create `apps/web/vite.config.ts`: + +```ts +import react from '@vitejs/plugin-react'; +import path from 'node:path'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [react()], + resolve: { + alias: { + '@': path.resolve(__dirname, './src'), + }, + }, +}); +``` + +- [ ] **Step 5: Create Vite HTML entry** + +Create `apps/web/index.html`: + +```html + + +
+ + ++ Scaffold ini menyiapkan fondasi aplikasi ZeaVis Edu untuk antarmuka edukasi, + API, dan integrasi model machine learning berikutnya. +
++ Placeholder awal untuk fitur deteksi, riwayat analisis, dan edukasi. +
+