moicle 1.0.0
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/README.md +201 -0
- package/assets/agents/developers/flutter-mobile-dev.md +69 -0
- package/assets/agents/developers/go-backend-dev.md +57 -0
- package/assets/agents/developers/laravel-backend-dev.md +123 -0
- package/assets/agents/developers/react-frontend-dev.md +69 -0
- package/assets/agents/developers/remix-fullstack-dev.md +69 -0
- package/assets/agents/utilities/api-designer.md +76 -0
- package/assets/agents/utilities/clean-architect.md +83 -0
- package/assets/agents/utilities/code-reviewer.md +76 -0
- package/assets/agents/utilities/db-designer.md +68 -0
- package/assets/agents/utilities/devops.md +71 -0
- package/assets/agents/utilities/docs-writer.md +75 -0
- package/assets/agents/utilities/perf-optimizer.md +87 -0
- package/assets/agents/utilities/refactor.md +173 -0
- package/assets/agents/utilities/security-audit.md +203 -0
- package/assets/agents/utilities/test-writer.md +139 -0
- package/assets/architecture/clean-architecture.md +143 -0
- package/assets/architecture/flutter-mobile.md +304 -0
- package/assets/architecture/go-backend.md +217 -0
- package/assets/architecture/laravel-backend.md +303 -0
- package/assets/architecture/monorepo.md +162 -0
- package/assets/architecture/react-frontend.md +268 -0
- package/assets/architecture/remix-fullstack.md +272 -0
- package/assets/commands/bootstrap.md +98 -0
- package/assets/commands/brainstorm.md +440 -0
- package/assets/skills/feature-workflow/SKILL.md +298 -0
- package/assets/skills/hotfix-workflow/SKILL.md +368 -0
- package/assets/templates/flutter/CLAUDE.md +454 -0
- package/assets/templates/go-gin/CLAUDE.md +244 -0
- package/assets/templates/monorepo/CLAUDE.md +362 -0
- package/assets/templates/react-vite/CLAUDE.md +304 -0
- package/assets/templates/remix/CLAUDE.md +304 -0
- package/bin/cli.js +76 -0
- package/dist/commands/disable.d.ts +3 -0
- package/dist/commands/disable.d.ts.map +1 -0
- package/dist/commands/disable.js +188 -0
- package/dist/commands/disable.js.map +1 -0
- package/dist/commands/enable.d.ts +3 -0
- package/dist/commands/enable.d.ts.map +1 -0
- package/dist/commands/enable.js +191 -0
- package/dist/commands/enable.js.map +1 -0
- package/dist/commands/install.d.ts +3 -0
- package/dist/commands/install.d.ts.map +1 -0
- package/dist/commands/install.js +290 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/list.d.ts +3 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +75 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/postinstall.d.ts +2 -0
- package/dist/commands/postinstall.d.ts.map +1 -0
- package/dist/commands/postinstall.js +25 -0
- package/dist/commands/postinstall.js.map +1 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +118 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/uninstall.d.ts +3 -0
- package/dist/commands/uninstall.d.ts.map +1 -0
- package/dist/commands/uninstall.js +178 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +47 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/config.d.ts +13 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +95 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/symlink.d.ts +24 -0
- package/dist/utils/symlink.d.ts.map +1 -0
- package/dist/utils/symlink.js +313 -0
- package/dist/utils/symlink.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
# CLAUDE.md - Monorepo Template (Frontend + Backend)
|
|
2
|
+
|
|
3
|
+
## Project Overview
|
|
4
|
+
|
|
5
|
+
Monorepo containing:
|
|
6
|
+
- **Frontend**: React + TypeScript + Vite + Tailwind CSS
|
|
7
|
+
- **Backend**: Go + Gin + GORM (or Node.js + Express alternative)
|
|
8
|
+
- **Shared**: Common types and utilities
|
|
9
|
+
|
|
10
|
+
## Quick Start
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# Backend (Terminal 1)
|
|
14
|
+
cd apps/backend && go run cmd/api/main.go # Port 8080
|
|
15
|
+
|
|
16
|
+
# Frontend (Terminal 2)
|
|
17
|
+
cd apps/frontend && pnpm dev # Port 5173
|
|
18
|
+
|
|
19
|
+
# Or run both with task runner
|
|
20
|
+
pnpm dev:all
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Project Structure
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
{project_name}/
|
|
27
|
+
├── apps/
|
|
28
|
+
│ ├── backend/ # Backend application
|
|
29
|
+
│ │ ├── cmd/
|
|
30
|
+
│ │ │ └── api/
|
|
31
|
+
│ │ │ └── main.go
|
|
32
|
+
│ │ ├── internal/
|
|
33
|
+
│ │ │ ├── config/
|
|
34
|
+
│ │ │ ├── middleware/
|
|
35
|
+
│ │ │ └── modules/
|
|
36
|
+
│ │ │ ├── router/
|
|
37
|
+
│ │ │ └── {module}/
|
|
38
|
+
│ │ │ ├── controllers/
|
|
39
|
+
│ │ │ ├── models/
|
|
40
|
+
│ │ │ ├── usecases/
|
|
41
|
+
│ │ │ └── init.go
|
|
42
|
+
│ │ ├── pkg/
|
|
43
|
+
│ │ │ ├── database/
|
|
44
|
+
│ │ │ └── response/
|
|
45
|
+
│ │ ├── config.yaml
|
|
46
|
+
│ │ └── go.mod
|
|
47
|
+
│ │
|
|
48
|
+
│ └── frontend/ # Frontend application
|
|
49
|
+
│ ├── src/
|
|
50
|
+
│ │ ├── app/
|
|
51
|
+
│ │ │ ├── config/
|
|
52
|
+
│ │ │ ├── layouts/
|
|
53
|
+
│ │ │ ├── modules/
|
|
54
|
+
│ │ │ │ └── {module}/
|
|
55
|
+
│ │ │ │ ├── components/
|
|
56
|
+
│ │ │ │ ├── models/
|
|
57
|
+
│ │ │ │ ├── viewmodels/
|
|
58
|
+
│ │ │ │ └── pages/
|
|
59
|
+
│ │ │ ├── shared/
|
|
60
|
+
│ │ │ └── router.tsx
|
|
61
|
+
│ │ ├── components/ui/
|
|
62
|
+
│ │ ├── lib/
|
|
63
|
+
│ │ │ └── api-client.ts
|
|
64
|
+
│ │ └── main.tsx
|
|
65
|
+
│ ├── package.json
|
|
66
|
+
│ └── vite.config.ts
|
|
67
|
+
│
|
|
68
|
+
├── packages/ # Shared packages (optional)
|
|
69
|
+
│ └── shared-types/
|
|
70
|
+
│ └── index.ts
|
|
71
|
+
│
|
|
72
|
+
├── scripts/ # Development scripts
|
|
73
|
+
├── docker-compose.yml
|
|
74
|
+
├── package.json # Root package.json for scripts
|
|
75
|
+
└── README.md
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Backend Module Structure
|
|
79
|
+
|
|
80
|
+
### Module Init Pattern
|
|
81
|
+
|
|
82
|
+
```go
|
|
83
|
+
// internal/modules/{module}/init.go
|
|
84
|
+
package module
|
|
85
|
+
|
|
86
|
+
import (
|
|
87
|
+
"github.com/gin-gonic/gin"
|
|
88
|
+
"gorm.io/gorm"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
func Init(r *gin.Engine, db *gorm.DB) {
|
|
92
|
+
repo := NewRepository(db)
|
|
93
|
+
uc := NewUseCase(repo)
|
|
94
|
+
ctrl := NewController(uc)
|
|
95
|
+
|
|
96
|
+
group := r.Group("/api/{module}")
|
|
97
|
+
{
|
|
98
|
+
group.GET("/", ctrl.List)
|
|
99
|
+
group.GET("/:id", ctrl.Get)
|
|
100
|
+
group.POST("/", ctrl.Create)
|
|
101
|
+
group.PUT("/:id", ctrl.Update)
|
|
102
|
+
group.DELETE("/:id", ctrl.Delete)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### GORM Model Pattern
|
|
108
|
+
|
|
109
|
+
```go
|
|
110
|
+
// models/entity.go
|
|
111
|
+
type Entity struct {
|
|
112
|
+
ID string `gorm:"type:char(36);primaryKey" json:"id"`
|
|
113
|
+
Name string `gorm:"type:varchar(255);not null" json:"name"`
|
|
114
|
+
Status string `gorm:"type:varchar(50);default:'active'" json:"status"`
|
|
115
|
+
CreatedAt time.Time `json:"created_at"`
|
|
116
|
+
UpdatedAt time.Time `json:"updated_at"`
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Response Format
|
|
121
|
+
|
|
122
|
+
```go
|
|
123
|
+
// Success
|
|
124
|
+
c.JSON(http.StatusOK, gin.H{"data": result})
|
|
125
|
+
|
|
126
|
+
// Paginated
|
|
127
|
+
c.JSON(http.StatusOK, gin.H{
|
|
128
|
+
"data": items,
|
|
129
|
+
"total": total,
|
|
130
|
+
"page": page,
|
|
131
|
+
"limit": limit,
|
|
132
|
+
"total_pages": totalPages,
|
|
133
|
+
})
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Frontend Module Structure
|
|
137
|
+
|
|
138
|
+
### MVVM Pattern
|
|
139
|
+
|
|
140
|
+
**Model** - Data types and API functions:
|
|
141
|
+
```typescript
|
|
142
|
+
// models/entity.model.ts
|
|
143
|
+
export interface Entity {
|
|
144
|
+
id: string;
|
|
145
|
+
name: string;
|
|
146
|
+
status: 'active' | 'inactive';
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export async function getEntities(): Promise<Entity[]> {
|
|
150
|
+
return api.get<Entity[]>('/entities');
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**ViewModel** - Business logic:
|
|
155
|
+
```typescript
|
|
156
|
+
// viewmodels/use-entities.ts
|
|
157
|
+
export function useEntitiesViewModel() {
|
|
158
|
+
const [data, setData] = useState<Entity[]>([]);
|
|
159
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
160
|
+
|
|
161
|
+
const loadData = useCallback(async () => {
|
|
162
|
+
setIsLoading(true);
|
|
163
|
+
const entities = await getEntities();
|
|
164
|
+
setData(entities);
|
|
165
|
+
setIsLoading(false);
|
|
166
|
+
}, []);
|
|
167
|
+
|
|
168
|
+
return { data, isLoading, loadData };
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**View** - Pure UI:
|
|
173
|
+
```typescript
|
|
174
|
+
// components/entity-table.tsx
|
|
175
|
+
interface Props {
|
|
176
|
+
data: Entity[];
|
|
177
|
+
onEdit: (entity: Entity) => void;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function EntityTable({ data, onEdit }: Props) {
|
|
181
|
+
return <Table>...</Table>;
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### API Client
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
// lib/api-client.ts
|
|
189
|
+
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL;
|
|
190
|
+
|
|
191
|
+
export const api = {
|
|
192
|
+
async get<T>(endpoint: string): Promise<T> {
|
|
193
|
+
const response = await fetch(`${API_BASE_URL}${endpoint}`);
|
|
194
|
+
const json = await response.json();
|
|
195
|
+
return json.data ?? json;
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
async post<T>(endpoint: string, data: unknown): Promise<T> {
|
|
199
|
+
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
|
|
200
|
+
method: 'POST',
|
|
201
|
+
headers: { 'Content-Type': 'application/json' },
|
|
202
|
+
body: JSON.stringify(data),
|
|
203
|
+
});
|
|
204
|
+
return response.json();
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Adding New Module
|
|
210
|
+
|
|
211
|
+
### Backend Module
|
|
212
|
+
|
|
213
|
+
1. Create directory structure:
|
|
214
|
+
```bash
|
|
215
|
+
mkdir -p apps/backend/internal/modules/{module}/{controllers,models,usecases}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
2. Create model (`models/entity.go`)
|
|
219
|
+
3. Create usecase (`usecases/usecase.go`)
|
|
220
|
+
4. Create controller (`controllers/controller.go`)
|
|
221
|
+
5. Create init.go and register in router
|
|
222
|
+
|
|
223
|
+
### Frontend Module
|
|
224
|
+
|
|
225
|
+
1. Create directory structure:
|
|
226
|
+
```bash
|
|
227
|
+
mkdir -p apps/frontend/src/app/modules/{module}/{models,viewmodels,components,pages}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
2. Create model (`models/entity.model.ts`)
|
|
231
|
+
3. Create viewmodel (`viewmodels/use-entity.ts`)
|
|
232
|
+
4. Create components and pages
|
|
233
|
+
5. Add route in router.tsx
|
|
234
|
+
6. Add menu item in config/menu.ts
|
|
235
|
+
|
|
236
|
+
## API Endpoints
|
|
237
|
+
|
|
238
|
+
| Method | Path | Description |
|
|
239
|
+
|--------|------|-------------|
|
|
240
|
+
| GET | /api/{resource}/ | List all |
|
|
241
|
+
| GET | /api/{resource}/:id | Get by ID |
|
|
242
|
+
| POST | /api/{resource}/ | Create |
|
|
243
|
+
| PUT | /api/{resource}/:id | Update |
|
|
244
|
+
| DELETE | /api/{resource}/:id | Delete |
|
|
245
|
+
|
|
246
|
+
### Query Parameters
|
|
247
|
+
- `page` - Page number (default: 1)
|
|
248
|
+
- `limit` - Items per page (default: 10)
|
|
249
|
+
- `search` - Search term
|
|
250
|
+
- `sort_by` - Sort field
|
|
251
|
+
- `order` - Sort order (asc/desc)
|
|
252
|
+
|
|
253
|
+
## Configuration
|
|
254
|
+
|
|
255
|
+
### Backend (config.yaml)
|
|
256
|
+
```yaml
|
|
257
|
+
server:
|
|
258
|
+
port: 8080
|
|
259
|
+
mode: debug
|
|
260
|
+
|
|
261
|
+
database:
|
|
262
|
+
type: mysql
|
|
263
|
+
host: localhost
|
|
264
|
+
port: 3306
|
|
265
|
+
user: root
|
|
266
|
+
password: ""
|
|
267
|
+
dbname: {project_name}
|
|
268
|
+
|
|
269
|
+
redis:
|
|
270
|
+
address: localhost:6379
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Frontend (.env)
|
|
274
|
+
```
|
|
275
|
+
VITE_API_BASE_URL=http://localhost:8080
|
|
276
|
+
VITE_APP_NAME={project_name}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Docker Compose
|
|
280
|
+
```yaml
|
|
281
|
+
version: '3.8'
|
|
282
|
+
services:
|
|
283
|
+
backend:
|
|
284
|
+
build: ./apps/backend
|
|
285
|
+
ports:
|
|
286
|
+
- "8080:8080"
|
|
287
|
+
depends_on:
|
|
288
|
+
- db
|
|
289
|
+
- redis
|
|
290
|
+
environment:
|
|
291
|
+
- DATABASE_URL=mysql://root:password@db:3306/{project_name}
|
|
292
|
+
|
|
293
|
+
frontend:
|
|
294
|
+
build: ./apps/frontend
|
|
295
|
+
ports:
|
|
296
|
+
- "5173:80"
|
|
297
|
+
|
|
298
|
+
db:
|
|
299
|
+
image: mysql:8
|
|
300
|
+
environment:
|
|
301
|
+
MYSQL_ROOT_PASSWORD: password
|
|
302
|
+
MYSQL_DATABASE: {project_name}
|
|
303
|
+
volumes:
|
|
304
|
+
- db_data:/var/lib/mysql
|
|
305
|
+
|
|
306
|
+
redis:
|
|
307
|
+
image: redis:7-alpine
|
|
308
|
+
|
|
309
|
+
volumes:
|
|
310
|
+
db_data:
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## Development Scripts
|
|
314
|
+
|
|
315
|
+
### Root package.json
|
|
316
|
+
```json
|
|
317
|
+
{
|
|
318
|
+
"scripts": {
|
|
319
|
+
"dev:backend": "cd apps/backend && go run cmd/api/main.go",
|
|
320
|
+
"dev:frontend": "cd apps/frontend && pnpm dev",
|
|
321
|
+
"dev:all": "concurrently \"pnpm dev:backend\" \"pnpm dev:frontend\"",
|
|
322
|
+
"build:backend": "cd apps/backend && go build -o bin/api cmd/api/main.go",
|
|
323
|
+
"build:frontend": "cd apps/frontend && pnpm build",
|
|
324
|
+
"test:backend": "cd apps/backend && go test ./...",
|
|
325
|
+
"test:frontend": "cd apps/frontend && pnpm test"
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
## Conventions
|
|
331
|
+
|
|
332
|
+
### Backend
|
|
333
|
+
- File naming: `snake_case.go`
|
|
334
|
+
- UUID primary keys
|
|
335
|
+
- Nullable fields use pointers
|
|
336
|
+
- No soft deletes
|
|
337
|
+
|
|
338
|
+
### Frontend
|
|
339
|
+
- File naming: `kebab-case.tsx`
|
|
340
|
+
- Hook naming: `use-*.ts`
|
|
341
|
+
- No direct API calls in components
|
|
342
|
+
- Zod schemas in models folder
|
|
343
|
+
|
|
344
|
+
## Shared Types (Optional)
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
// packages/shared-types/index.ts
|
|
348
|
+
export interface PaginatedResponse<T> {
|
|
349
|
+
data: T[];
|
|
350
|
+
total: number;
|
|
351
|
+
page: number;
|
|
352
|
+
limit: number;
|
|
353
|
+
total_pages: number;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export type Status = 'active' | 'inactive' | 'pending';
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Use in frontend:
|
|
360
|
+
```typescript
|
|
361
|
+
import type { PaginatedResponse } from '@{project_name}/shared-types';
|
|
362
|
+
```
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
# CLAUDE.md - React + Vite + TypeScript Frontend Template
|
|
2
|
+
|
|
3
|
+
## Project Overview
|
|
4
|
+
|
|
5
|
+
Frontend application built with:
|
|
6
|
+
- **React 19** - UI library
|
|
7
|
+
- **TypeScript** - Type safety
|
|
8
|
+
- **Vite 6** - Build tool
|
|
9
|
+
- **Tailwind CSS 4** - Utility-first CSS
|
|
10
|
+
- **shadcn/ui** - UI component library
|
|
11
|
+
- **React Router 7** - Client-side routing
|
|
12
|
+
- **TanStack Query** - Server state management
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Install dependencies
|
|
18
|
+
pnpm install
|
|
19
|
+
|
|
20
|
+
# Run development server
|
|
21
|
+
pnpm dev
|
|
22
|
+
|
|
23
|
+
# Build for production
|
|
24
|
+
pnpm build
|
|
25
|
+
|
|
26
|
+
# Preview production build
|
|
27
|
+
pnpm preview
|
|
28
|
+
|
|
29
|
+
# Run linter
|
|
30
|
+
pnpm lint
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Project Structure
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
{project_name}/
|
|
37
|
+
├── src/
|
|
38
|
+
│ ├── app/
|
|
39
|
+
│ │ ├── config/ # App configuration
|
|
40
|
+
│ │ │ ├── routes.ts # Route definitions
|
|
41
|
+
│ │ │ └── menu.ts # Navigation menu
|
|
42
|
+
│ │ ├── layouts/ # Layout components
|
|
43
|
+
│ │ │ ├── root-layout.tsx
|
|
44
|
+
│ │ │ └── sidebar-layout.tsx
|
|
45
|
+
│ │ ├── modules/ # Feature modules (MVVM)
|
|
46
|
+
│ │ │ └── {module}/
|
|
47
|
+
│ │ │ ├── components/
|
|
48
|
+
│ │ │ ├── models/
|
|
49
|
+
│ │ │ ├── viewmodels/
|
|
50
|
+
│ │ │ └── pages/
|
|
51
|
+
│ │ ├── shared/ # Shared utilities
|
|
52
|
+
│ │ │ ├── components/
|
|
53
|
+
│ │ │ ├── contexts/
|
|
54
|
+
│ │ │ └── hooks/
|
|
55
|
+
│ │ └── router.tsx # Router setup
|
|
56
|
+
│ ├── components/ui/ # shadcn/ui components
|
|
57
|
+
│ ├── lib/
|
|
58
|
+
│ │ ├── api-client.ts # API client
|
|
59
|
+
│ │ └── utils.ts # Utility functions
|
|
60
|
+
│ ├── main.tsx # App entry
|
|
61
|
+
│ └── index.css # Global styles
|
|
62
|
+
├── public/
|
|
63
|
+
├── .env
|
|
64
|
+
├── vite.config.ts
|
|
65
|
+
├── tailwind.config.ts
|
|
66
|
+
├── tsconfig.json
|
|
67
|
+
└── package.json
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Key Patterns and Conventions
|
|
71
|
+
|
|
72
|
+
### File Naming
|
|
73
|
+
- Use `kebab-case.tsx` for components
|
|
74
|
+
- Use `use-*.ts` for hooks
|
|
75
|
+
- Use `*.model.ts` for models
|
|
76
|
+
- Use `*.schema.ts` for Zod schemas
|
|
77
|
+
|
|
78
|
+
### MVVM Pattern
|
|
79
|
+
|
|
80
|
+
**Model** - Data types and API functions:
|
|
81
|
+
```typescript
|
|
82
|
+
// models/entity.model.ts
|
|
83
|
+
export interface Entity {
|
|
84
|
+
id: string;
|
|
85
|
+
name: string;
|
|
86
|
+
status: 'active' | 'inactive';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export async function getEntities(): Promise<Entity[]> {
|
|
90
|
+
return api.get<Entity[]>('/entities');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export async function createEntity(data: CreateEntityDTO): Promise<Entity> {
|
|
94
|
+
return api.post<Entity>('/entities', data);
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**ViewModel** - Business logic and state:
|
|
99
|
+
```typescript
|
|
100
|
+
// viewmodels/use-entities.ts
|
|
101
|
+
export function useEntitiesViewModel() {
|
|
102
|
+
const [data, setData] = useState<Entity[]>([]);
|
|
103
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
104
|
+
const [error, setError] = useState<string | null>(null);
|
|
105
|
+
|
|
106
|
+
const loadData = useCallback(async () => {
|
|
107
|
+
setIsLoading(true);
|
|
108
|
+
try {
|
|
109
|
+
const entities = await getEntities();
|
|
110
|
+
setData(entities);
|
|
111
|
+
} catch (err) {
|
|
112
|
+
setError('Failed to load data');
|
|
113
|
+
} finally {
|
|
114
|
+
setIsLoading(false);
|
|
115
|
+
}
|
|
116
|
+
}, []);
|
|
117
|
+
|
|
118
|
+
return { data, isLoading, error, loadData };
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**View** - Pure UI components:
|
|
123
|
+
```typescript
|
|
124
|
+
// components/entity-table.tsx
|
|
125
|
+
interface EntityTableProps {
|
|
126
|
+
data: Entity[];
|
|
127
|
+
onEdit: (entity: Entity) => void;
|
|
128
|
+
onDelete: (entity: Entity) => void;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function EntityTable({ data, onEdit, onDelete }: EntityTableProps) {
|
|
132
|
+
return (
|
|
133
|
+
<Table>
|
|
134
|
+
{data.map((entity) => (
|
|
135
|
+
<TableRow key={entity.id}>
|
|
136
|
+
<TableCell>{entity.name}</TableCell>
|
|
137
|
+
<TableCell>
|
|
138
|
+
<Button onClick={() => onEdit(entity)}>Edit</Button>
|
|
139
|
+
</TableCell>
|
|
140
|
+
</TableRow>
|
|
141
|
+
))}
|
|
142
|
+
</Table>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### API Client
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
// lib/api-client.ts
|
|
151
|
+
import { getAuth } from '@/app/shared/contexts/auth-context';
|
|
152
|
+
|
|
153
|
+
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL;
|
|
154
|
+
|
|
155
|
+
export const api = {
|
|
156
|
+
async get<T>(endpoint: string, options?: { requireAuth?: boolean }): Promise<T> {
|
|
157
|
+
const headers: HeadersInit = { 'Content-Type': 'application/json' };
|
|
158
|
+
if (options?.requireAuth !== false) {
|
|
159
|
+
const token = await getAuth().currentUser?.getIdToken();
|
|
160
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
161
|
+
}
|
|
162
|
+
const response = await fetch(`${API_BASE_URL}${endpoint}`, { headers });
|
|
163
|
+
return response.json();
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
async post<T>(endpoint: string, data: unknown): Promise<T> {
|
|
167
|
+
// Similar to get with method: 'POST' and body
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Page Component Pattern
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
// pages/entities-page.tsx
|
|
176
|
+
export default function EntitiesPage() {
|
|
177
|
+
const vm = useEntitiesPageViewModel();
|
|
178
|
+
|
|
179
|
+
useEffect(() => {
|
|
180
|
+
vm.loadData();
|
|
181
|
+
}, [vm.loadData]);
|
|
182
|
+
|
|
183
|
+
return (
|
|
184
|
+
<div className="container mx-auto p-6">
|
|
185
|
+
<div className="flex justify-between items-center mb-6">
|
|
186
|
+
<h1 className="text-2xl font-bold">Entities</h1>
|
|
187
|
+
<Button onClick={() => vm.setIsFormOpen(true)}>Add New</Button>
|
|
188
|
+
</div>
|
|
189
|
+
|
|
190
|
+
<EntityTable
|
|
191
|
+
data={vm.data}
|
|
192
|
+
isLoading={vm.isLoading}
|
|
193
|
+
onEdit={vm.handleEdit}
|
|
194
|
+
onDelete={vm.handleDelete}
|
|
195
|
+
/>
|
|
196
|
+
|
|
197
|
+
<EntityFormDialog
|
|
198
|
+
open={vm.isFormOpen}
|
|
199
|
+
onOpenChange={vm.setIsFormOpen}
|
|
200
|
+
entity={vm.editingEntity}
|
|
201
|
+
onSubmit={vm.handleSubmit}
|
|
202
|
+
/>
|
|
203
|
+
</div>
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Adding New Module
|
|
209
|
+
|
|
210
|
+
1. Create module directory structure:
|
|
211
|
+
```bash
|
|
212
|
+
mkdir -p src/app/modules/{module}/{models,viewmodels,components,pages}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
2. Create model (`models/entity.model.ts`)
|
|
216
|
+
3. Create viewmodel (`viewmodels/use-entity.ts`)
|
|
217
|
+
4. Create components (`components/*.tsx`)
|
|
218
|
+
5. Create page (`pages/entity-page.tsx`)
|
|
219
|
+
6. Add route in `src/app/router.tsx`
|
|
220
|
+
7. Add menu item in `src/app/config/menu.ts`
|
|
221
|
+
|
|
222
|
+
## Component Guidelines
|
|
223
|
+
|
|
224
|
+
### Form with Zod Validation
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
// models/entity.schema.ts
|
|
228
|
+
import { z } from 'zod';
|
|
229
|
+
|
|
230
|
+
export const entitySchema = z.object({
|
|
231
|
+
name: z.string().min(1, 'Name is required'),
|
|
232
|
+
email: z.string().email('Invalid email'),
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
export type EntityFormData = z.infer<typeof entitySchema>;
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
// components/entity-form.tsx
|
|
240
|
+
import { useForm } from 'react-hook-form';
|
|
241
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
242
|
+
|
|
243
|
+
export function EntityForm({ onSubmit }: Props) {
|
|
244
|
+
const form = useForm<EntityFormData>({
|
|
245
|
+
resolver: zodResolver(entitySchema),
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
return (
|
|
249
|
+
<Form {...form}>
|
|
250
|
+
<form onSubmit={form.handleSubmit(onSubmit)}>
|
|
251
|
+
<FormField name="name" control={form.control} render={...} />
|
|
252
|
+
</form>
|
|
253
|
+
</Form>
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Loading States
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
// Use skeleton for initial load
|
|
262
|
+
{isLoading && <TableSkeleton rows={5} />}
|
|
263
|
+
|
|
264
|
+
// Use overlay for data refresh
|
|
265
|
+
{isRefreshing && (
|
|
266
|
+
<div className="absolute inset-0 bg-background/50 flex items-center justify-center">
|
|
267
|
+
<Spinner />
|
|
268
|
+
</div>
|
|
269
|
+
)}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## Configuration
|
|
273
|
+
|
|
274
|
+
### Environment Variables (.env)
|
|
275
|
+
```
|
|
276
|
+
VITE_API_BASE_URL=http://localhost:8080
|
|
277
|
+
VITE_APP_NAME={project_name}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Tailwind Config
|
|
281
|
+
```typescript
|
|
282
|
+
// tailwind.config.ts
|
|
283
|
+
export default {
|
|
284
|
+
content: ['./src/**/*.{ts,tsx}'],
|
|
285
|
+
theme: {
|
|
286
|
+
extend: {
|
|
287
|
+
// Custom theme extensions
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
plugins: [],
|
|
291
|
+
};
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Path Aliases (tsconfig.json)
|
|
295
|
+
```json
|
|
296
|
+
{
|
|
297
|
+
"compilerOptions": {
|
|
298
|
+
"baseUrl": ".",
|
|
299
|
+
"paths": {
|
|
300
|
+
"@/*": ["./src/*"]
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
```
|