create-tigra 1.1.0 → 2.0.1
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/LICENSE +21 -21
- package/README.md +80 -87
- package/bin/create-tigra.js +259 -308
- package/package.json +49 -41
- package/template/_claude/QUICK_REFERENCE.md +193 -0
- package/template/_claude/README.md +53 -0
- package/template/_claude/commands/create-client.md +881 -0
- package/template/_claude/commands/create-server.md +383 -0
- package/template/_claude/rules/client/01-project-structure.md +133 -0
- package/template/_claude/rules/client/02-components-and-types.md +146 -0
- package/template/_claude/rules/client/03-data-and-state.md +156 -0
- package/template/_claude/rules/client/04-design-system.md +185 -0
- package/template/_claude/rules/client/05-security.md +55 -0
- package/template/_claude/rules/client/06-ux-checklist.md +81 -0
- package/template/_claude/rules/client/core.md +42 -0
- package/template/_claude/rules/global/core.md +77 -0
- package/template/_claude/rules/server/core.md +50 -0
- package/template/_claude/rules/server/database.md +124 -0
- package/template/_claude/rules/server/project-conventions.md +150 -0
- package/template/_claude/rules/server/response-handling.md +144 -0
- package/template/client/.env.example +5 -0
- package/template/client/README.md +36 -0
- package/template/client/components.json +23 -0
- package/template/client/eslint.config.mjs +18 -0
- package/template/client/next.config.ts +34 -0
- package/template/client/package.json +44 -0
- package/template/client/postcss.config.mjs +7 -0
- package/template/client/src/app/(auth)/layout.tsx +18 -0
- package/template/client/src/app/(auth)/login/page.tsx +13 -0
- package/template/client/src/app/(auth)/register/page.tsx +13 -0
- package/template/client/src/app/(main)/dashboard/page.tsx +22 -0
- package/template/client/src/app/(main)/layout.tsx +11 -0
- package/template/client/src/app/error.tsx +27 -0
- package/template/client/src/app/favicon.ico +0 -0
- package/template/client/src/app/globals.css +145 -0
- package/template/client/src/app/layout.tsx +36 -0
- package/template/client/src/app/loading.tsx +11 -0
- package/template/client/src/app/not-found.tsx +23 -0
- package/template/client/src/app/page.tsx +45 -0
- package/template/client/src/app/providers.tsx +43 -0
- package/template/client/src/components/common/ConfirmDialog.tsx +56 -0
- package/template/client/src/components/common/EmptyState.tsx +31 -0
- package/template/client/src/components/common/LoadingSpinner.tsx +30 -0
- package/template/client/src/components/common/Pagination.tsx +55 -0
- package/template/client/src/components/layout/Footer.tsx +17 -0
- package/template/client/src/components/layout/Header.tsx +173 -0
- package/template/client/src/components/layout/MainLayout.tsx +18 -0
- package/template/client/src/components/ui/alert-dialog.tsx +196 -0
- package/template/client/src/components/ui/badge.tsx +48 -0
- package/template/client/src/components/ui/button.tsx +64 -0
- package/template/client/src/components/ui/card.tsx +92 -0
- package/template/client/src/components/ui/input.tsx +21 -0
- package/template/client/src/components/ui/label.tsx +24 -0
- package/template/client/src/components/ui/select.tsx +190 -0
- package/template/client/src/components/ui/skeleton.tsx +13 -0
- package/template/client/src/components/ui/table.tsx +116 -0
- package/template/client/src/features/auth/components/AuthInitializer.tsx +55 -0
- package/template/client/src/features/auth/components/LoginForm.tsx +107 -0
- package/template/client/src/features/auth/components/RegisterForm.tsx +178 -0
- package/template/client/src/features/auth/hooks/useAuth.ts +84 -0
- package/template/client/src/features/auth/services/auth.service.ts +52 -0
- package/template/client/src/features/auth/store/authSlice.ts +38 -0
- package/template/client/src/features/auth/types/auth.types.ts +32 -0
- package/template/client/src/hooks/useDebounce.ts +14 -0
- package/template/client/src/hooks/useLocalStorage.ts +55 -0
- package/template/client/src/hooks/useMediaQuery.ts +27 -0
- package/template/client/src/lib/api/api.types.ts +34 -0
- package/template/client/src/lib/api/axios.config.ts +98 -0
- package/template/client/src/lib/constants/api-endpoints.ts +18 -0
- package/template/client/src/lib/constants/app.constants.ts +12 -0
- package/template/client/src/lib/constants/routes.ts +9 -0
- package/template/client/src/lib/utils/error.ts +32 -0
- package/template/client/src/lib/utils/format.ts +37 -0
- package/template/client/src/lib/utils/security.ts +34 -0
- package/template/client/src/lib/utils.ts +6 -0
- package/template/client/src/middleware.ts +57 -0
- package/template/client/src/store/hooks.ts +7 -0
- package/template/client/src/store/index.ts +12 -0
- package/template/client/src/types/index.ts +3 -0
- package/template/client/tsconfig.json +34 -0
- package/template/gitignore +34 -0
- package/template/server/.dockerignore +66 -0
- package/template/server/.env.example +96 -69
- package/template/server/.env.production.example +90 -0
- package/template/server/Dockerfile +94 -0
- package/template/server/docker-compose.yml +82 -111
- package/template/server/docs/logging.md +62 -0
- package/template/server/eslint.config.mjs +17 -0
- package/template/server/package.json +68 -81
- package/template/server/phpmyadmin-config.php +26 -0
- package/template/server/postman_collection.json +666 -0
- package/template/server/prisma/schema.prisma +77 -93
- package/template/server/prisma/seed.ts +46 -142
- package/template/server/scripts/flush-redis.ts +41 -0
- package/template/server/src/app.ts +243 -71
- package/template/server/src/config/env.ts +67 -94
- package/template/server/src/libs/auth.ts +88 -0
- package/template/server/src/libs/cleanup.ts +35 -0
- package/template/server/src/libs/cookies.ts +46 -0
- package/template/server/src/libs/logger.ts +33 -60
- package/template/server/src/libs/monitoring.ts +205 -0
- package/template/server/src/libs/password.ts +38 -0
- package/template/server/src/libs/prisma.ts +68 -0
- package/template/server/src/libs/redis.ts +60 -79
- package/template/server/src/libs/requestLogger.ts +66 -0
- package/template/server/src/libs/storage/file-storage.service.ts +211 -0
- package/template/server/src/libs/storage/file-validator.ts +97 -0
- package/template/server/src/libs/storage/filename-sanitizer.ts +71 -0
- package/template/server/src/libs/storage/image-optimizer.service.ts +144 -0
- package/template/server/src/modules/auth/__tests__/auth.service.test.ts +365 -0
- package/template/server/src/modules/auth/auth.controller.ts +90 -141
- package/template/server/src/modules/auth/auth.repo.ts +120 -218
- package/template/server/src/modules/auth/auth.routes.ts +96 -83
- package/template/server/src/modules/auth/auth.schemas.ts +35 -137
- package/template/server/src/modules/auth/auth.service.ts +286 -329
- package/template/server/src/modules/auth/session.repo.ts +110 -0
- package/template/server/src/modules/users/users.controller.ts +120 -0
- package/template/server/src/modules/users/users.repo.ts +77 -0
- package/template/server/src/modules/users/users.routes.ts +89 -0
- package/template/server/src/modules/users/users.schemas.ts +21 -0
- package/template/server/src/modules/users/users.service.ts +169 -0
- package/template/server/src/server.ts +58 -139
- package/template/server/src/shared/errors/AppError.ts +21 -0
- package/template/server/src/shared/errors/errors.ts +43 -0
- package/template/server/src/shared/responses/paginatedResponse.ts +38 -0
- package/template/server/src/shared/responses/successResponse.ts +17 -0
- package/template/server/src/shared/schemas/pagination.schema.ts +12 -0
- package/template/server/src/shared/types/index.ts +26 -0
- package/template/server/src/test/setup.ts +74 -38
- package/template/server/tsconfig.json +27 -89
- package/template/server/uploads/avatars/.gitkeep +1 -0
- package/template/server/vitest.config.ts +43 -98
- package/template/.agent/rules/client/01-project-structure.md +0 -326
- package/template/.agent/rules/client/02-component-patterns.md +0 -249
- package/template/.agent/rules/client/03-typescript-rules.md +0 -226
- package/template/.agent/rules/client/04-state-management.md +0 -474
- package/template/.agent/rules/client/05-api-integration.md +0 -129
- package/template/.agent/rules/client/06-forms-validation.md +0 -129
- package/template/.agent/rules/client/07-common-patterns.md +0 -150
- package/template/.agent/rules/client/08-color-system.md +0 -93
- package/template/.agent/rules/client/09-security-rules.md +0 -97
- package/template/.agent/rules/client/10-testing-strategy.md +0 -370
- package/template/.agent/rules/global/ai-edit-safety.md +0 -38
- package/template/.agent/rules/server/01-db-and-migrations.md +0 -242
- package/template/.agent/rules/server/02-general-rules.md +0 -111
- package/template/.agent/rules/server/03-migrations.md +0 -20
- package/template/.agent/rules/server/04-pagination.md +0 -130
- package/template/.agent/rules/server/05-project-conventions.md +0 -71
- package/template/.agent/rules/server/06-response-handling.md +0 -173
- package/template/.agent/rules/server/07-testing-strategy.md +0 -506
- package/template/.agent/rules/server/08-observability.md +0 -180
- package/template/.agent/rules/server/10-background-jobs-v2.md +0 -185
- package/template/.agent/rules/server/11-rate-limiting-v2.md +0 -210
- package/template/.agent/rules/server/12-performance-optimization.md +0 -567
- package/template/.claude/rules/client-01-project-structure.md +0 -327
- package/template/.claude/rules/client-02-component-patterns.md +0 -250
- package/template/.claude/rules/client-03-typescript-rules.md +0 -227
- package/template/.claude/rules/client-04-state-management.md +0 -475
- package/template/.claude/rules/client-05-api-integration.md +0 -130
- package/template/.claude/rules/client-06-forms-validation.md +0 -130
- package/template/.claude/rules/client-07-common-patterns.md +0 -151
- package/template/.claude/rules/client-08-color-system.md +0 -94
- package/template/.claude/rules/client-09-security-rules.md +0 -98
- package/template/.claude/rules/client-10-testing-strategy.md +0 -371
- package/template/.claude/rules/global-ai-edit-safety.md +0 -39
- package/template/.claude/rules/server-01-db-and-migrations.md +0 -243
- package/template/.claude/rules/server-02-general-rules.md +0 -112
- package/template/.claude/rules/server-03-migrations.md +0 -21
- package/template/.claude/rules/server-04-pagination.md +0 -131
- package/template/.claude/rules/server-05-project-conventions.md +0 -72
- package/template/.claude/rules/server-06-response-handling.md +0 -174
- package/template/.claude/rules/server-07-testing-strategy.md +0 -507
- package/template/.claude/rules/server-08-observability.md +0 -181
- package/template/.claude/rules/server-10-background-jobs-v2.md +0 -186
- package/template/.claude/rules/server-11-rate-limiting-v2.md +0 -211
- package/template/.claude/rules/server-12-performance-optimization.md +0 -568
- package/template/.cursor/rules/client-01-project-structure.mdc +0 -327
- package/template/.cursor/rules/client-02-component-patterns.mdc +0 -250
- package/template/.cursor/rules/client-03-typescript-rules.mdc +0 -227
- package/template/.cursor/rules/client-04-state-management.mdc +0 -475
- package/template/.cursor/rules/client-05-api-integration.mdc +0 -130
- package/template/.cursor/rules/client-06-forms-validation.mdc +0 -130
- package/template/.cursor/rules/client-07-common-patterns.mdc +0 -151
- package/template/.cursor/rules/client-08-color-system.mdc +0 -94
- package/template/.cursor/rules/client-09-security-rules.mdc +0 -98
- package/template/.cursor/rules/client-10-testing-strategy.mdc +0 -371
- package/template/.cursor/rules/global-ai-edit-safety.mdc +0 -39
- package/template/.cursor/rules/server-01-db-and-migrations.mdc +0 -243
- package/template/.cursor/rules/server-02-general-rules.mdc +0 -112
- package/template/.cursor/rules/server-03-migrations.mdc +0 -21
- package/template/.cursor/rules/server-04-pagination.mdc +0 -131
- package/template/.cursor/rules/server-05-project-conventions.mdc +0 -72
- package/template/.cursor/rules/server-06-response-handling.mdc +0 -174
- package/template/.cursor/rules/server-07-testing-strategy.mdc +0 -507
- package/template/.cursor/rules/server-08-observability.mdc +0 -181
- package/template/.cursor/rules/server-09-api-documentation-v2.mdc +0 -169
- package/template/.cursor/rules/server-10-background-jobs-v2.mdc +0 -186
- package/template/.cursor/rules/server-11-rate-limiting-v2.mdc +0 -211
- package/template/.cursor/rules/server-12-performance-optimization.mdc +0 -568
- package/template/CLAUDE.md +0 -207
- package/template/server/.tsc-aliasrc.json +0 -13
- package/template/server/IMPORT_FIX_CHECKLIST.md +0 -98
- package/template/server/IMPORT_FIX_COMPLETE.md +0 -89
- package/template/server/README.md +0 -183
- package/template/server/REMAINING_IMPORT_FIXES.md +0 -150
- package/template/server/SECURITY.md +0 -190
- package/template/server/Tigra-API.postman_collection.json +0 -733
- package/template/server/biome.json +0 -42
- package/template/server/scripts/fix-all-imports.ps1 +0 -52
- package/template/server/scripts/fix-imports-reference.ps1 +0 -16
- package/template/server/scripts/fix-imports.mjs +0 -55
- package/template/server/scripts/setup-env.js +0 -50
- package/template/server/scripts/wait-for-db.js +0 -60
- package/template/server/src/hooks/request-timing.hook.ts +0 -26
- package/template/server/src/libs/auth/authenticate.middleware.ts +0 -22
- package/template/server/src/libs/auth/rbac.middleware.test.ts +0 -134
- package/template/server/src/libs/auth/rbac.middleware.ts +0 -147
- package/template/server/src/libs/db.ts +0 -76
- package/template/server/src/libs/error-handler.ts +0 -89
- package/template/server/src/libs/queue.ts +0 -79
- package/template/server/src/modules/admin/admin.controller.ts +0 -122
- package/template/server/src/modules/admin/admin.routes.ts +0 -62
- package/template/server/src/modules/admin/admin.schemas.ts +0 -35
- package/template/server/src/modules/admin/admin.service.ts +0 -167
- package/template/server/src/modules/auth/auth.integration.test.ts +0 -150
- package/template/server/src/modules/auth/auth.service.test.ts +0 -119
- package/template/server/src/modules/auth/auth.types.ts +0 -97
- package/template/server/src/modules/resources/resources.controller.ts +0 -218
- package/template/server/src/modules/resources/resources.repo.ts +0 -253
- package/template/server/src/modules/resources/resources.routes.ts +0 -116
- package/template/server/src/modules/resources/resources.schemas.ts +0 -146
- package/template/server/src/modules/resources/resources.service.ts +0 -218
- package/template/server/src/modules/resources/resources.types.ts +0 -73
- package/template/server/src/plugins/rate-limit.plugin.ts +0 -21
- package/template/server/src/plugins/security.plugin.ts +0 -21
- package/template/server/src/routes/health.routes.ts +0 -31
- package/template/server/src/types/fastify.d.ts +0 -36
- package/template/server/src/utils/errors.ts +0 -108
- package/template/server/src/utils/pagination.ts +0 -120
- package/template/server/src/utils/response.ts +0 -110
- package/template/server/src/workers/file.worker.ts +0 -106
- package/template/server/tsconfig.build.json +0 -30
- package/template/server/tsconfig.test.json +0 -22
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
> **SCOPE**: These rules apply specifically to the **server** directory.
|
|
2
|
+
|
|
3
|
+
# Database & Migrations
|
|
4
|
+
|
|
5
|
+
## Principles
|
|
6
|
+
|
|
7
|
+
- **Database**: MySQL 8.0+
|
|
8
|
+
- **ORM**: Prisma 6.x (do NOT upgrade to 7.x without testing)
|
|
9
|
+
- **Schema source of truth**: `prisma/schema.prisma`
|
|
10
|
+
- **Dev**: Fast iteration with resets. **Prod**: Safe, forward-only migrations.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Critical Rules
|
|
15
|
+
|
|
16
|
+
| Rule | Detail |
|
|
17
|
+
|------|--------|
|
|
18
|
+
| Schema changes | Prisma migrations ONLY. Never raw DDL. Data changes (INSERT/UPDATE/DELETE) can be raw SQL. |
|
|
19
|
+
| Dev conflicts | Don't fix migration files manually. Run `prisma:reset` then `prisma:seed`. |
|
|
20
|
+
| `db push` | Prototyping only. NEVER in production. |
|
|
21
|
+
| `prisma:reset` | Freely in dev. NEVER in production. |
|
|
22
|
+
| Production deploys | ONLY `prisma:migrate deploy`. Never reset, never manual edits. |
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Naming Conventions
|
|
27
|
+
|
|
28
|
+
| Entity | Format | Example |
|
|
29
|
+
|--------|--------|---------|
|
|
30
|
+
| Tables | `snake_case` plural | `users`, `order_items` |
|
|
31
|
+
| Columns | `snake_case` | `user_id`, `created_at` |
|
|
32
|
+
| Foreign keys | `<table_singular>_id` | `user_id`, `category_id` |
|
|
33
|
+
| Prisma models | `PascalCase` singular | `User`, `OrderItem` |
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Required Fields
|
|
38
|
+
|
|
39
|
+
Every main entity table MUST have:
|
|
40
|
+
```prisma
|
|
41
|
+
model EntityName {
|
|
42
|
+
id String @id @default(uuid())
|
|
43
|
+
createdAt DateTime @default(now())
|
|
44
|
+
updatedAt DateTime @updatedAt
|
|
45
|
+
// Optional:
|
|
46
|
+
deletedAt DateTime? // Soft deletes (prefer for user content)
|
|
47
|
+
isActive Boolean @default(true) // Disable without deleting
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Relationships
|
|
54
|
+
|
|
55
|
+
- Use proper foreign keys with `onDelete: Cascade` for dependent data
|
|
56
|
+
- Explicit junction tables for many-to-many (not implicit)
|
|
57
|
+
- Normalize shared lookup data into own tables (no duplicated strings)
|
|
58
|
+
- Polymorphic attachments: use `entityType` + `entityId` with `@@index([entityType, entityId])`
|
|
59
|
+
- **Column renames**: Two-step migration — add new column, migrate data, then drop old column. Renaming directly DROPS data.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Indexing
|
|
64
|
+
|
|
65
|
+
**Always index:** foreign keys, filter columns (WHERE clauses), composite filters, unique constraints.
|
|
66
|
+
|
|
67
|
+
**Never index:** low-cardinality booleans alone, text/blob columns, fields not used in WHERE/ORDER BY.
|
|
68
|
+
|
|
69
|
+
```prisma
|
|
70
|
+
@@index([userId]) // Foreign key
|
|
71
|
+
@@index([categoryId, isActive]) // Composite filter
|
|
72
|
+
@@unique([email]) // Unique constraint
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Migration Workflow
|
|
78
|
+
|
|
79
|
+
1. Edit `prisma/schema.prisma`
|
|
80
|
+
2. Run: `npm run prisma:migrate dev --name descriptive_name`
|
|
81
|
+
3. If conflict: `npm run prisma:reset` then `npm run prisma:seed`
|
|
82
|
+
4. Verify generated SQL and use `prisma:studio` to inspect
|
|
83
|
+
|
|
84
|
+
**Adding NOT NULL column**: Must provide a default value or make it optional, otherwise Prisma will error.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Production Deployment
|
|
89
|
+
|
|
90
|
+
**Pre-deploy**: All migrations tested locally, seed runs, migration files committed to git.
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
npm run prisma:migrate deploy # Apply pending migrations (safe)
|
|
94
|
+
npm run prisma:generate # Regenerate client
|
|
95
|
+
npm start # Restart application
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Troubleshooting
|
|
101
|
+
|
|
102
|
+
| Symptom | Fix |
|
|
103
|
+
|---------|-----|
|
|
104
|
+
| `DATABASE_URL` not found | Verify `.env` exists with valid `DATABASE_URL`. If Prisma 7 issue, downgrade to v6. |
|
|
105
|
+
| "Table already exists" | `npm run prisma:reset` (dev only) |
|
|
106
|
+
| "Prisma Client is not generated" | `npm run prisma:generate` |
|
|
107
|
+
| Slow queries | Add missing indexes via schema, use `EXPLAIN`, use `select` to limit fields |
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Commands Reference
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# Development
|
|
115
|
+
npm run prisma:studio # Visual DB browser
|
|
116
|
+
npm run prisma:migrate dev # Create migration
|
|
117
|
+
npm run prisma:reset # Drop all, rerun migrations
|
|
118
|
+
npm run prisma:seed # Repopulate test data
|
|
119
|
+
npm run prisma:generate # Regenerate client
|
|
120
|
+
|
|
121
|
+
# Production
|
|
122
|
+
npm run prisma:migrate deploy # Apply migrations (safe)
|
|
123
|
+
npm run prisma:generate # Regenerate client
|
|
124
|
+
```
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
> **SCOPE**: These rules apply specifically to the **server** directory.
|
|
2
|
+
|
|
3
|
+
# Project Conventions
|
|
4
|
+
|
|
5
|
+
## Stack
|
|
6
|
+
|
|
7
|
+
| Technology | Purpose |
|
|
8
|
+
|------------|---------|
|
|
9
|
+
| Node.js 20+ LTS | Runtime |
|
|
10
|
+
| Fastify | HTTP framework (plugins, route prefixes, centralized error handling) |
|
|
11
|
+
| TypeScript (strict) | Language — always type parameters and return types |
|
|
12
|
+
| MySQL 8.0+ | Primary database |
|
|
13
|
+
| Prisma 6.x | ORM, migrations, schema source of truth |
|
|
14
|
+
| Redis | Caching (frequently accessed data, lookups), rate limiting, background task signaling |
|
|
15
|
+
| Zod | Runtime validation and type inference |
|
|
16
|
+
| JWT (HS256/RS256) | Auth — minimal token payload (id, role), role-based access control |
|
|
17
|
+
| PM2 + Nginx | Production deployment (cluster mode) |
|
|
18
|
+
| Jest / Vitest | Testing — deterministic, mock external APIs |
|
|
19
|
+
|
|
20
|
+
## Package Manager
|
|
21
|
+
|
|
22
|
+
Detect from lockfile: `pnpm-lock.yaml` → pnpm, `yarn.lock` → yarn, else → npm.
|
|
23
|
+
|
|
24
|
+
## Folder Structure
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
src/
|
|
28
|
+
├── app.ts # Fastify instance and plugin registration
|
|
29
|
+
├── server.ts # listen() call only, no app logic
|
|
30
|
+
├── config/ # env, config, constants
|
|
31
|
+
├── libs/ # shared libraries (db, redis, logger, auth)
|
|
32
|
+
└── modules/<domain>/ # domain modules
|
|
33
|
+
├── <domain>.routes.ts
|
|
34
|
+
├── <domain>.controller.ts
|
|
35
|
+
├── <domain>.service.ts
|
|
36
|
+
├── <domain>.repo.ts
|
|
37
|
+
├── <domain>.schemas.ts
|
|
38
|
+
└── <domain>.types.ts (if needed)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
New modules MUST follow `src/modules/<name>/` with the exact naming pattern above.
|
|
42
|
+
|
|
43
|
+
## API Routes
|
|
44
|
+
|
|
45
|
+
All routes prefixed with `/api/v1`. Grouped by domain: `/api/v1/<domain>/...`
|
|
46
|
+
|
|
47
|
+
Register routes as Fastify plugins in `<domain>.routes.ts`.
|
|
48
|
+
|
|
49
|
+
## Coding Style
|
|
50
|
+
|
|
51
|
+
- TypeScript `strict` mode ON
|
|
52
|
+
- `async/await` over `.then()`
|
|
53
|
+
- Named exports (except `src/app.ts` which default-exports the Fastify instance)
|
|
54
|
+
- Relative imports within a module (`./user.service`), aliased imports cross-module (`@modules/users/...`)
|
|
55
|
+
- Use `logger` from `src/libs/logger` — never `console.log`
|
|
56
|
+
|
|
57
|
+
## Layer Responsibilities
|
|
58
|
+
|
|
59
|
+
| Layer | MUST | MUST NOT |
|
|
60
|
+
|-------|------|----------|
|
|
61
|
+
| **Routes** | Register Fastify plugins, define HTTP method + path | Contain any logic |
|
|
62
|
+
| **Controllers** | Validate input (Zod), call services, return via `successResponse`/`paginatedResponse`, throw typed `AppError` | Business logic, direct DB access, manual error JSON, set HTTP status codes for errors |
|
|
63
|
+
| **Services** | All business logic, throw `AppError` subclasses, call repos for DB ops, be stateless | Touch Fastify (request/reply), format responses, return HTTP status codes |
|
|
64
|
+
| **Repositories** | Prisma queries, return raw data | Business logic, error formatting |
|
|
65
|
+
|
|
66
|
+
## Postman Collection
|
|
67
|
+
|
|
68
|
+
When API endpoints are created or modified, **always update the Postman collection** (`postman/collection.json`). If the collection does not exist, create it.
|
|
69
|
+
|
|
70
|
+
### Collection Structure
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
postman/
|
|
74
|
+
├── collection.json # Postman v2.1 collection
|
|
75
|
+
└── environment.json # Environment variables template
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Environment Variables
|
|
79
|
+
|
|
80
|
+
Use variables so requests are connected and portable:
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"variables": [
|
|
85
|
+
{ "key": "baseUrl", "value": "http://localhost:3000/api/v1" },
|
|
86
|
+
{ "key": "accessToken", "value": "" },
|
|
87
|
+
{ "key": "refreshToken", "value": "" },
|
|
88
|
+
{ "key": "userId", "value": "" }
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
- **`{{baseUrl}}`** — all request URLs use this prefix, never hardcoded hosts.
|
|
94
|
+
- **`{{accessToken}}`** / **`{{refreshToken}}`** — set automatically via login/register test scripts.
|
|
95
|
+
- **`{{userId}}`** and other IDs — captured from responses so subsequent requests can reference them.
|
|
96
|
+
|
|
97
|
+
### Auto-set Tokens via Test Scripts
|
|
98
|
+
|
|
99
|
+
The **Login** and **Register** requests MUST include a `Tests` script that stores tokens and user data into collection variables:
|
|
100
|
+
|
|
101
|
+
```javascript
|
|
102
|
+
if (pm.response.code === 200 || pm.response.code === 201) {
|
|
103
|
+
const res = pm.response.json();
|
|
104
|
+
pm.collectionVariables.set("accessToken", res.data.tokens.accessToken);
|
|
105
|
+
pm.collectionVariables.set("refreshToken", res.data.tokens.refreshToken);
|
|
106
|
+
pm.collectionVariables.set("userId", res.data.user.id);
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Auth Header
|
|
111
|
+
|
|
112
|
+
All authenticated requests use a collection-level or folder-level **Bearer Token** auth set to `{{accessToken}}`. Do not duplicate the auth header on every request — inherit from the parent folder.
|
|
113
|
+
|
|
114
|
+
### Folder Organization
|
|
115
|
+
|
|
116
|
+
Organize requests into folders matching domain modules:
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
Collection Root (Bearer Token: {{accessToken}})
|
|
120
|
+
├── Auth (No Auth)
|
|
121
|
+
│ ├── Register → POST {{baseUrl}}/auth/register
|
|
122
|
+
│ ├── Login → POST {{baseUrl}}/auth/login [sets tokens]
|
|
123
|
+
│ ├── Refresh Token → POST {{baseUrl}}/auth/refresh
|
|
124
|
+
│ └── Logout → POST {{baseUrl}}/auth/logout
|
|
125
|
+
├── Users
|
|
126
|
+
│ ├── Get Me → GET {{baseUrl}}/users/me
|
|
127
|
+
│ ├── Update Me → PATCH {{baseUrl}}/users/me
|
|
128
|
+
│ └── Delete Me → DELETE {{baseUrl}}/users/me
|
|
129
|
+
└── <Domain> → One folder per module
|
|
130
|
+
├── List → GET {{baseUrl}}/<domain>
|
|
131
|
+
├── Get by ID → GET {{baseUrl}}/<domain>/{{<domain>Id}}
|
|
132
|
+
├── Create → POST {{baseUrl}}/<domain>
|
|
133
|
+
├── Update → PATCH {{baseUrl}}/<domain>/{{<domain>Id}}
|
|
134
|
+
└── Delete → DELETE {{baseUrl}}/<domain>/{{<domain>Id}}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Rules
|
|
138
|
+
|
|
139
|
+
1. **Every route gets a request.** No endpoint should exist without a matching Postman request.
|
|
140
|
+
2. **Include example request bodies** for POST/PATCH/PUT with realistic placeholder data.
|
|
141
|
+
3. **Capture IDs from create responses** — add a `Tests` script that sets `{{<domain>Id}}` so Get/Update/Delete requests work without manual copy-paste.
|
|
142
|
+
4. **Auth folder uses "No Auth"** — login and register don't need tokens. All other folders inherit Bearer Token from the collection root.
|
|
143
|
+
5. **Keep it importable** — the collection must be valid Postman v2.1 JSON that anyone can import and run immediately after setting `baseUrl`.
|
|
144
|
+
|
|
145
|
+
## Security
|
|
146
|
+
|
|
147
|
+
- Validate all inputs with Zod schemas
|
|
148
|
+
- Never interpolate raw values into SQL — always use Prisma query builder
|
|
149
|
+
- Rate limit public endpoints
|
|
150
|
+
- Avoid N+1 queries — prefer joins or batched queries
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
> **SCOPE**: These rules apply specifically to the **server** directory.
|
|
2
|
+
|
|
3
|
+
# Response Handling
|
|
4
|
+
|
|
5
|
+
All API responses follow a unified contract. No exceptions.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Success Response
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"success": true,
|
|
14
|
+
"message": "Human-readable message",
|
|
15
|
+
"data": {}
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- `data` can be an object, array, or null
|
|
20
|
+
- Controllers MUST use the shared `successResponse()` helper
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
// Allowed
|
|
24
|
+
return reply.send(successResponse("Item created successfully", item));
|
|
25
|
+
|
|
26
|
+
// Forbidden — never send raw values or custom shapes
|
|
27
|
+
reply.send(item);
|
|
28
|
+
reply.send({ data: item });
|
|
29
|
+
reply.send({ success: true, item });
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Error Response
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"success": false,
|
|
39
|
+
"error": {
|
|
40
|
+
"code": "MACHINE_READABLE_CODE",
|
|
41
|
+
"message": "Human-readable, user-safe message"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
- `code`: stable machine-readable string (e.g. `RESOURCE_NOT_FOUND`, `VALIDATION_FAILED`)
|
|
47
|
+
- Never expose internal details, stack traces, or SQL errors
|
|
48
|
+
- Controllers MUST NOT manually construct error responses
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Paginated Response
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"success": true,
|
|
57
|
+
"message": "string",
|
|
58
|
+
"data": {
|
|
59
|
+
"items": [],
|
|
60
|
+
"pagination": {
|
|
61
|
+
"page": 1,
|
|
62
|
+
"limit": 10,
|
|
63
|
+
"totalItems": 237,
|
|
64
|
+
"totalPages": 24,
|
|
65
|
+
"hasNextPage": true,
|
|
66
|
+
"hasPreviousPage": false
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Controllers MUST use the shared `paginatedResponse()` helper:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
function paginatedResponse<T>(message: string, items: T[], page: number, limit: number, totalItems: number)
|
|
76
|
+
|
|
77
|
+
// Allowed
|
|
78
|
+
return reply.send(paginatedResponse("Items retrieved successfully", items, page, limit, totalCount));
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Pagination Input Validation
|
|
82
|
+
|
|
83
|
+
All paginated endpoints MUST validate with this shared Zod schema:
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
const PaginationSchema = z.object({
|
|
87
|
+
page: z.coerce.number().int().min(1).default(1),
|
|
88
|
+
limit: z.coerce.number().int().min(1).max(100).default(10)
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Service Return Pattern for Pagination
|
|
93
|
+
|
|
94
|
+
Services return `{ items, totalItems }`. Controllers build the pagination metadata.
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
// Service returns:
|
|
98
|
+
return { items, totalItems: count };
|
|
99
|
+
// Offset calculation: (page - 1) * limit
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Filters with Pagination
|
|
103
|
+
|
|
104
|
+
- Apply filters/sorting BEFORE `limit` and `offset`
|
|
105
|
+
- Count total items AFTER filters but BEFORE pagination
|
|
106
|
+
- Pages start at 1 (never 0)
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Error Architecture
|
|
111
|
+
|
|
112
|
+
ONE global Fastify error handler via `fastify.setErrorHandler(...)`:
|
|
113
|
+
- Maps `AppError` subclasses to HTTP status codes
|
|
114
|
+
- Formats the unified error JSON structure
|
|
115
|
+
- Logs internal details server-side only — never sends to client
|
|
116
|
+
|
|
117
|
+
### AppError Subclasses
|
|
118
|
+
|
|
119
|
+
All errors MUST extend `AppError` (which defines `code`, `message`, `statusCode`). Only throw these typed subclasses — never raw `Error`, strings, or plain objects.
|
|
120
|
+
|
|
121
|
+
| Class | Status | Example Code |
|
|
122
|
+
|-------|--------|--------------|
|
|
123
|
+
| `BadRequestError` | 400 | `INVALID_INPUT` |
|
|
124
|
+
| `ValidationError` | 422 | `VALIDATION_FAILED` |
|
|
125
|
+
| `UnauthorizedError` | 401 | `UNAUTHORIZED` |
|
|
126
|
+
| `ForbiddenError` | 403 | `FORBIDDEN` |
|
|
127
|
+
| `NotFoundError` | 404 | `RESOURCE_NOT_FOUND` |
|
|
128
|
+
| `ConflictError` | 409 | `EMAIL_ALREADY_EXISTS` |
|
|
129
|
+
| `InternalError` | 500 | `INTERNAL_ERROR` |
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Controller Response Rules
|
|
134
|
+
|
|
135
|
+
**ALWAYS:**
|
|
136
|
+
- Use `successResponse()` or `paginatedResponse()` for all responses
|
|
137
|
+
- Validate input with Zod before calling services
|
|
138
|
+
- Throw typed `AppError` subclasses on failure
|
|
139
|
+
|
|
140
|
+
**NEVER:**
|
|
141
|
+
- Send raw values or custom JSON shapes
|
|
142
|
+
- Set HTTP status codes for errors (global handler does this)
|
|
143
|
+
- Catch errors unless rethrowing as typed `AppError`
|
|
144
|
+
- Manually construct error response JSON
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
|
2
|
+
|
|
3
|
+
## Getting Started
|
|
4
|
+
|
|
5
|
+
First, run the development server:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm run dev
|
|
9
|
+
# or
|
|
10
|
+
yarn dev
|
|
11
|
+
# or
|
|
12
|
+
pnpm dev
|
|
13
|
+
# or
|
|
14
|
+
bun dev
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
|
18
|
+
|
|
19
|
+
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
|
20
|
+
|
|
21
|
+
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
|
22
|
+
|
|
23
|
+
## Learn More
|
|
24
|
+
|
|
25
|
+
To learn more about Next.js, take a look at the following resources:
|
|
26
|
+
|
|
27
|
+
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
|
28
|
+
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
|
29
|
+
|
|
30
|
+
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
|
31
|
+
|
|
32
|
+
## Deploy on Vercel
|
|
33
|
+
|
|
34
|
+
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
|
35
|
+
|
|
36
|
+
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
|
3
|
+
"style": "new-york",
|
|
4
|
+
"rsc": true,
|
|
5
|
+
"tsx": true,
|
|
6
|
+
"tailwind": {
|
|
7
|
+
"config": "",
|
|
8
|
+
"css": "src/app/globals.css",
|
|
9
|
+
"baseColor": "neutral",
|
|
10
|
+
"cssVariables": true,
|
|
11
|
+
"prefix": ""
|
|
12
|
+
},
|
|
13
|
+
"iconLibrary": "lucide",
|
|
14
|
+
"rtl": false,
|
|
15
|
+
"aliases": {
|
|
16
|
+
"components": "@/components",
|
|
17
|
+
"utils": "@/lib/utils",
|
|
18
|
+
"ui": "@/components/ui",
|
|
19
|
+
"lib": "@/lib",
|
|
20
|
+
"hooks": "@/hooks"
|
|
21
|
+
},
|
|
22
|
+
"registries": {}
|
|
23
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineConfig, globalIgnores } from "eslint/config";
|
|
2
|
+
import nextVitals from "eslint-config-next/core-web-vitals";
|
|
3
|
+
import nextTs from "eslint-config-next/typescript";
|
|
4
|
+
|
|
5
|
+
const eslintConfig = defineConfig([
|
|
6
|
+
...nextVitals,
|
|
7
|
+
...nextTs,
|
|
8
|
+
// Override default ignores of eslint-config-next.
|
|
9
|
+
globalIgnores([
|
|
10
|
+
// Default ignores of eslint-config-next:
|
|
11
|
+
".next/**",
|
|
12
|
+
"out/**",
|
|
13
|
+
"build/**",
|
|
14
|
+
"next-env.d.ts",
|
|
15
|
+
]),
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
export default eslintConfig;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { NextConfig } from "next";
|
|
2
|
+
|
|
3
|
+
const nextConfig: NextConfig = {
|
|
4
|
+
async headers() {
|
|
5
|
+
return [
|
|
6
|
+
{
|
|
7
|
+
source: "/(.*)",
|
|
8
|
+
headers: [
|
|
9
|
+
{ key: "X-Frame-Options", value: "DENY" },
|
|
10
|
+
{ key: "X-Content-Type-Options", value: "nosniff" },
|
|
11
|
+
{ key: "Referrer-Policy", value: "origin-when-cross-origin" },
|
|
12
|
+
{ key: "X-XSS-Protection", value: "1; mode=block" },
|
|
13
|
+
{
|
|
14
|
+
key: "Content-Security-Policy",
|
|
15
|
+
value: [
|
|
16
|
+
"default-src 'self'",
|
|
17
|
+
"script-src 'self' 'unsafe-eval' 'unsafe-inline'",
|
|
18
|
+
"style-src 'self' 'unsafe-inline'",
|
|
19
|
+
"img-src 'self' blob: data: https:",
|
|
20
|
+
"font-src 'self'",
|
|
21
|
+
"object-src 'none'",
|
|
22
|
+
"base-uri 'self'",
|
|
23
|
+
"form-action 'self'",
|
|
24
|
+
"frame-ancestors 'none'",
|
|
25
|
+
`connect-src 'self' ${process.env.NEXT_PUBLIC_API_BASE_URL || "http://localhost:8000"}`,
|
|
26
|
+
].join("; "),
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default nextConfig;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{PROJECT_NAME}}-client",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "next dev",
|
|
7
|
+
"build": "next build",
|
|
8
|
+
"start": "next start",
|
|
9
|
+
"lint": "eslint src/"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@hookform/resolvers": "^5.2.2",
|
|
13
|
+
"@reduxjs/toolkit": "^2.11.2",
|
|
14
|
+
"@tanstack/react-query": "^5.90.21",
|
|
15
|
+
"axios": "^1.13.5",
|
|
16
|
+
"class-variance-authority": "^0.7.1",
|
|
17
|
+
"clsx": "^2.1.1",
|
|
18
|
+
"lucide-react": "^0.563.0",
|
|
19
|
+
"next": "16.1.6",
|
|
20
|
+
"next-themes": "^0.4.6",
|
|
21
|
+
"radix-ui": "^1.4.3",
|
|
22
|
+
"react": "19.2.3",
|
|
23
|
+
"react-dom": "19.2.3",
|
|
24
|
+
"react-hook-form": "^7.71.1",
|
|
25
|
+
"react-redux": "^9.2.0",
|
|
26
|
+
"sonner": "^2.0.7",
|
|
27
|
+
"tailwind-merge": "^3.4.0",
|
|
28
|
+
"tailwindcss-animate": "^1.0.7",
|
|
29
|
+
"zod": "^4.3.6"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@tailwindcss/postcss": "^4",
|
|
33
|
+
"@tanstack/react-query-devtools": "^5.91.3",
|
|
34
|
+
"@types/node": "^20",
|
|
35
|
+
"@types/react": "^19",
|
|
36
|
+
"@types/react-dom": "^19",
|
|
37
|
+
"eslint": "^9",
|
|
38
|
+
"eslint-config-next": "16.1.6",
|
|
39
|
+
"shadcn": "^3.8.4",
|
|
40
|
+
"tailwindcss": "^4",
|
|
41
|
+
"tw-animate-css": "^1.4.0",
|
|
42
|
+
"typescript": "^5"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
|
|
3
|
+
import { APP_NAME } from '@/lib/constants/app.constants';
|
|
4
|
+
|
|
5
|
+
export default function AuthLayout({
|
|
6
|
+
children,
|
|
7
|
+
}: {
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
}): React.ReactElement {
|
|
10
|
+
return (
|
|
11
|
+
<div className="flex min-h-dvh flex-col items-center justify-center px-4 py-12">
|
|
12
|
+
<span className="mb-8 text-xl font-semibold tracking-tight text-foreground">
|
|
13
|
+
{APP_NAME}
|
|
14
|
+
</span>
|
|
15
|
+
{children}
|
|
16
|
+
</div>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
|
|
3
|
+
import { LoginForm } from '@/features/auth/components/LoginForm';
|
|
4
|
+
|
|
5
|
+
import type { Metadata } from 'next';
|
|
6
|
+
|
|
7
|
+
export const metadata: Metadata = {
|
|
8
|
+
title: 'Sign in',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default function LoginPage(): React.ReactElement {
|
|
12
|
+
return <LoginForm />;
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
|
|
3
|
+
import { RegisterForm } from '@/features/auth/components/RegisterForm';
|
|
4
|
+
|
|
5
|
+
import type { Metadata } from 'next';
|
|
6
|
+
|
|
7
|
+
export const metadata: Metadata = {
|
|
8
|
+
title: 'Create account',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default function RegisterPage(): React.ReactElement {
|
|
12
|
+
return <RegisterForm />;
|
|
13
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { Metadata } from 'next';
|
|
4
|
+
|
|
5
|
+
export const metadata: Metadata = {
|
|
6
|
+
title: 'Dashboard',
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export default function DashboardPage(): React.ReactElement {
|
|
10
|
+
return (
|
|
11
|
+
<div className="container mx-auto px-4 py-12 md:px-6 lg:px-8">
|
|
12
|
+
<div className="space-y-6">
|
|
13
|
+
<div>
|
|
14
|
+
<h1 className="text-3xl font-bold tracking-tight">Dashboard</h1>
|
|
15
|
+
<p className="mt-2 text-muted-foreground">
|
|
16
|
+
Welcome to your dashboard. This is where you'll manage everything.
|
|
17
|
+
</p>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
|
|
3
|
+
import { MainLayout } from '@/components/layout/MainLayout';
|
|
4
|
+
|
|
5
|
+
export default function MainGroupLayout({
|
|
6
|
+
children,
|
|
7
|
+
}: {
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
}): React.ReactElement {
|
|
10
|
+
return <MainLayout>{children}</MainLayout>;
|
|
11
|
+
}
|