generated from wiz4apps/templates.AdminDashboard
Initial commit
This commit is contained in:
commit
637debc4de
24
.editorconfig
Normal file
24
.editorconfig
Normal 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
146
.gitignore
vendored
Normal 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
116
.w4c/README.md
Normal 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
120
.w4c/boards/roadmap.json
Normal 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."
|
||||
}
|
||||
]
|
||||
}
|
||||
499
.w4c/diagrams/architecture.excalidraw.json
Normal file
499
.w4c/diagrams/architecture.excalidraw.json
Normal 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
16
.w4c/preview.svg
Normal 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
14
.w4c/project.json
Normal 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
115
.w4c/template.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
9
.w4c/workflows/ci-build.sh
Normal file
9
.w4c/workflows/ci-build.sh
Normal 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
|
||||
14
.w4c/workflows/ci-build.yaml
Normal file
14
.w4c/workflows/ci-build.yaml
Normal 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
64
README.md
Normal 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
202
SPEC.md
Normal 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.
|
||||
Loading…
Reference in a new issue