@mars-stack/cli 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/template/.cursor/rules/composition-patterns.mdc +186 -0
- package/template/.cursor/rules/data-access.mdc +29 -0
- package/template/.cursor/rules/project-structure.mdc +34 -0
- package/template/.cursor/rules/security.mdc +25 -0
- package/template/.cursor/rules/testing.mdc +24 -0
- package/template/.cursor/rules/ui-conventions.mdc +29 -0
- package/template/.cursor/skills/add-api-route/SKILL.md +122 -0
- package/template/.cursor/skills/add-audit-log/SKILL.md +373 -0
- package/template/.cursor/skills/add-blog/SKILL.md +447 -0
- package/template/.cursor/skills/add-command-palette/SKILL.md +438 -0
- package/template/.cursor/skills/add-component/SKILL.md +158 -0
- package/template/.cursor/skills/add-crud-routes/SKILL.md +221 -0
- package/template/.cursor/skills/add-e2e-test/SKILL.md +227 -0
- package/template/.cursor/skills/add-error-boundary/SKILL.md +472 -0
- package/template/.cursor/skills/add-feature/SKILL.md +174 -0
- package/template/.cursor/skills/add-middleware/SKILL.md +135 -0
- package/template/.cursor/skills/add-page/SKILL.md +151 -0
- package/template/.cursor/skills/add-prisma-model/SKILL.md +148 -0
- package/template/.cursor/skills/add-protected-resource/SKILL.md +192 -0
- package/template/.cursor/skills/add-role/SKILL.md +156 -0
- package/template/.cursor/skills/add-server-action/SKILL.md +167 -0
- package/template/.cursor/skills/add-webhook/SKILL.md +192 -0
- package/template/.cursor/skills/build-complete-feature/SKILL.md +227 -0
- package/template/.cursor/skills/build-dashboard/SKILL.md +211 -0
- package/template/.cursor/skills/build-data-table/SKILL.md +283 -0
- package/template/.cursor/skills/build-form/SKILL.md +231 -0
- package/template/.cursor/skills/build-landing-page/SKILL.md +248 -0
- package/template/.cursor/skills/configure-ai/SKILL.md +617 -0
- package/template/.cursor/skills/configure-analytics/SKILL.md +413 -0
- package/template/.cursor/skills/configure-dark-mode/SKILL.md +309 -0
- package/template/.cursor/skills/configure-email/SKILL.md +170 -0
- package/template/.cursor/skills/configure-email-verification/SKILL.md +333 -0
- package/template/.cursor/skills/configure-feature-flags/SKILL.md +361 -0
- package/template/.cursor/skills/configure-i18n/SKILL.md +518 -0
- package/template/.cursor/skills/configure-jobs/SKILL.md +500 -0
- package/template/.cursor/skills/configure-magic-links/SKILL.md +385 -0
- package/template/.cursor/skills/configure-multi-tenancy/SKILL.md +611 -0
- package/template/.cursor/skills/configure-notifications/SKILL.md +569 -0
- package/template/.cursor/skills/configure-oauth/SKILL.md +217 -0
- package/template/.cursor/skills/configure-onboarding/SKILL.md +483 -0
- package/template/.cursor/skills/configure-payments/SKILL.md +243 -0
- package/template/.cursor/skills/configure-realtime/SKILL.md +733 -0
- package/template/.cursor/skills/configure-search/SKILL.md +581 -0
- package/template/.cursor/skills/configure-storage/SKILL.md +273 -0
- package/template/.cursor/skills/configure-two-factor/SKILL.md +518 -0
- package/template/.cursor/skills/create-execution-plan/SKILL.md +204 -0
- package/template/.cursor/skills/create-seed/SKILL.md +191 -0
- package/template/.cursor/skills/deploy-to-vercel/SKILL.md +300 -0
- package/template/.cursor/skills/design-tokens/SKILL.md +138 -0
- package/template/.cursor/skills/mars-capture-conversation-context/SKILL.md +119 -0
- package/template/.cursor/skills/setup-billing/SKILL.md +322 -0
- package/template/.cursor/skills/setup-project/SKILL.md +104 -0
- package/template/.cursor/skills/setup-teams/SKILL.md +682 -0
- package/template/.cursor/skills/test-api-route/SKILL.md +219 -0
- package/template/.cursor/skills/update-architecture-docs/SKILL.md +99 -0
- package/template/AGENTS.md +104 -0
- package/template/ARCHITECTURE.md +102 -0
- package/template/docs/QUALITY_SCORE.md +20 -0
- package/template/docs/design-docs/conversation-as-system-record.md +70 -0
- package/template/docs/design-docs/core-beliefs.md +43 -0
- package/template/docs/design-docs/index.md +8 -0
- package/template/docs/exec-plans/active/.gitkeep +0 -0
- package/template/docs/exec-plans/completed/.gitkeep +0 -0
- package/template/docs/exec-plans/tech-debt.md +7 -0
- package/template/docs/generated/.gitkeep +0 -0
- package/template/docs/product-specs/index.md +7 -0
- package/template/docs/references/index.md +18 -0
- package/template/e2e/api.spec.ts +20 -0
- package/template/e2e/auth.spec.ts +24 -0
- package/template/e2e/public.spec.ts +25 -0
- package/template/eslint.config.mjs +24 -0
- package/template/next-env.d.ts +6 -0
- package/template/next.config.ts +45 -0
- package/template/package.json +80 -0
- package/template/playwright.config.ts +31 -0
- package/template/postcss.config.mjs +8 -0
- package/template/prisma/generated/prisma/browser.ts +49 -0
- package/template/prisma/generated/prisma/client.ts +73 -0
- package/template/prisma/generated/prisma/commonInputTypes.ts +406 -0
- package/template/prisma/generated/prisma/enums.ts +15 -0
- package/template/prisma/generated/prisma/internal/class.ts +254 -0
- package/template/prisma/generated/prisma/internal/prismaNamespace.ts +1240 -0
- package/template/prisma/generated/prisma/internal/prismaNamespaceBrowser.ts +190 -0
- package/template/prisma/generated/prisma/models/Account.ts +1543 -0
- package/template/prisma/generated/prisma/models/File.ts +1529 -0
- package/template/prisma/generated/prisma/models/Session.ts +1415 -0
- package/template/prisma/generated/prisma/models/Subscription.ts +1455 -0
- package/template/prisma/generated/prisma/models/User.ts +2235 -0
- package/template/prisma/generated/prisma/models/VerificationToken.ts +1099 -0
- package/template/prisma/generated/prisma/models.ts +17 -0
- package/template/prisma/schema/auth.prisma +69 -0
- package/template/prisma/schema/base.prisma +8 -0
- package/template/prisma/schema/file.prisma +15 -0
- package/template/prisma/schema/subscription.prisma +17 -0
- package/template/prisma.config.ts +13 -0
- package/template/scripts/check-architecture.ts +221 -0
- package/template/scripts/check-doc-freshness.ts +242 -0
- package/template/scripts/ensure-db.mjs +291 -0
- package/template/scripts/generate-docs.ts +143 -0
- package/template/scripts/generate-env-example.ts +89 -0
- package/template/scripts/seed.ts +56 -0
- package/template/scripts/update-quality-score.ts +263 -0
- package/template/src/__tests__/architecture.test.ts +114 -0
- package/template/src/app/(auth)/forgotten-password/page.tsx +92 -0
- package/template/src/app/(auth)/layout.tsx +11 -0
- package/template/src/app/(auth)/register/page.tsx +162 -0
- package/template/src/app/(auth)/reset-password/page.tsx +109 -0
- package/template/src/app/(auth)/sign-in/page.tsx +122 -0
- package/template/src/app/(auth)/verify/[token]/page.tsx +87 -0
- package/template/src/app/(auth)/verify/page.tsx +56 -0
- package/template/src/app/(protected)/admin/page.tsx +108 -0
- package/template/src/app/(protected)/dashboard/loading.tsx +20 -0
- package/template/src/app/(protected)/dashboard/page.tsx +22 -0
- package/template/src/app/(protected)/layout.tsx +262 -0
- package/template/src/app/(protected)/settings/page.tsx +370 -0
- package/template/src/app/api/auth/forgot/route.ts +63 -0
- package/template/src/app/api/auth/login/route.ts +121 -0
- package/template/src/app/api/auth/logout/route.ts +19 -0
- package/template/src/app/api/auth/me/route.ts +30 -0
- package/template/src/app/api/auth/reset/route.ts +45 -0
- package/template/src/app/api/auth/signup/route.ts +85 -0
- package/template/src/app/api/auth/verify/route.ts +46 -0
- package/template/src/app/api/csrf/route.ts +12 -0
- package/template/src/app/api/health/route.ts +10 -0
- package/template/src/app/api/protected/admin/users/route.ts +24 -0
- package/template/src/app/api/protected/billing/checkout/route.ts +83 -0
- package/template/src/app/api/protected/billing/portal/route.ts +39 -0
- package/template/src/app/api/protected/files/[fileId]/route.ts +86 -0
- package/template/src/app/api/protected/files/upload/route.ts +64 -0
- package/template/src/app/api/protected/user/password/route.ts +63 -0
- package/template/src/app/api/protected/user/profile/route.ts +35 -0
- package/template/src/app/api/protected/user/sessions/[sessionId]/route.ts +33 -0
- package/template/src/app/api/protected/user/sessions/route.ts +22 -0
- package/template/src/app/api/readiness/route.ts +15 -0
- package/template/src/app/api/webhooks/stripe/route.ts +166 -0
- package/template/src/app/error.tsx +33 -0
- package/template/src/app/layout.tsx +29 -0
- package/template/src/app/not-found.tsx +20 -0
- package/template/src/app/page.tsx +136 -0
- package/template/src/app/privacy/page.tsx +178 -0
- package/template/src/app/providers.tsx +8 -0
- package/template/src/app/terms/page.tsx +139 -0
- package/template/src/config/app.config.ts +70 -0
- package/template/src/config/routes.ts +17 -0
- package/template/src/features/admin/index.ts +11 -0
- package/template/src/features/admin/permissions.ts +64 -0
- package/template/src/features/auth/context/AuthContext.tsx +96 -0
- package/template/src/features/auth/context/index.ts +2 -0
- package/template/src/features/auth/index.ts +3 -0
- package/template/src/features/auth/server/consent.ts +66 -0
- package/template/src/features/auth/server/session-revocation.ts +20 -0
- package/template/src/features/auth/server/sessions.ts +66 -0
- package/template/src/features/auth/server/user.ts +166 -0
- package/template/src/features/auth/types.ts +19 -0
- package/template/src/features/auth/validators.ts +29 -0
- package/template/src/features/billing/server/index.ts +66 -0
- package/template/src/features/billing/types.ts +43 -0
- package/template/src/features/uploads/server/index.ts +49 -0
- package/template/src/features/uploads/types.ts +26 -0
- package/template/src/lib/core/email/templates/base-layout.ts +122 -0
- package/template/src/lib/core/email/templates/index.ts +4 -0
- package/template/src/lib/core/email/templates/password-reset-email.ts +42 -0
- package/template/src/lib/core/email/templates/verification-email.ts +41 -0
- package/template/src/lib/core/email/templates/welcome-email.ts +40 -0
- package/template/src/lib/mars.ts +56 -0
- package/template/src/lib/prisma.ts +19 -0
- package/template/src/proxy.ts +92 -0
- package/template/src/styles/brand.css +17 -0
- package/template/src/styles/globals.css +6 -0
- package/template/tsconfig.json +59 -0
- package/template/vitest.config.ts +41 -0
- package/template/vitest.setup.ts +24 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# Skill: Build Complete Feature (Meta-Skill)
|
|
2
|
+
|
|
3
|
+
Orchestrate the end-to-end implementation of a new feature from database schema through UI to tests and documentation. This meta-skill chains multiple sub-skills in the correct order.
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
|
|
7
|
+
Use this skill when the user asks to:
|
|
8
|
+
- Build a complete feature end-to-end
|
|
9
|
+
- Add a new domain with full CRUD (e.g., "add blog posts", "add projects", "add invoices")
|
|
10
|
+
- Implement a feature from scratch with all layers (database, API, UI, tests)
|
|
11
|
+
|
|
12
|
+
This is the right skill when the request implies multiple layers of work, not just a single API route or page.
|
|
13
|
+
|
|
14
|
+
## Prerequisites
|
|
15
|
+
|
|
16
|
+
- Read `AGENTS.md` to understand the project structure.
|
|
17
|
+
- Read `src/config/app.config.ts` to check existing feature flags.
|
|
18
|
+
- Read `prisma/schema/` to see existing models and relations.
|
|
19
|
+
|
|
20
|
+
## Execution Order
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
┌─────────────────────────────────────────────────────┐
|
|
24
|
+
│ Phase 0: Plan │
|
|
25
|
+
│ create-execution-plan │
|
|
26
|
+
├─────────────────────────────────────────────────────┤
|
|
27
|
+
│ Phase 1: Data Layer │
|
|
28
|
+
│ add-prisma-model → db:push │
|
|
29
|
+
├─────────────────────────────────────────────────────┤
|
|
30
|
+
│ Phase 2: Feature Module │
|
|
31
|
+
│ add-feature (directory, types, validation, server) │
|
|
32
|
+
├─────────────────────────────────────────────────────┤
|
|
33
|
+
│ Phase 3: API Layer │
|
|
34
|
+
│ add-api-route (collection) → add-crud-routes │
|
|
35
|
+
│ ↳ If webhooks needed: add-webhook │
|
|
36
|
+
├─────────────────────────────────────────────────────┤
|
|
37
|
+
│ Phase 4: UI Layer │
|
|
38
|
+
│ add-page → add-component (list, form, detail) │
|
|
39
|
+
│ ↳ If data table needed: build-data-table │
|
|
40
|
+
│ ↳ If form needed: build-form │
|
|
41
|
+
├─────────────────────────────────────────────────────┤
|
|
42
|
+
│ Phase 5: Testing │
|
|
43
|
+
│ test-api-route → add-e2e-test │
|
|
44
|
+
├─────────────────────────────────────────────────────┤
|
|
45
|
+
│ Phase 6: Documentation │
|
|
46
|
+
│ update-architecture-docs │
|
|
47
|
+
└─────────────────────────────────────────────────────┘
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Phase 0: Create Execution Plan
|
|
51
|
+
|
|
52
|
+
**Skill:** `create-execution-plan`
|
|
53
|
+
|
|
54
|
+
Before writing any code, create an execution plan:
|
|
55
|
+
|
|
56
|
+
1. Read existing code to understand the current state
|
|
57
|
+
2. Create `docs/exec-plans/active/<feature-name>.md`
|
|
58
|
+
3. List all tasks with phase prefixes
|
|
59
|
+
4. Define verification criteria
|
|
60
|
+
|
|
61
|
+
## Phase 1: Data Layer
|
|
62
|
+
|
|
63
|
+
**Skill:** `add-prisma-model`
|
|
64
|
+
|
|
65
|
+
1. Design the data model based on requirements
|
|
66
|
+
2. Create `prisma/schema/<feature>.prisma` with the model(s)
|
|
67
|
+
3. Add relations to existing models (e.g., User) in their respective schema files
|
|
68
|
+
4. Run `yarn db:push` to sync
|
|
69
|
+
|
|
70
|
+
**Outputs passed to Phase 2:**
|
|
71
|
+
- Model name(s) and field names
|
|
72
|
+
- Relation fields added to User or other models
|
|
73
|
+
- Index definitions
|
|
74
|
+
|
|
75
|
+
### Decision: Does this feature need multiple models?
|
|
76
|
+
|
|
77
|
+
- **Single entity** (e.g., blog posts) → One model
|
|
78
|
+
- **Entity with metadata** (e.g., posts with tags) → Two+ models with relations
|
|
79
|
+
- **Entity with history** (e.g., orders with line items) → Parent-child models
|
|
80
|
+
|
|
81
|
+
## Phase 2: Feature Module
|
|
82
|
+
|
|
83
|
+
**Skill:** `add-feature`
|
|
84
|
+
|
|
85
|
+
1. Create the feature directory structure:
|
|
86
|
+
```
|
|
87
|
+
src/features/<name>/
|
|
88
|
+
├── components/
|
|
89
|
+
├── server/
|
|
90
|
+
├── hooks/ (optional)
|
|
91
|
+
├── validation/
|
|
92
|
+
└── types.ts
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
2. Create types based on the Prisma model
|
|
96
|
+
3. Create Zod validation schemas for create/update inputs
|
|
97
|
+
4. Create server module with queries and mutations (import `'server-only'`)
|
|
98
|
+
|
|
99
|
+
**Outputs passed to Phase 3:**
|
|
100
|
+
- Validation schema exports
|
|
101
|
+
- Server function exports (findAll, findById, create, update, delete)
|
|
102
|
+
- Type exports
|
|
103
|
+
|
|
104
|
+
## Phase 3: API Layer
|
|
105
|
+
|
|
106
|
+
**Skill:** `add-api-route` + `add-crud-routes`
|
|
107
|
+
|
|
108
|
+
1. Create collection route: `src/app/api/protected/<name>/route.ts` (GET list, POST create)
|
|
109
|
+
2. Create item route: `src/app/api/protected/<name>/[id]/route.ts` (GET one, PUT update, DELETE)
|
|
110
|
+
|
|
111
|
+
**Conditional branches:**
|
|
112
|
+
|
|
113
|
+
- **If the feature needs admin-only routes:**
|
|
114
|
+
Use `withRole(['admin'], ...)` for admin routes under `src/app/api/protected/admin/<name>/`
|
|
115
|
+
|
|
116
|
+
- **If the feature receives external callbacks:**
|
|
117
|
+
**Skill:** `add-webhook` — Create webhook handler at `src/app/api/webhooks/<service>/`
|
|
118
|
+
|
|
119
|
+
- **If the feature needs ownership-gated access:**
|
|
120
|
+
Use `withOwnership` in the item route to verify the user owns the resource
|
|
121
|
+
|
|
122
|
+
**Outputs passed to Phase 4:**
|
|
123
|
+
- API endpoint paths for client-side data fetching
|
|
124
|
+
- Response shapes for TypeScript types
|
|
125
|
+
|
|
126
|
+
## Phase 4: UI Layer
|
|
127
|
+
|
|
128
|
+
**Skill:** `add-page` + `add-component`
|
|
129
|
+
|
|
130
|
+
1. Create the main page: `src/app/(protected)/<name>/page.tsx`
|
|
131
|
+
2. Create `loading.tsx` for Suspense fallback
|
|
132
|
+
3. Create feature components in `src/features/<name>/components/`:
|
|
133
|
+
|
|
134
|
+
**Conditional branches:**
|
|
135
|
+
|
|
136
|
+
- **If the feature has a list view:**
|
|
137
|
+
**Skill:** `build-data-table` — Create a data table component with sorting, filtering, pagination
|
|
138
|
+
|
|
139
|
+
- **If the feature has a create/edit form:**
|
|
140
|
+
**Skill:** `build-form` — Create a form component with validation, error handling, loading states
|
|
141
|
+
|
|
142
|
+
- **If the feature has a detail view:**
|
|
143
|
+
Create a detail component that fetches a single item by ID
|
|
144
|
+
|
|
145
|
+
4. Add route to `src/config/routes.ts`
|
|
146
|
+
5. Add navigation link (if applicable)
|
|
147
|
+
|
|
148
|
+
### Component breakdown
|
|
149
|
+
|
|
150
|
+
| Component | Location | Purpose |
|
|
151
|
+
|-----------|----------|---------|
|
|
152
|
+
| `<Feature>List` | `src/features/<name>/components/` | List view with data table |
|
|
153
|
+
| `<Feature>Form` | `src/features/<name>/components/` | Create/edit form |
|
|
154
|
+
| `<Feature>Detail` | `src/features/<name>/components/` | Single item detail view |
|
|
155
|
+
|
|
156
|
+
## Phase 5: Testing
|
|
157
|
+
|
|
158
|
+
**Skill:** `test-api-route` + `add-e2e-test`
|
|
159
|
+
|
|
160
|
+
1. Create unit tests for each API route (`route.test.ts` next to `route.ts`)
|
|
161
|
+
2. Create unit tests for server module functions
|
|
162
|
+
3. Create E2E tests for critical user flows:
|
|
163
|
+
- Can create a new item
|
|
164
|
+
- Can view the list
|
|
165
|
+
- Can edit an item
|
|
166
|
+
- Can delete an item
|
|
167
|
+
- Unauthorised access is rejected
|
|
168
|
+
|
|
169
|
+
## Phase 6: Documentation
|
|
170
|
+
|
|
171
|
+
**Skill:** `update-architecture-docs`
|
|
172
|
+
|
|
173
|
+
1. Update `docs/QUALITY_SCORE.md` with the new feature grade
|
|
174
|
+
2. Update `AGENTS.md` if the feature introduces new patterns
|
|
175
|
+
3. Mark the execution plan as complete
|
|
176
|
+
4. Move the plan from `active/` to `completed/`
|
|
177
|
+
|
|
178
|
+
## Feature Flag Integration
|
|
179
|
+
|
|
180
|
+
If the feature should be toggleable:
|
|
181
|
+
|
|
182
|
+
1. Add flag to `appConfig.features` in `src/config/app.config.ts`
|
|
183
|
+
2. Gate the API routes:
|
|
184
|
+
```typescript
|
|
185
|
+
if (!appConfig.features.<name>) {
|
|
186
|
+
return NextResponse.json({ error: 'Feature not available' }, { status: 404 });
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
3. Gate the navigation link
|
|
190
|
+
4. Gate the page (redirect if disabled)
|
|
191
|
+
|
|
192
|
+
## Complete Example: Adding a "Projects" Feature
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
Phase 0: Create docs/exec-plans/active/add-projects.md
|
|
196
|
+
Phase 1: Create prisma/schema/projects.prisma (Project model)
|
|
197
|
+
Add projects relation to User in auth.prisma
|
|
198
|
+
Run yarn db:push
|
|
199
|
+
Phase 2: Create src/features/projects/ directory structure
|
|
200
|
+
Create types.ts, validation/schemas.ts, server/index.ts
|
|
201
|
+
Phase 3: Create src/app/api/protected/projects/route.ts (GET, POST)
|
|
202
|
+
Create src/app/api/protected/projects/[id]/route.ts (GET, PUT, DELETE)
|
|
203
|
+
Phase 4: Create src/app/(protected)/projects/page.tsx
|
|
204
|
+
Create ProjectList, ProjectForm components
|
|
205
|
+
Add to routes.ts
|
|
206
|
+
Phase 5: Write route.test.ts for both API routes
|
|
207
|
+
Write projects.spec.ts E2E test
|
|
208
|
+
Phase 6: Update QUALITY_SCORE.md
|
|
209
|
+
Complete execution plan
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Checklist
|
|
213
|
+
|
|
214
|
+
- [ ] Execution plan created before starting
|
|
215
|
+
- [ ] Prisma model(s) created and `db:push` run
|
|
216
|
+
- [ ] Feature directory with correct structure
|
|
217
|
+
- [ ] Server module with `'server-only'` import
|
|
218
|
+
- [ ] Validation schemas for all inputs
|
|
219
|
+
- [ ] CRUD API routes with auth wrappers
|
|
220
|
+
- [ ] Page with loading state
|
|
221
|
+
- [ ] List, form, and detail components
|
|
222
|
+
- [ ] Route added to `routes.ts`
|
|
223
|
+
- [ ] Feature flag in `app.config.ts` (if toggleable)
|
|
224
|
+
- [ ] Unit tests for API routes
|
|
225
|
+
- [ ] E2E tests for critical flows
|
|
226
|
+
- [ ] QUALITY_SCORE.md updated
|
|
227
|
+
- [ ] Execution plan marked complete
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# Skill: Build a Dashboard
|
|
2
|
+
|
|
3
|
+
Create a dashboard page with stat cards, charts, recent activity, and quick actions using MARS design system components.
|
|
4
|
+
|
|
5
|
+
## When to Use
|
|
6
|
+
|
|
7
|
+
Use this skill when the user asks to build a dashboard, overview page, home screen, or analytics view.
|
|
8
|
+
|
|
9
|
+
## Layout Architecture
|
|
10
|
+
|
|
11
|
+
MARS dashboards use a responsive grid with Cards as the primary container:
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<div className="mx-auto max-w-7xl space-y-6 p-6">
|
|
15
|
+
{/* Header */}
|
|
16
|
+
<div>
|
|
17
|
+
<H1>Dashboard</H1>
|
|
18
|
+
<Paragraph className="text-text-secondary">Overview of your account.</Paragraph>
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
{/* Stat Cards Row */}
|
|
22
|
+
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
|
23
|
+
{/* ...stat cards */}
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
{/* Main Content */}
|
|
27
|
+
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
|
|
28
|
+
<div className="lg:col-span-2">{/* Primary content */}</div>
|
|
29
|
+
<div>{/* Sidebar content */}</div>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Template: Stat Card
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import { Card, CardBody } from '@mars-stack/ui';
|
|
38
|
+
|
|
39
|
+
interface StatCardProps {
|
|
40
|
+
title: string;
|
|
41
|
+
value: string | number;
|
|
42
|
+
change?: { value: number; label: string };
|
|
43
|
+
icon?: React.ReactNode;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function StatCard({ title, value, change, icon }: StatCardProps) {
|
|
47
|
+
return (
|
|
48
|
+
<Card>
|
|
49
|
+
<CardBody className="flex items-start justify-between">
|
|
50
|
+
<div>
|
|
51
|
+
<p className="text-sm font-medium text-text-secondary">{title}</p>
|
|
52
|
+
<p className="mt-1 text-2xl font-bold text-text-primary">{value}</p>
|
|
53
|
+
{change && (
|
|
54
|
+
<p className={`mt-1 text-sm ${change.value >= 0 ? 'text-text-success' : 'text-text-error'}`}>
|
|
55
|
+
{change.value >= 0 ? '↑' : '↓'} {Math.abs(change.value)}% {change.label}
|
|
56
|
+
</p>
|
|
57
|
+
)}
|
|
58
|
+
</div>
|
|
59
|
+
{icon && (
|
|
60
|
+
<div className="rounded-lg bg-brand-primary-muted p-2 text-brand-primary">
|
|
61
|
+
{icon}
|
|
62
|
+
</div>
|
|
63
|
+
)}
|
|
64
|
+
</CardBody>
|
|
65
|
+
</Card>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Template: Full Dashboard Page
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
import { Card, CardHeader, CardBody, Badge, Spinner, H1, Paragraph } from '@mars-stack/ui';
|
|
74
|
+
import type { Metadata } from 'next';
|
|
75
|
+
|
|
76
|
+
export const metadata: Metadata = { title: 'Dashboard' };
|
|
77
|
+
|
|
78
|
+
export default function DashboardPage() {
|
|
79
|
+
return (
|
|
80
|
+
<div className="mx-auto max-w-7xl space-y-6 p-6">
|
|
81
|
+
<div>
|
|
82
|
+
<H1>Dashboard</H1>
|
|
83
|
+
<Paragraph className="text-text-secondary">
|
|
84
|
+
Welcome back. Here's what's happening.
|
|
85
|
+
</Paragraph>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
{/* Stats Row */}
|
|
89
|
+
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
|
90
|
+
<StatCard title="Total Users" value="1,234" change={{ value: 12, label: 'vs last month' }} />
|
|
91
|
+
<StatCard title="Revenue" value="£9,450" change={{ value: 8, label: 'vs last month' }} />
|
|
92
|
+
<StatCard title="Active Projects" value="23" />
|
|
93
|
+
<StatCard title="Completion Rate" value="94%" change={{ value: -2, label: 'vs last month' }} />
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
{/* Main Content Grid */}
|
|
97
|
+
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
|
|
98
|
+
{/* Recent Activity */}
|
|
99
|
+
<Card className="lg:col-span-2">
|
|
100
|
+
<CardHeader>
|
|
101
|
+
<h2 className="text-lg font-semibold text-text-primary">Recent Activity</h2>
|
|
102
|
+
</CardHeader>
|
|
103
|
+
<CardBody>
|
|
104
|
+
<RecentActivityList />
|
|
105
|
+
</CardBody>
|
|
106
|
+
</Card>
|
|
107
|
+
|
|
108
|
+
{/* Quick Actions */}
|
|
109
|
+
<Card>
|
|
110
|
+
<CardHeader>
|
|
111
|
+
<h2 className="text-lg font-semibold text-text-primary">Quick Actions</h2>
|
|
112
|
+
</CardHeader>
|
|
113
|
+
<CardBody className="space-y-2">
|
|
114
|
+
<QuickActionButton label="Create New Project" href="/projects/new" />
|
|
115
|
+
<QuickActionButton label="Invite Team Member" href="/settings/team" />
|
|
116
|
+
<QuickActionButton label="View Reports" href="/reports" />
|
|
117
|
+
</CardBody>
|
|
118
|
+
</Card>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Template: Recent Activity List
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
interface ActivityItem {
|
|
129
|
+
id: string;
|
|
130
|
+
action: string;
|
|
131
|
+
subject: string;
|
|
132
|
+
timestamp: string;
|
|
133
|
+
status?: 'success' | 'warning' | 'error' | 'info';
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function RecentActivityList({ items }: { items: ActivityItem[] }) {
|
|
137
|
+
return (
|
|
138
|
+
<ul className="divide-y divide-border-divider">
|
|
139
|
+
{items.map((item) => (
|
|
140
|
+
<li key={item.id} className="flex items-center justify-between py-3">
|
|
141
|
+
<div>
|
|
142
|
+
<p className="text-sm font-medium text-text-primary">{item.action}</p>
|
|
143
|
+
<p className="text-sm text-text-secondary">{item.subject}</p>
|
|
144
|
+
</div>
|
|
145
|
+
<div className="flex items-center gap-3">
|
|
146
|
+
{item.status && <Badge variant={item.status}>{item.status}</Badge>}
|
|
147
|
+
<span className="text-xs text-text-muted whitespace-nowrap">
|
|
148
|
+
{new Date(item.timestamp).toLocaleDateString()}
|
|
149
|
+
</span>
|
|
150
|
+
</div>
|
|
151
|
+
</li>
|
|
152
|
+
))}
|
|
153
|
+
</ul>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Template: Quick Action Button
|
|
159
|
+
|
|
160
|
+
```tsx
|
|
161
|
+
import { LinkButton } from '@mars-stack/ui';
|
|
162
|
+
|
|
163
|
+
function QuickActionButton({ label, href }: { label: string; href: string }) {
|
|
164
|
+
return (
|
|
165
|
+
<LinkButton href={href} variant="subtle" className="w-full justify-start">
|
|
166
|
+
{label}
|
|
167
|
+
</LinkButton>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Data Fetching Pattern
|
|
173
|
+
|
|
174
|
+
Dashboards typically aggregate data from multiple sources. Use parallel fetches:
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
// API route: /api/protected/dashboard/route.ts
|
|
178
|
+
export const GET = withAuthNoParams(async (request: AuthenticatedRequest) => {
|
|
179
|
+
try {
|
|
180
|
+
const userId = request.session.userId;
|
|
181
|
+
|
|
182
|
+
const [stats, recentActivity] = await Promise.all([
|
|
183
|
+
getDashboardStats(userId),
|
|
184
|
+
getRecentActivity(userId, 10),
|
|
185
|
+
]);
|
|
186
|
+
|
|
187
|
+
return NextResponse.json({ stats, recentActivity });
|
|
188
|
+
} catch (error) {
|
|
189
|
+
return handleApiError(error, { endpoint: '/api/protected/dashboard' });
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Responsive Breakpoints
|
|
195
|
+
|
|
196
|
+
| Breakpoint | Stat Cards | Main Grid |
|
|
197
|
+
|-----------|-----------|-----------|
|
|
198
|
+
| Mobile (`< sm`) | 1 column | 1 column |
|
|
199
|
+
| Tablet (`sm`) | 2 columns | 1 column |
|
|
200
|
+
| Desktop (`lg`) | 4 columns | 2:1 ratio |
|
|
201
|
+
|
|
202
|
+
## Checklist
|
|
203
|
+
|
|
204
|
+
- [ ] Page wrapped in max-width container with padding
|
|
205
|
+
- [ ] Stat cards in responsive grid (1/2/4 columns)
|
|
206
|
+
- [ ] Main content in 2:1 grid layout at `lg`
|
|
207
|
+
- [ ] All colours use semantic tokens
|
|
208
|
+
- [ ] Loading state for async data
|
|
209
|
+
- [ ] Empty states for sections with no data
|
|
210
|
+
- [ ] SEO metadata exported
|
|
211
|
+
- [ ] Data fetched via protected API route
|