commit 637debc4debd7c67c1e7525a53ed4aec6fed85f1 Author: Platform Administrator Date: Sat Oct 10 21:08:20 2026 +0000 Initial commit diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..07bfc11 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,24 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[Makefile] +indent_style = tab + +[*.{go,mod}] +indent_style = tab + +[*.{py,cs,java,kt,kts,swift,rs,c,cpp,h}] +indent_size = 4 + +[*.{yml,yaml,json}] +indent_size = 2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..73a6827 --- /dev/null +++ b/.gitignore @@ -0,0 +1,146 @@ +# W4C project template — default ignores. +# The stack is chosen in the creation dialog, so this file covers the ecosystems the +# dialog offers (Node/TypeScript, Python, .NET, JVM, Go, Rust, Dart/Flutter, Ruby, +# Elixir, Swift) plus OS/editor cruft and secret-bearing files. + +# --- OS cruft --- +.DS_Store +.AppleDouble +.LSOverride +Thumbs.db +ehthumbs.db +Desktop.ini +$RECYCLE.BIN/ + +# --- Editors / IDEs --- +.idea/ +.vscode/ +!.vscode/extensions.json +!.vscode/settings.json +!.vscode/launch.json +!.vscode/tasks.json +*.swp +*.swo +*.sublime-workspace +*.sublime-project +*~ + +# --- Secrets and local environment (keep .env.example tracked) --- +.env +.env.* +!.env.example +*.pem +*.key +*.p12 +*.pfx +secrets/ +certs/ + +# --- Logs and temporary files --- +*.log +logs/ +log/ +tmp/ +temp/ +.cache/ +*.tmp + +# --- Build output (shared across stacks) --- +build/ +dist/ +out/ +coverage/ +artifacts/ +*.tsbuildinfo + +# --- Node / JavaScript / TypeScript --- +node_modules/ +.pnpm-store/ +.yarn/ +.npm/ +.pnp +.pnp.* +.next/ +.nuxt/ +.output/ +.svelte-kit/ +.astro/ +.turbo/ +.vercel/ +.parcel-cache/ +.eslintcache +.angular/ + +# --- Python --- +__pycache__/ +*.py[cod] +*.egg-info/ +.eggs/ +.venv/ +venv/ +.pytest_cache/ +.mypy_cache/ +.ruff_cache/ +.tox/ +htmlcov/ + +# --- .NET / C# --- +bin/ +obj/ +.vs/ +TestResults/ +*.user +*.suo +*.nupkg + +# --- Java / Kotlin / Gradle / Maven --- +target/ +.gradle/ +*.class +*.jar +!gradle/wrapper/gradle-wrapper.jar +!.mvn/wrapper/maven-wrapper.jar +local.properties +*.apk +*.aab + +# --- Go --- +*.exe +*.exe~ +*.dll +*.so +*.dylib +*.test +*.out + +# --- Rust --- +/target/ + +# --- Dart / Flutter --- +.dart_tool/ +.pub-cache/ +.flutter-plugins +.flutter-plugins-dependencies + +# --- Ruby / Rails --- +.bundle/ +vendor/bundle/ +.byebug_history + +# --- Elixir / Phoenix --- +_build/ +deps/ +*.ez +.elixir_ls/ +erl_crash.dump + +# --- Swift / Xcode --- +.build/ +DerivedData/ +.swiftpm/ +*.xcuserstate + +# --- Local databases --- +*.sqlite +*.sqlite3 +*.db diff --git a/.w4c/README.md b/.w4c/README.md new file mode 100644 index 0000000..c61be65 --- /dev/null +++ b/.w4c/README.md @@ -0,0 +1,116 @@ +# `.w4c/` — project folder contract + +Everything W4C needs to treat a repository as a **project** lives here. The +folder is committed with the repository, so project metadata and seed assets +travel with the code. + +``` +.w4c/ +├── project.json # project metadata (source of truth in-repo) +├── template.json # present only in template repositories +├── preview.svg # catalog thumbnail for templates +├── diagrams/ +│ └── architecture.excalidraw.json # Excalidraw scenes +├── boards/ +│ └── roadmap.json # board columns + seed tasks +└── workflows/ + └── ci-build.yaml # workflow definitions (same YAML shape as the workflows module) +``` + +## `project.json` + +Marks the repository as a project and carries its metadata. A repository +without this file (or without a server-side project record) is just a +repository — selecting it optionally creates a project from it. + +```jsonc +{ + "schema": 1, + "name": "Online Shop", + "description": "Starter e-commerce storefront.", + "agentId": "w4c-startup", // default agent for project chats + "tags": ["web", "e-commerce"], + "source": { "template": "wiz4apps/templates.OnlineShop" } +} +``` + +## `template.json` (templates only) + +Catalog descriptor for the template picker on the start page: the dialog +fields the user fills in, the prompt assembled at start, and the list of +elements to import. + +```jsonc +{ + "schema": 1, + "id": "online-shop", + "title": "Online Shop", + "description": "…", + "thumbnail": ".w4c/preview.svg", + "agentId": "w4c-startup", + "defaultName": "online-shop", // default repository name in the dialog + "skills": ["project-bootstrap"], // marketplace skills installed + attached on create + "fields": [ + { "key": "storeName", "label": "Store name", "type": "text", "required": true } + ], + "promptTemplate": "…", + "elements": [ + { "kind": "diagram", "name": "Architecture", "path": ".w4c/diagrams/architecture.excalidraw.json" } + ] +} +``` + +`fields[].type` is one of `text | textarea | number | select | boolean`. +`select` fields also declare `options`; other fields may declare `default` +and `placeholder`. `promptTemplate` supports `{{key}}` interpolation plus the +service tokens `{{title}}`, `{{repoFullName}}` and `{{elements}}`. + +If `elements` is omitted, the importer may discover assets by folder +convention (`diagrams/`, `boards/`, `workflows/`). + +## Elements + +| Kind | Folder | Format | +|------|--------|--------| +| `diagram` | `diagrams/` | Excalidraw scene JSON (`{"type":"excalidraw", ...}`) | +| `board` | `boards/` | `{ name, columns[], tasks[] }` | +| `workflow` | `workflows/` | Workflow YAML (see `w4c-workflows-api` `WorkflowDefinition`) | +| `widget` | `widgets/` | Widget definition JSON (reserved) | + +## `boards/roadmap.json` — the build stages + +The board seed is the canonical **layer → task** list for the project. The +Startup Creator (`w4c-startup`) reads it on the first turn and materialises one +board card (repository issue) per entry in the project's own repository, then +hands the user over to My Boards. A card is only "done" when its acceptance +criterion has been shown at runtime. + +```jsonc +{ + "schema": 1, + "name": "Project roadmap", + "columns": [ // the board's real grouping + { "id": "high", "name": "High Priority" }, + { "id": "low", "name": "Low Priority" }, + { "id": "none", "name": "No Priority" }, + { "id": "done", "name": "Done" } + ], + "tasks": [ + { + "icon": "🗺️", // unicode icon, prefixed to the card title + "title": "Architecture diagram — architecture, ERD and key flows", + "column": "high", // target column id + "labels": ["High Priority"], + "body": "…goal, deliverables, acceptance criteria, references…" + } + ] +} +``` + +Keep the layer set complete: plan, design mockups, architecture diagram, +database & forms, backend/API, frontend code, admin, control panel, workflows, +deploy and quality. Agent provisioning is deliberately **not** a bootstrap +stage — creating a specialist agent is a follow-up the user starts on purpose. + +Element import from a repository is not active yet; the descriptors above are +the stable contract for when it is. diff --git a/.w4c/boards/roadmap.json b/.w4c/boards/roadmap.json new file mode 100644 index 0000000..d22ee67 --- /dev/null +++ b/.w4c/boards/roadmap.json @@ -0,0 +1,120 @@ +{ + "schema": 1, + "name": "Project roadmap", + "description": "Seed board for a project created from this template (internal admin/back-office app). Every entry is one layer of the build; the Startup Creator materialises them as board cards (repository issues) on the first turn, then hands the user over to My Boards. `column` uses the W4C board's real grouping (priority + done).", + "columns": [ + { + "id": "high", + "name": "High Priority" + }, + { + "id": "low", + "name": "Low Priority" + }, + { + "id": "none", + "name": "No Priority" + }, + { + "id": "done", + "name": "Done" + } + ], + "tasks": [ + { + "icon": "🧭", + "title": "Plan & scope — routes, data model, acceptance criteria", + "column": "high", + "labels": [ + "High Priority" + ], + "body": "Turn `SPEC.md` into `docs/PLAN.md`: the route/screen map, the data model and the acceptance criteria.\n\nDeliverables: `docs/PLAN.md` committed, every route and entity named, decisions locked.\nAcceptance: the plan names a proof command or a runtime check for every later layer.\nRef: `SPEC.md`, `project-bootstrap` skill." + }, + { + "icon": "🎨", + "title": "Design (mockups) — design system and screens on the canvas", + "column": "high", + "labels": [ + "High Priority" + ], + "body": "Design the UI/UX on the OpenPencil canvas (`/design-studio`, agent `w4c-design`): design tokens plus labelled artboards for the shell and every route, with loading/empty/error variants.\n\nDeliverables (evidence): canvas document name + node ids (image export as backup); tokens committed under `docs/design/`.\nAcceptance: the canvas document covers every route in `docs/PLAN.md`; AA contrast checked.\nRef: `SPEC.md`, `stack-*-quality` skill." + }, + { + "icon": "🗺️", + "title": "Architecture diagram — architecture, ERD and key flows", + "column": "high", + "labels": [ + "High Priority" + ], + "body": "On My Diagrams (`/diagrams`, agent `w4c-diagram`) draw the architecture, the ERD and the key flows, derived from this card's task description and `docs/PLAN.md`.\n\nDeliverables: a saved architecture diagram and an ERD of the data model, plus a sequence diagram for the main user flow. Add only the elements that belong to the task to the diagram context.\nAcceptance: the diagrams reload without loss and match the plan; the ERD lists the same entities as `SPEC.md`.\nRef: `docs/PLAN.md`, `/diagrams`." + }, + { + "icon": "🗄️", + "title": "Database & forms — tables, relations, migrations, seed", + "column": "high", + "labels": [ + "High Priority" + ], + "body": "Implement the data layer: schema, relations, migrations and seed data (agent `w4c-db` for the W4C Data Tables + forms on `/db-forms`, `w4c-code` for in-repo migrations/ORM).\n\nDeliverables: the tables/enums from `SPEC.md` with correct nullability and indexes; an idempotent seed; the data-entry forms created and published.\nAcceptance: migrate + seed run clean on a fresh clone; a public form token accepts a submission without login; aggregate queries return hand-checked totals.\nRef: `SPEC.md`, `/db-forms`." + }, + { + "icon": "⚙️", + "title": "Backend & API — services, endpoints, auth", + "column": "low", + "labels": [ + "Low Priority" + ], + "body": "Implement the services and endpoints the screens call, plus authentication and authorisation (agent `w4c-code`).\n\nDeliverables: the API routes behind every screen action; server-side authorisation on every mutation; validation and error envelopes.\nAcceptance: endpoints answer with the documented shapes; unauthorised and cross-user requests are rejected server-side (401/403); `.env.example` lists every required variable.\nRef: `docs/PLAN.md`, `SPEC.md`." + }, + { + "icon": "💻", + "title": "Frontend (code) — generate the screens", + "column": "low", + "labels": [ + "Low Priority" + ], + "body": "Generate the frontend: the screens from `SPEC.md` wired to the API, following the design system from the canvas (agent `w4c-code`).\n\nDeliverables: every route implemented with loading, empty and error states.\nAcceptance: build + lint + typecheck pass; each route renders with seeded data.\nRef: `docs/PLAN.md`, `SPEC.md`." + }, + { + "icon": "🛡️", + "title": "Admin panel — guarded back-office", + "column": "low", + "labels": [ + "Low Priority" + ], + "body": "Build the guarded admin/back-office (agent `w4c-code`): dashboard, CRUD over the core entities, lists with filters and audited state transitions.\n\nDeliverables: `/admin/*` routes; destructive actions behind a confirmation dialog; role checks server-side.\nAcceptance: a non-admin request to every admin route/mutation is rejected server-side.\nRef: `SPEC.md`." + }, + { + "icon": "📊", + "title": "Control Panel — dashboard pages and widget data binding", + "column": "low", + "labels": [ + "Low Priority" + ], + "body": "Build the operations dashboard on the Control Panel (`/control-panel`, agent `w4c-panel`): KPI tiles, charts, tables and the board, with widgets bound to real data.\n\nDeliverables: at least one page per audience, widgets bound through data binding with an explicit empty state.\nAcceptance: widgets show real data or an empty state (never an endless spinner); no horizontal scroll at 390×844; the page can be shared publicly and revoked.\nRef: `/control-panel`, `WIDGETS.md`." + }, + { + "icon": "🔀", + "title": "Workflows — automation and CI", + "column": "low", + "labels": [ + "Low Priority" + ], + "body": "Author the automation flows (agent `w4c-workflow`) and keep the CI workflow shipped with the template.\n\nDeliverables: workflow YAML under `.w4c/workflows/` (and/or in the Workflows module), run history visible.\nAcceptance: a test trigger actually performs its action (task/email/log); a failing node is reported as failed, not a silent success.\nRef: `/workflows`, `.w4c/workflows/`." + }, + { + "icon": "🚀", + "title": "Deploy — containers, compose, public URL", + "column": "none", + "labels": [], + "body": "Deploy the product (agent `w4c-server`): connect a server, create docker-compose for the app and its API, build and run the containers, expose the public URL.\n\nDeliverables: compose file committed; running containers; public URL.\nAcceptance: the public URL serves the app; a container restart restores the service; container logs are readable from the Server Console.\nRef: `/servers`, `/servers/console`." + }, + { + "icon": "✅", + "title": "Quality & delivery — tests, docs, accessibility, definition of done", + "column": "none", + "labels": [], + "body": "Close the build (agents `w4c-code` / `w4c-workflow`): unit tests, end-to-end tests, README quickstart, `.env.example`, accessibility pass.\n\nDeliverables: unit tests for the domain rules; E2E for the happy path and the guard; CI runs install + lint + typecheck + test + build.\nAcceptance: the CI command is green locally; the README quickstart works on a clean clone; `.env.example` lists every secret; keyboard/contrast checked.\nRef: `SPEC.md`, `stack-*-quality` skill." + } + ] +} diff --git a/.w4c/diagrams/architecture.excalidraw.json b/.w4c/diagrams/architecture.excalidraw.json new file mode 100644 index 0000000..f2d8de0 --- /dev/null +++ b/.w4c/diagrams/architecture.excalidraw.json @@ -0,0 +1,499 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://w4c.dev/templates", + "elements": [ + { + "id": "text_30", + "type": "text", + "x": 60, + "y": 24, + "width": 439.04, + "height": 36.4, + "angle": 0, + "strokeColor": "#0f172a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "roundness": null, + "seed": 771897110, + "version": 1, + "versionNonce": 1664454508, + "isDeleted": false, + "groupIds": [], + "frameId": null, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "index": "a0", + "metadata": {}, + "text": "Admin dashboard architecture", + "originalText": "Admin dashboard architecture", + "fontSize": 28, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "lineHeight": 1.25, + "autoResize": true + }, + { + "id": "rectangle_31", + "type": "rectangle", + "x": 40, + "y": 130, + "width": 180, + "height": 80, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#a5d8ff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "roundness": { + "type": 3 + }, + "seed": 1332191912, + "version": 1, + "versionNonce": 407252351, + "isDeleted": false, + "groupIds": [], + "frameId": null, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "index": "a1", + "metadata": {} + }, + { + "id": "text_32", + "type": "text", + "x": 94.16, + "y": 160.0, + "width": 71.68, + "height": 20.8, + "angle": 0, + "strokeColor": "#0f172a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "roundness": null, + "seed": 1695016344, + "version": 1, + "versionNonce": 1429804126, + "isDeleted": false, + "groupIds": [], + "frameId": null, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "index": "a2", + "metadata": {}, + "text": "Admin UI", + "originalText": "Admin UI", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "lineHeight": 1.25, + "autoResize": true + }, + { + "id": "rectangle_33", + "type": "rectangle", + "x": 290, + "y": 60, + "width": 180, + "height": 70, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#b2f2bb", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "roundness": { + "type": 3 + }, + "seed": 772215614, + "version": 1, + "versionNonce": 1569605774, + "isDeleted": false, + "groupIds": [], + "frameId": null, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "index": "a3", + "metadata": {} + }, + { + "id": "text_34", + "type": "text", + "x": 360.0, + "y": 85.0, + "width": 40, + "height": 20.8, + "angle": 0, + "strokeColor": "#0f172a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "roundness": null, + "seed": 420523392, + "version": 1, + "versionNonce": 1449304901, + "isDeleted": false, + "groupIds": [], + "frameId": null, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "index": "a4", + "metadata": {}, + "text": "API", + "originalText": "API", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "lineHeight": 1.25, + "autoResize": true + }, + { + "id": "rectangle_35", + "type": "rectangle", + "x": 290, + "y": 200, + "width": 180, + "height": 70, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffec99", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "roundness": { + "type": 3 + }, + "seed": 2109455742, + "version": 1, + "versionNonce": 1535330911, + "isDeleted": false, + "groupIds": [], + "frameId": null, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "index": "a5", + "metadata": {} + }, + { + "id": "text_36", + "type": "text", + "x": 330.72, + "y": 225.0, + "width": 98.56, + "height": 20.8, + "angle": 0, + "strokeColor": "#0f172a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "roundness": null, + "seed": 691645852, + "version": 1, + "versionNonce": 182247227, + "isDeleted": false, + "groupIds": [], + "frameId": null, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "index": "a6", + "metadata": {}, + "text": "Auth / RBAC", + "originalText": "Auth / RBAC", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "lineHeight": 1.25, + "autoResize": true + }, + { + "id": "rectangle_37", + "type": "rectangle", + "x": 550, + "y": 130, + "width": 170, + "height": 80, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#d0bfff", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "roundness": { + "type": 3 + }, + "seed": 714896912, + "version": 1, + "versionNonce": 1420598982, + "isDeleted": false, + "groupIds": [], + "frameId": null, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "index": "a7", + "metadata": {} + }, + { + "id": "text_38", + "type": "text", + "x": 599.16, + "y": 160.0, + "width": 71.68, + "height": 20.8, + "angle": 0, + "strokeColor": "#0f172a", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "roundness": null, + "seed": 149785661, + "version": 1, + "versionNonce": 2140064855, + "isDeleted": false, + "groupIds": [], + "frameId": null, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "index": "a8", + "metadata": {}, + "text": "Database", + "originalText": "Database", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "lineHeight": 1.25, + "autoResize": true + }, + { + "id": "arrow_39", + "type": "arrow", + "x": 220, + "y": 170.0, + "width": 70, + "height": 75.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "roundness": { + "type": 2 + }, + "seed": 1249982161, + "version": 1, + "versionNonce": 569683851, + "isDeleted": false, + "groupIds": [], + "frameId": null, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "index": "a9", + "metadata": {}, + "points": [ + [ + 0, + 0 + ], + [ + 70, + -75.0 + ] + ], + "endArrowhead": "arrow" + }, + { + "id": "arrow_40", + "type": "arrow", + "x": 220, + "y": 170.0, + "width": 70, + "height": 65.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "roundness": { + "type": 2 + }, + "seed": 352741327, + "version": 1, + "versionNonce": 128045008, + "isDeleted": false, + "groupIds": [], + "frameId": null, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "index": "a10", + "metadata": {}, + "points": [ + [ + 0, + 0 + ], + [ + 70, + 65.0 + ] + ], + "endArrowhead": "arrow" + }, + { + "id": "arrow_41", + "type": "arrow", + "x": 470, + "y": 95.0, + "width": 80, + "height": 75.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "roundness": { + "type": 2 + }, + "seed": 1618992934, + "version": 1, + "versionNonce": 1478230372, + "isDeleted": false, + "groupIds": [], + "frameId": null, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "index": "a11", + "metadata": {}, + "points": [ + [ + 0, + 0 + ], + [ + 80, + 75.0 + ] + ], + "endArrowhead": "arrow" + }, + { + "id": "arrow_42", + "type": "arrow", + "x": 470, + "y": 235.0, + "width": 80, + "height": 65.0, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "roundness": { + "type": 2 + }, + "seed": 1182614515, + "version": 1, + "versionNonce": 1383340429, + "isDeleted": false, + "groupIds": [], + "frameId": null, + "boundElements": null, + "updated": 1, + "link": null, + "locked": false, + "index": "a12", + "metadata": {}, + "points": [ + [ + 0, + 0 + ], + [ + 80, + -65.0 + ] + ], + "endArrowhead": "arrow" + } + ], + "appState": { + "viewBackgroundColor": "#ffffff", + "gridSize": null, + "scrollX": 0, + "scrollY": 0, + "zoom": { + "value": 1 + } + }, + "files": {} +} diff --git a/.w4c/preview.svg b/.w4c/preview.svg new file mode 100644 index 0000000..cb0f2ab --- /dev/null +++ b/.w4c/preview.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/.w4c/project.json b/.w4c/project.json new file mode 100644 index 0000000..d1c30ea --- /dev/null +++ b/.w4c/project.json @@ -0,0 +1,14 @@ +{ + "schema": 1, + "name": "Admin Dashboard / CRM", + "description": "An internal back-office app: data tables, filters, record forms, roles and an audit trail.", + "agentId": "w4c-startup", + "tags": [ + "internal", + "admin", + "crm" + ], + "source": { + "template": "wiz4apps/templates.AdminDashboard" + } +} diff --git a/.w4c/template.json b/.w4c/template.json new file mode 100644 index 0000000..3f39db2 --- /dev/null +++ b/.w4c/template.json @@ -0,0 +1,115 @@ +{ + "schema": 1, + "id": "admin-dashboard", + "title": "Admin Dashboard / CRM", + "description": "An internal back-office app: data tables, filters, record forms, roles and an audit trail.", + "tags": [ + "internal", + "admin", + "crm" + ], + "thumbnail": ".w4c/preview.svg", + "agentId": "w4c-startup", + "defaultName": "admin-dashboard", + "skills": [ + "project-bootstrap" + ], + "fields": [ + { + "key": "appName", + "label": "App name", + "type": "text", + "required": true, + "placeholder": "Acme CRM" + }, + { + "key": "stack", + "label": "Tech stack", + "type": "select", + "options": [ + "Vue 3 + Quasar", + "Nuxt 3", + "Next.js", + "React + Vite", + "Angular", + "SvelteKit", + "SolidStart", + "Remix", + "Astro", + "Laravel + Vue", + "Django + HTMX", + "Rails + Hotwire", + "Phoenix (Elixir)", + "Spring Boot + React", + "ASP.NET Core + Blazor", + "Go + Templ", + "Rust + Axum", + "Flutter", + "React Native (Expo)", + "SwiftUI", + "Jetpack Compose (Kotlin)" + ], + "default": "React + Vite", + "required": true + }, + { + "key": "database", + "label": "Database", + "type": "select", + "options": [ + "PostgreSQL", + "MySQL", + "SQLite", + "MongoDB" + ], + "default": "PostgreSQL", + "required": true + }, + { + "key": "auth", + "label": "Authentication", + "type": "select", + "options": [ + "Local accounts", + "SSO / OIDC", + "None (internal)" + ], + "default": "SSO / OIDC" + }, + { + "key": "entities", + "label": "Main entities", + "type": "textarea", + "default": "customers, deals, activities, tasks" + }, + { + "key": "features", + "label": "Must-have features", + "type": "textarea", + "default": "data tables with filters, record forms, roles, CSV export, audit log" + }, + { + "key": "notes", + "label": "Additional notes", + "type": "textarea" + } + ], + "promptTemplate": "You are starting a new project from the '{{title}}' template.\n\nRepository: {{repoFullName}}\nApp: {{appName}}\nTech stack: {{stack}}\nDatabase: {{database}}\nAuth: {{auth}}\nMain entities: {{entities}}\nMust-have features: {{features}}\nAdditional notes: {{notes}}\n\nBefore writing any code, read README.md, SPEC.md and the .w4c/ folder — they define the scope, routes/screens, data model, flows and acceptance criteria. Apply the project-bootstrap best practices, then work top-down in layers: scaffold → auth & roles → entity metadata + generic table → record form → audit trail → dashboard → quality. Keep docs/PLAN.md, the board and .w4c/project.json in sync as you go. Bundled assets: {{elements}}\n\nFIRST TURN — lay the project out on its board before writing any code. Read `.w4c/boards/roadmap.json` and create one board card per layer in THIS repository (projectId = the repository full name above), prefixing every title with its unicode icon: 🧭 plan & scope, 🎨 design (mockups), 🗺️ architecture diagram, 🗄️ database & forms, ⚙️ backend & API, 💻 frontend (code), 🛡️ admin panel, 📊 control panel, 🔀 workflows, 🚀 deploy, ✅ quality & delivery. Each card body carries the goal, the deliverables, the acceptance criteria (a command or a runtime check) and the references; set the priority labels. Do not create or configure new agents — that is a follow-up, not part of bootstrap. Then report the card list and invite the user to continue on My Boards (/boards); do not start a layer before the user has reviewed the plan.", + "elements": [ + { + "kind": "diagram", + "name": "Architecture", + "path": ".w4c/diagrams/architecture.excalidraw.json" + }, + { + "kind": "board", + "name": "Project roadmap", + "path": ".w4c/boards/roadmap.json" + }, + { + "kind": "workflow", + "name": "CI Build", + "path": ".w4c/workflows/ci-build.yaml" + } + ] +} diff --git a/.w4c/workflows/ci-build.sh b/.w4c/workflows/ci-build.sh new file mode 100644 index 0000000..61d2aca --- /dev/null +++ b/.w4c/workflows/ci-build.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -euo pipefail +npm ci +echo '→ lint' +npm run lint +echo '→ test' +npm test -- --run +echo '→ build' +npm run build diff --git a/.w4c/workflows/ci-build.yaml b/.w4c/workflows/ci-build.yaml new file mode 100644 index 0000000..2a56bf3 --- /dev/null +++ b/.w4c/workflows/ci-build.yaml @@ -0,0 +1,14 @@ +name: Admin Dashboard / CRM CI +description: >- + Install dependencies, lint, test and build the app. + Runs on demand or on a repository event; starts the project's continuous + integration contract. +version: 1.0.0 +mode: function +language: shell +target: default +trigger: + type: event +tasks: + - id: build + entry: { file: ci-build.sh } diff --git a/README.md b/README.md new file mode 100644 index 0000000..756f211 --- /dev/null +++ b/README.md @@ -0,0 +1,64 @@ +# Admin Dashboard / CRM — W4C project template + +A W4C **project template**. Creating a project from it generates a private copy of this +repository in your namespace and starts the **Startup Creator** agent, which drives the build +from this brief. + +This repository is deliberately a **brief, not a finished app**: it ships the product +specification, the project metadata and the seed assets; the tech stack chosen in the creation +dialog decides the scaffold. Read [`SPEC.md`](SPEC.md) for the full product contract — roles, +routes, data model, flows, functional and non-functional requirements, and the acceptance +criteria the work is checked against. + +## What ships in this repository + +| Path | Purpose | +|------|---------| +| `README.md` | This file: how the template is wired and how to work in the project. | +| `SPEC.md` | The product specification and acceptance criteria — the contract to build. | +| `.gitignore` | Default ignores for dependencies, build output, local secrets and editor/OS cruft. | +| `.editorconfig` | Shared editor defaults (UTF-8, LF, indentation) for the generated project. | +| `.w4c/template.json` | Template manifest: dialog fields, starter prompt, element list (templates only). | +| `.w4c/project.json` | Project metadata committed with the repository. | +| `.w4c/diagrams/` | Excalidraw architecture diagram. | +| `.w4c/boards/roadmap.json` | Board columns and the seed tasks for the build. | +| `.w4c/workflows/` | The CI workflow (`ci-build.yaml` + `ci-build.sh`) to import into the workflows module. | + +See [`.w4c/README.md`](.w4c/README.md) for the full `.w4c/` contract. + +## Stack + +The creation dialog records the chosen stack (and its skill pair `stack-*` + +`stack-*-quality`), the database and the auth provider. Scaffold with those skills and hold the +work to their conventions and quality gates. The stack is not fixed here on purpose: the same +brief is built on React + Vite, Next.js, Vue 3 + Quasar, Laravel + Vue, Phoenix, … without +changing `SPEC.md`. + +## Build order + +Work **top-down**, one layer at a time, and verify each layer before starting the next: + +1. **Scaffold** the stack and get the empty shell running (install → dev server → commit). +2. **Auth & roles** — the role→permission model, server-side enforcement and the seeded admin. +3. **Entity metadata + generic table** from `SPEC.md` §3–§4 (registry, migrations, seeded rows). +4. **Generic record form** — typed inputs, validation, relations, bulk actions. +5. **Audit trail** — atomic writes for every mutation, change history, `/audit`. +6. **Dashboard + global search** — KPIs, recent activity, cross-entity search. +7. **Import / export** — streaming CSV export, mapped import with dry-run. +8. **Quality** — tests, CI, README quickstart, `.env.example`, accessibility. + +The full rationale and the acceptance criteria live in `SPEC.md` §8–§9. + +## Working rules + +- One concern per commit, conventional messages (`feat:`, `fix:`, `chore:`, `docs:`, `test:`). +- The CRUD surface is **generated from entity metadata** — add a field in the registry, never a + new per-entity screen. +- Authorize every entity+action on the server; never only in the UI; keep every list query + paginated and bounded. +- Write the audit record **atomically** with the mutation it describes; never update or delete an + audit event. +- Runtime data is real typed records — never floating-point for money/quantities and never a + schemaless blob where a `FieldDefinition` should drive validation. +- Never commit secrets — document them in `.env.example` instead. +- Keep `docs/PLAN.md`, the board and `.w4c/project.json` in sync as the project evolves. diff --git a/SPEC.md b/SPEC.md new file mode 100644 index 0000000..07bcf10 --- /dev/null +++ b/SPEC.md @@ -0,0 +1,202 @@ +# Admin Dashboard / CRM — product specification + +This is the contract the project is built against. It is written for the agent that scaffolds +the repository, but it doubles as the human-readable brief: every requirement below is meant to +be implementable and verifiable. + +Values in angle brackets (``, ``, ``) come from the +project-creation dialog and are recorded in the project record; replace them as you read. + +## 1. Goal + +An internal back-office / CRM that an operations team could actually run on day one: sign in with +the configured ``, see a dashboard of KPIs, manage the supplied `` through +generated data tables and record forms, search across everything, import and export CSV, and trust +an append-only audit trail for every change. The CRUD surface is **generic and driven by entity +metadata** — one registry defines the entities and their typed fields, and the list views, forms, +filters and related-record links are generated from it, so adding a field is a metadata change, +not new hand-written screens for every entity. + +It must be **runnable end-to-end on day one** (with seeded demo data and a seeded admin), not a +set of screens wired together later. + +Non-goals (do not build unless the must-have list says otherwise): a public/customer-facing portal, +marketing pages, billing/subscriptions, real-time collaboration, workflow automation/approvals, +ML scoring, mobile apps. + +## 2. Roles + +| Role | Can do | +|------|--------| +| Viewer | Sign in, view the dashboard, browse and search any entity they can read, open records, run exports. Read-only. | +| Editor | Everything a viewer can, plus create, update, soft-delete and restore records in the entities they are granted. | +| Admin | Everything an editor can, plus manage entity metadata and field definitions, manage roles/permissions, delete permanently, and read the full audit trail. | + +RBAC is **per entity and per action**: a role maps to `{ entity, actions[] }` where actions are +`read | create | update | delete | restore | export | import`. A user may hold different rights on +different entities (e.g. edit `customers`, read-only `deals`). Authentication is ``; +provide a **seeded admin** account for local development so a reviewer can log in immediately. + +## 3. Information architecture (routes) + +Auth: +- `/login` — sign in (and the SSO callback route when `` is `SSO / OIDC`). +- `/logout`. + +App (guarded; the entity routes are generated from the registry): +- `/` — dashboard: KPI cards, recent activity, quick links to each entity. +- `/search` — global search across the readable entities. +- `/:entity` — generic list view for one entity: server-side pagination, sorting, filters. +- `/:entity/new` — generic record form (create). +- `/:entity/:id` — record detail: field values, related-record links, change history. +- `/:entity/:id/edit` — generic record form (edit); destructive actions live here behind + confirmation. +- `/import` — CSV import: upload, column mapping, dry-run preview, commit, per-row errors. +- `/audit` — audit trail: filter by actor, entity, action and date; immutable entries. +- `/settings/entities` — entity & field metadata registry (admin only). +- `/settings/roles` — roles and per-entity permission matrix (admin only). +- `/settings/saved-views` — saved list filters (personal and shared). + +Access to `/:entity` routes is denied (403) when the signed-in role has no `read` on that entity; +the nav only shows entities the role can read. + +## 4. Data model + +Minimum viable entities (add fields the requirements imply; keep them typed and validated): + +- **EntityDefinition** — id, key (route segment), label, labelPlural, icon, isSystem, + displayField, sortField, defaultSortDirection, createdAt. +- **FieldDefinition** — id, entityId, key, label, type (`text` | `textarea` | `number` | `date` | + `boolean` | `select` | `relation`), isRequired, isUnique, options[], relationEntityId?, + isListColumn, listOrder, isFilterable, isSortable, defaultValue, validationRules (min/max/ + pattern/maxLength). The pair EntityDefinition + FieldDefinition is the typed registry that + drives the generated tables and forms. +- **Domain entities** — the user-supplied `` (e.g. `customers`, `deals`, `activities`, + `tasks`). Each is a real, typed table/collection whose columns follow its FieldDefinitions; it + carries the shared audit columns below. Model them with migrations, not a schemaless blob, and + seed a few rows each so lists are presentable on first run. +- **Shared record columns** — id, createdAt, createdBy, updatedAt, updatedBy, deletedAt? + (soft delete), deletedBy?. `deletedAt IS NULL` means active. +- **Role** — id, key (`viewer` | `editor` | `admin` or custom), name, isSystem. +- **Permission** — id, roleId, entityId (or `*`), actions[] (`read`/`create`/`update`/`delete`/ + `restore`/`export`/`import`). +- **User** — id, email, name, passwordHash?, externalId? (for SSO), roleIds[], status, createdAt. +- **AuditEvent** — id, actorId, actorEmail, entityId, recordId, action (`create` | `update` | + `delete` | `restore` | `import`), changes (before/after diff of changed fields), + occurredAt. Append-only; never updated or deleted. +- **SavedView** — id, ownerId or shared flag, entityId, name, filters, sort, visibleColumns[]. +- **ImportJob** — id, entityId, filename, status (`pending` | `validating` | `ready` | + `committed` | `failed`), columnMapping, rowsTotal, rowsValid, rowsFailed, errors[], + createdBy, createdAt. Export jobs may reuse it or stream directly without persisting a row. + +Keep every field typed and validated; a record value that does not satisfy its FieldDefinition is +a 422, not a stored string. + +## 5. Key flows + +1. **Sign in → dashboard.** A reviewer logs in with the seeded admin, lands on `/`, and sees KPI + cards (record counts per entity, recent creates/updates, and at least one invented business KPI + per seeded entity) plus recent audit activity. +2. **Browse a list.** The user opens `/:entity`, sees a server-paginated table of the visible + columns, sorts by a sortable column, applies a filter and saves it as a **saved view**; reloading + or sharing the filtered URL reproduces the same view. +3. **Create / edit a record.** The user opens the generated record form, fills typed fields, gets + inline validation errors on submit, saves, and is taken to the record — which now appears in the + list and has a new `create`/`update` entry in its change history. +4. **Related records.** From a record with a `relation` field the user follows a link to the related + record; the reverse record lists its referrers. +5. **Soft delete → restore.** An editor soft-deletes a record: it disappears from the default list, + the deletion is audited, and the record is recoverable from the "deleted" filter or the record + view. An admin can restore it; permanent delete is admin-only, confirmed, and audited. +6. **Audit.** An admin opens `/audit`, filters to one record, and sees who changed what and when — + including before/after values for each changed field — for every create, update, delete and + restore. +7. **Import / export.** A user exports the current filtered list to CSV (streamed) and imports a + CSV of new records: mapping columns to fields, seeing a dry-run with per-row errors, then + committing only the valid rows — each committed row producing an audit event. +8. **Admin metadata.** An admin adds a field in `/settings/entities`, and it appears as a list + column and form input for that entity without writing per-entity screen code. + +## 6. Functional requirements + +- **Generic CRUD from metadata:** list views, filters and forms are generated from + EntityDefinition/FieldDefinition. No per-entity hand-written tables or forms. +- **List views:** server-side pagination (bounded page size), sorting, and filters derived from the + filterable fields; empty state when a filter matches nothing; total count shown. +- **Filters:** text contains/equals, number/date ranges, select/boolean equals, and a relation picker; + all applied server-side, never by loading the whole table. +- **Record forms:** one generated form per entity with typed inputs, required/unique/format + validation, and inline, field-level errors returned by the server. +- **Related records:** `relation` fields render as links to the related record; the related record + shows a back-reference list. +- **Bulk actions:** select rows and apply a bulk action (bulk soft-delete, bulk export, bulk field + update where enabled) — each row audited, with a confirmation step. +- **Dashboard:** KPI cards and recent activity; numbers come from real (seeded) data, not placeholders. +- **Global search:** one query box that searches across the readable entities and returns grouped, + paged results, respecting RBAC. +- **CSV import/export:** export streams the current filtered/selected set; import supports column + mapping, a dry-run preview, per-row errors and a commit of valid rows only. +- **Audit trail:** every create/update/delete/restore writes an immutable AuditEvent with actor, + timestamp and the changed fields; the record view and `/audit` expose them. +- **Soft delete + restore:** deletes set `deletedAt`; restore clears it; both are authorized and + audited. Permanent delete is admin-only and confirmed. +- **Saved views:** persist filters/sort/columns per user and optionally shared. +- **Admin:** manage entity/field metadata and the role→permission matrix; no destructive action + without confirmation. +- **Seed data:** a seeded admin (viewer + editor + admin usable immediately), the supplied entities + with a few rows each, and a saved view or two, so the dashboard is presentable on first run. + +## 7. Non-functional requirements + +- **Authorization:** enforce every entity+action permission **server-side per request** — never only + in the UI; a direct API call with insufficient rights must fail, not merely hide a button. +- **Bounded queries:** every list query is paginated and bounded with a server-enforced maximum page + size; no unbounded scans of a table, ever (list, export and search included). +- **Audit atomicity:** the audit write is committed **atomically with the mutation it records** — in + the same transaction. A change without an audit event, or an audit event without the change, must + not be observable. +- **Output escaping:** escape every value rendered in the UI (field values, names, error text) so a + stored string can never execute as markup. +- **Streaming exports:** large CSV exports stream to the client; they must not buffer the whole + result set in memory. Imports validate in bounded batches. +- **Destructive actions:** require explicit confirmation and are recoverable wherever soft delete + applies; permanent delete is admin-only. +- **Resilience:** every screen has explicit loading, empty and error states; validation errors are + shown per field, not as one generic message. +- **Observability:** structured logs for imports, exports, permission denials and auth failures; a + health endpoint. + +## 8. Acceptance criteria (definition of done) + +- [ ] Install, dev server, lint, typecheck, tests and production build all pass. +- [ ] A seeded admin can log in immediately and sees a dashboard with real (seeded) KPIs. +- [ ] Every supplied `` has a working list view and record form generating from metadata — + CRUD works end-to-end with typed fields and server-returned validation errors. +- [ ] RBAC is enforced server-side per entity and action: viewer is read-only, editor can mutate its + entities, admin manages metadata/roles, and a denied direct API call returns 403. +- [ ] Filters, sorting and pagination are applied server-side (`LIMIT`/`OFFSET`, not client slicing) + and every list query is bounded. +- [ ] Every create, update, delete and restore writes an immutable audit record with actor, + timestamp and changed fields, committed atomically with the mutation. +- [ ] Soft delete hides a record and restore brings it back; both are audited; permanent delete is + admin-only and confirmed. +- [ ] Global search and CSV export respect RBAC; export streams and import supports mapping + + dry-run with per-row errors. +- [ ] `.env.example` documents every secret; README quickstart (install, run, test, env) is accurate. +- [ ] CI runs install + lint + typecheck + tests + build. + +## 9. Suggested build order + +Follow this order and finish (and verify) a layer before starting the next: + +1. **Scaffold** the chosen stack (with ``), install dependencies, get the dev server and + the empty shell running, and commit the skeleton. +2. **Auth & roles** — sign in with ``, the role→permission model, server-side + enforcement, and the seeded admin. +3. **Entity metadata + generic table** — the EntityDefinition/FieldDefinition registry, migrations + for the supplied `` and their seed rows, and the generated, server-paginated list view. +4. **Generic record form** — typed inputs, validation, relation links, bulk actions. +5. **Audit trail** — atomic audit writes for every mutation, the record change history and `/audit`. +6. **Dashboard + global search** — KPI cards, recent activity, cross-entity search. +7. **Import / export** — streaming CSV export, import with mapping, dry-run and per-row errors. +8. **Quality** — tests for the flows above, CI, README, `.env.example`, accessibility pass.