Initial commit

This commit is contained in:
Platform Administrator 2026-10-10 21:08:20 +00:00
commit 637debc4de
12 changed files with 1339 additions and 0 deletions

24
.editorconfig Normal file
View file

@ -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

146
.gitignore vendored Normal file
View file

@ -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

116
.w4c/README.md Normal file
View file

@ -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.

120
.w4c/boards/roadmap.json Normal file
View file

@ -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."
}
]
}

View file

@ -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": {}
}

16
.w4c/preview.svg Normal file
View file

@ -0,0 +1,16 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 360" role="img" aria-label="Admin Dashboard / CRM template preview">
<defs>
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#0f172a"/>
<stop offset="1" stop-color="#1e293b"/>
</linearGradient>
<linearGradient id="a" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#4ade80"/>
<stop offset="1" stop-color="#0ea5e9"/>
</linearGradient>
</defs>
<rect width="640" height="360" rx="18" fill="url(#bg)"/>
<rect x="48" y="48" width="544" height="44" rx="10" stroke="#334155" stroke-width="2" fill="none"/>
<rect x="66" y="64" width="110" height="12" rx="6" fill="#4ade80"/>
<rect x="48" y="112" width="120" height="174" rx="12" stroke="#334155" stroke-width="2" fill="none"/><rect x="188" y="112" width="404" height="80" rx="12" fill="url(#a)" opacity="0.9"/><rect x="188" y="206" width="196" height="80" rx="12" stroke="#334155" stroke-width="2" fill="none"/><rect x="396" y="206" width="196" height="80" rx="12" stroke="#334155" stroke-width="2" fill="none"/><rect x="64" y="130" width="90" height="12" rx="6" fill="#94a3b8"/><rect x="64" y="150" width="70" height="12" rx="6" fill="#94a3b8"/><rect x="64" y="170" width="84" height="12" rx="6" fill="#94a3b8"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

14
.w4c/project.json Normal file
View file

@ -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"
}
}

115
.w4c/template.json Normal file
View file

@ -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"
}
]
}

View file

@ -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

View file

@ -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 }

64
README.md Normal file
View file

@ -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.

202
SPEC.md Normal file
View file

@ -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 (`<App name>`, `<entities>`, `<auth provider>`) 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 `<auth provider>`, see a dashboard of KPIs, manage the supplied `<entities>` 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 `<auth provider>`;
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 `<auth provider>` 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 `<entities>` (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 `<entity>` 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 `<database>`), install dependencies, get the dev server and
the empty shell running, and commit the skeleton.
2. **Auth & roles** — sign in with `<auth provider>`, the role→permission model, server-side
enforcement, and the seeded admin.
3. **Entity metadata + generic table** — the EntityDefinition/FieldDefinition registry, migrations
for the supplied `<entities>` 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.