create-ern-boilerplate 0.0.21 → 0.0.22
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 +1 -1
- package/templates/default_draft/README.md +3 -0
- package/templates/{simple/app/(admin)/profile.tsx → default_draft/app/(admin)/draft_profile.tsx} +5 -5
- package/templates/{simple/app/(admin)/user-management.tsx → default_draft/app/(admin)/draft_userManagement.tsx} +4 -4
- package/templates/{simple → default_draft}/app/(auth)/_layout.tsx +2 -2
- package/templates/{simple/app/(auth)/login.tsx → default_draft/app/(auth)/draft_login.tsx} +5 -5
- package/templates/{simple/app/(auth)/register.tsx → default_draft/app/(auth)/draft_register.tsx} +4 -4
- package/templates/{simple → default_draft}/app/(protected)/_layout.tsx +5 -5
- package/templates/{simple/app/(protected)/home.tsx → default_draft/app/(protected)/draft_home.tsx} +4 -4
- package/templates/{simple/app/(protected)/settings.tsx → default_draft/app/(protected)/draft_settings.tsx} +6 -6
- package/templates/{simple → default_draft}/app/_layout.tsx +2 -2
- package/templates/{simple → default_draft}/src/components/auth/ProtectedRoute.tsx +2 -2
- package/templates/{simple → default_draft}/src/components/common/Card.tsx +1 -1
- package/templates/{simple → default_draft}/src/components/common/Input.tsx +1 -1
- package/templates/{simple → default_draft}/src/components/common/Loading.tsx +1 -1
- package/templates/{simple → default_draft}/src/components/users/FilterModal.tsx +1 -1
- package/templates/{simple → default_draft}/src/components/users/UserCard.tsx +1 -1
- package/templates/{simple → default_draft}/src/components/users/UserFormModal.tsx +1 -1
- package/templates/{simple/src/contexts/AuthContext.tsx → default_draft/src/contexts/draft_AuthContext.tsx} +6 -6
- package/templates/{simple/src/contexts/ThemeContext.tsx → default_draft/src/contexts/draft_ThemeContext.tsx} +2 -2
- package/templates/{simple/src/hooks/useAuth.ts → default_draft/src/hooks/draft_useAuth.ts} +1 -1
- package/templates/{simple/src/hooks/useTheme.ts → default_draft/src/hooks/draft_useTheme.ts} +1 -1
- package/templates/{simple/src/hooks/useUsers.ts → default_draft/src/hooks/draft_useUsers.ts} +2 -2
- package/templates/{simple/src/services/api.ts → default_draft/src/services/draft_api.ts} +3 -3
- package/templates/{simple/src/services/authService.ts → default_draft/src/services/draft_authService.ts} +3 -3
- package/templates/{simple/src/services/productService.ts → default_draft/src/services/draft_productService.ts} +3 -3
- package/templates/{simple/src/services/userService.ts → default_draft/src/services/draft_userService.ts} +3 -3
- package/templates/{simple/src/services/mockApi/auth.mock.ts → default_draft/src/services/mockApi/draft_auth.mock.ts} +1 -1
- package/templates/{simple/src/services/mockApi/categories.mock.ts → default_draft/src/services/mockApi/draft_categories.mock.ts} +1 -1
- package/templates/{simple/src/services/mockApi/products.mock.ts → default_draft/src/services/mockApi/draft_products.mock.ts} +1 -1
- package/templates/{simple/src/services/mockApi/users.mock.ts → default_draft/src/services/mockApi/draft_users.mock.ts} +2 -2
- package/templates/{simple → default_draft}/src/services/mockApi/index.ts +4 -4
- package/templates/{simple/src/utils/validation.ts → default_draft/src/utils/draft_validation.ts} +1 -1
- package/templates/simple/PROJECT_CONTEXT.MD +0 -663
- package/templates/simple/README.md +0 -274
- /package/templates/{simple → default_draft}/app.json +0 -0
- /package/templates/{simple → default_draft}/babel.config.js +0 -0
- /package/templates/{simple → default_draft}/global.css +0 -0
- /package/templates/{simple → default_draft}/metro.config.js +0 -0
- /package/templates/{simple → default_draft}/nativewind-env.d.ts +0 -0
- /package/templates/{simple → default_draft}/package.json +0 -0
- /package/templates/{simple → default_draft}/server/db.json +0 -0
- /package/templates/{simple → default_draft}/src/assets/images/adaptive-icon.png +0 -0
- /package/templates/{simple → default_draft}/src/assets/images/favicon.png +0 -0
- /package/templates/{simple → default_draft}/src/assets/images/icon.png +0 -0
- /package/templates/{simple → default_draft}/src/assets/images/splash-icon.png +0 -0
- /package/templates/{simple → default_draft}/src/components/common/Button.tsx +0 -0
- /package/templates/{simple → default_draft}/src/components/config/ToastConfig.tsx +0 -0
- /package/templates/{simple/src/components/header/AppHeader.tsx → default_draft/src/components/header/draft_AppHeader.tsx} +0 -0
- /package/templates/{simple → default_draft}/src/components/shared/ConfirmDialog.tsx +0 -0
- /package/templates/{simple → default_draft}/src/components/shared/FormInput.tsx +0 -0
- /package/templates/{simple → default_draft}/src/components/shared/LucideIcon.tsx +0 -0
- /package/templates/{simple → default_draft}/src/components/shared/RoleSelector.tsx +0 -0
- /package/templates/{simple → default_draft}/src/components/users/EmptyState.tsx +0 -0
- /package/templates/{simple → default_draft}/src/components/users/ErrorState.tsx +0 -0
- /package/templates/{simple/src/theme/colors.ts → default_draft/src/theme/draft_colors.ts} +0 -0
- /package/templates/{simple/src/types/api.types.ts → default_draft/src/types/draft_api.types.ts} +0 -0
- /package/templates/{simple/src/types/auth.types.ts → default_draft/src/types/draft_auth.types.ts} +0 -0
- /package/templates/{simple/src/types/product.types.ts → default_draft/src/types/draft_product.types.ts} +0 -0
- /package/templates/{simple/src/types/user.types.ts → default_draft/src/types/draft_user.types.ts} +0 -0
- /package/templates/{simple/src/utils/constants.ts → default_draft/src/utils/draft_constants.ts} +0 -0
- /package/templates/{simple/src/utils/storage.ts → default_draft/src/utils/draft_storage.ts} +0 -0
- /package/templates/{simple → default_draft}/tailwind.config.js +0 -0
- /package/templates/{simple → default_draft}/tsconfig.json +0 -0
|
@@ -1,663 +0,0 @@
|
|
|
1
|
-
# Project Context for AI Agents
|
|
2
|
-
|
|
3
|
-
> **Purpose**: This document helps AI coding agents understand the project architecture, conventions, and patterns to generate consistent, high-quality code.
|
|
4
|
-
|
|
5
|
-
## 🏗️ Tech Stack
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
- Framework: React Native + Expo SDK 54
|
|
9
|
-
- Routing: Expo Router (file-based)
|
|
10
|
-
- Language: TypeScript (strict mode)
|
|
11
|
-
- Styling: NativeWind (Tailwind CSS for React Native)
|
|
12
|
-
- State: React Context API
|
|
13
|
-
- API: Mock API (development) / Real API (production)
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## 📁 Project Structure
|
|
17
|
-
|
|
18
|
-
### Core Directories
|
|
19
|
-
|
|
20
|
-
```
|
|
21
|
-
app/ # File-based routing (Expo Router)
|
|
22
|
-
├── (auth)/ # Public authentication routes
|
|
23
|
-
├── (protected)/ # Routes requiring authentication
|
|
24
|
-
├── (admin)/ # Routes requiring admin role
|
|
25
|
-
└── _layout.tsx # Root layout wrapper
|
|
26
|
-
|
|
27
|
-
src/
|
|
28
|
-
├── components/ # Reusable UI components
|
|
29
|
-
├── contexts/ # React Context providers
|
|
30
|
-
├── hooks/ # Custom React hooks
|
|
31
|
-
├── services/ # API and business logic
|
|
32
|
-
├── types/ # TypeScript type definitions
|
|
33
|
-
├── theme/ # Design tokens and theme config
|
|
34
|
-
└── utils/ # Helper functions and utilities
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
### Detailed Structure
|
|
38
|
-
|
|
39
|
-
#### **Components** (`src/components/`)
|
|
40
|
-
- `auth/` - Authentication-specific components (ProtectedRoute)
|
|
41
|
-
- `common/` - Generic reusable components (Button, Input, Card)
|
|
42
|
-
- `shared/` - Shared business components (FormInput, ConfirmDialog)
|
|
43
|
-
- `users/` - User management components
|
|
44
|
-
- `header/` - Navigation and header components
|
|
45
|
-
- `config/` - Configuration components (ToastConfig)
|
|
46
|
-
|
|
47
|
-
#### **Services** (`src/services/`)
|
|
48
|
-
- `*.service.ts` - Real API service implementations
|
|
49
|
-
- `mockApi/*.mock.ts` - Mock data for development
|
|
50
|
-
- `api.ts` - Base API client configuration
|
|
51
|
-
|
|
52
|
-
#### **Types** (`src/types/`)
|
|
53
|
-
- `*.types.ts` - TypeScript interfaces and types
|
|
54
|
-
- Group by domain: `auth`, `user`, `product`, `api`
|
|
55
|
-
|
|
56
|
-
## 🎯 Architecture Patterns
|
|
57
|
-
|
|
58
|
-
### 1. Data Flow
|
|
59
|
-
```
|
|
60
|
-
Component → Custom Hook → Service → API/Mock
|
|
61
|
-
↓
|
|
62
|
-
Context (if global state needed)
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### 2. Authentication Flow
|
|
66
|
-
```
|
|
67
|
-
1. User logs in via authService.login()
|
|
68
|
-
2. Token stored in AsyncStorage
|
|
69
|
-
3. AuthContext provides user state
|
|
70
|
-
4. ProtectedRoute guards routes in (protected)/ and (admin)/
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### 3. API Strategy
|
|
74
|
-
- **Development**: Uses `mockApi/*.mock.ts` files
|
|
75
|
-
- **Production**: Uses real API via `*.service.ts`
|
|
76
|
-
- Switch via environment variable or constants
|
|
77
|
-
|
|
78
|
-
## 📝 Naming Conventions
|
|
79
|
-
|
|
80
|
-
### Files
|
|
81
|
-
- Components: `PascalCase.tsx` (e.g., `UserCard.tsx`)
|
|
82
|
-
- Services: `camelCase.service.ts` (e.g., `auth.service.ts`)
|
|
83
|
-
- Hooks: `useCamelCase.ts` (e.g., `useAuth.ts`)
|
|
84
|
-
- Types: `camelCase.types.ts` (e.g., `user.types.ts`)
|
|
85
|
-
- Utils: `camelCase.ts` (e.g., `validation.ts`)
|
|
86
|
-
|
|
87
|
-
### Code
|
|
88
|
-
- Components: `PascalCase` (e.g., `UserCard`)
|
|
89
|
-
- Functions: `camelCase` (e.g., `getUserById`)
|
|
90
|
-
- Constants: `UPPER_SNAKE_CASE` (e.g., `API_BASE_URL`)
|
|
91
|
-
- Types/Interfaces: `PascalCase` (e.g., `User`, `ApiResponse<T>`)
|
|
92
|
-
- Enums: `PascalCase` with `UPPER_SNAKE_CASE` values
|
|
93
|
-
|
|
94
|
-
### Variables
|
|
95
|
-
- Boolean: prefix with `is`, `has`, `should` (e.g., `isLoading`, `hasError`)
|
|
96
|
-
- Arrays: plural nouns (e.g., `users`, `products`)
|
|
97
|
-
- Event handlers: prefix with `handle` or `on` (e.g., `handleSubmit`, `onPress`)
|
|
98
|
-
|
|
99
|
-
## 🗺️ Routing Guide
|
|
100
|
-
|
|
101
|
-
### Route Groups (Expo Router)
|
|
102
|
-
|
|
103
|
-
#### `(auth)` - Public Routes
|
|
104
|
-
```typescript
|
|
105
|
-
// No authentication required
|
|
106
|
-
app/(auth)/
|
|
107
|
-
├── _layout.tsx // Auth-specific layout (no header)
|
|
108
|
-
├── login.tsx // Login screen
|
|
109
|
-
└── register.tsx // Registration screen
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
#### `(protected)` - Authenticated Routes
|
|
113
|
-
```typescript
|
|
114
|
-
// Requires valid user token
|
|
115
|
-
app/(protected)/
|
|
116
|
-
├── _layout.tsx // Protected layout with header
|
|
117
|
-
├── home.tsx // Home screen
|
|
118
|
-
└── settings.tsx // User settings
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
#### `(admin)` - Admin-Only Routes
|
|
122
|
-
```typescript
|
|
123
|
-
// Requires admin role
|
|
124
|
-
app/(admin)/
|
|
125
|
-
├── profile.tsx // Admin profile
|
|
126
|
-
└── user-management.tsx // User CRUD
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
### Navigation
|
|
130
|
-
```typescript
|
|
131
|
-
import { router } from 'expo-router';
|
|
132
|
-
|
|
133
|
-
// Navigate
|
|
134
|
-
router.push('/login');
|
|
135
|
-
router.replace('/(protected)/home');
|
|
136
|
-
|
|
137
|
-
// Go back
|
|
138
|
-
router.back();
|
|
139
|
-
|
|
140
|
-
// With params
|
|
141
|
-
router.push({
|
|
142
|
-
pathname: '/user/[id]',
|
|
143
|
-
params: { id: '123' }
|
|
144
|
-
});
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
## 🎨 Styling Guide
|
|
148
|
-
|
|
149
|
-
### NativeWind (Tailwind CSS)
|
|
150
|
-
```typescript
|
|
151
|
-
// Use className prop
|
|
152
|
-
<View className="flex-1 bg-white p-4">
|
|
153
|
-
<Text className="text-lg font-bold text-gray-900">
|
|
154
|
-
Hello World
|
|
155
|
-
</Text>
|
|
156
|
-
</View>
|
|
157
|
-
|
|
158
|
-
// Conditional classes
|
|
159
|
-
<View className={`p-4 ${isActive ? 'bg-blue-500' : 'bg-gray-200'}`}>
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
### Theme Colors
|
|
163
|
-
```typescript
|
|
164
|
-
// Defined in src/theme/colors.ts
|
|
165
|
-
import { colors } from '@/theme/colors';
|
|
166
|
-
|
|
167
|
-
// Usage in StyleSheet (if needed)
|
|
168
|
-
backgroundColor: colors.primary[500]
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
### Best Practices
|
|
172
|
-
- Prefer NativeWind classes over StyleSheet
|
|
173
|
-
- Use theme colors for consistency
|
|
174
|
-
- Responsive: `sm:`, `md:`, `lg:` prefixes
|
|
175
|
-
- Dark mode: `dark:` prefix
|
|
176
|
-
|
|
177
|
-
## 🔐 Authentication & Authorization
|
|
178
|
-
|
|
179
|
-
### AuthContext
|
|
180
|
-
```typescript
|
|
181
|
-
// Available throughout the app
|
|
182
|
-
const { user, login, logout, isAuthenticated } = useAuth();
|
|
183
|
-
|
|
184
|
-
// User object structure
|
|
185
|
-
interface User {
|
|
186
|
-
id: string;
|
|
187
|
-
email: string;
|
|
188
|
-
name: string;
|
|
189
|
-
role: 'user' | 'admin';
|
|
190
|
-
}
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
### Protected Routes
|
|
194
|
-
```typescript
|
|
195
|
-
// Automatic protection via ProtectedRoute component
|
|
196
|
-
// Already implemented in (protected)/_layout.tsx and (admin)/_layout.tsx
|
|
197
|
-
|
|
198
|
-
// Manual check in component
|
|
199
|
-
const { isAuthenticated, user } = useAuth();
|
|
200
|
-
|
|
201
|
-
if (!isAuthenticated) {
|
|
202
|
-
return <Redirect href="/login" />;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
if (user?.role !== 'admin') {
|
|
206
|
-
return <Redirect href="/(protected)/home" />;
|
|
207
|
-
}
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
### Token Management
|
|
211
|
-
```typescript
|
|
212
|
-
// Stored in AsyncStorage via utils/storage.ts
|
|
213
|
-
import { storage } from '@/utils/storage';
|
|
214
|
-
|
|
215
|
-
await storage.setToken(token);
|
|
216
|
-
const token = await storage.getToken();
|
|
217
|
-
await storage.removeToken();
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
## 🛠️ Service Layer Guide
|
|
221
|
-
|
|
222
|
-
### Service Structure
|
|
223
|
-
```typescript
|
|
224
|
-
// src/services/user.service.ts
|
|
225
|
-
import { api } from './api';
|
|
226
|
-
import { User, CreateUserDto } from '@/types/user.types';
|
|
227
|
-
|
|
228
|
-
export const userService = {
|
|
229
|
-
// GET all users
|
|
230
|
-
getAll: async (): Promise<User[]> => {
|
|
231
|
-
const response = await api.get('/users');
|
|
232
|
-
return response.data;
|
|
233
|
-
},
|
|
234
|
-
|
|
235
|
-
// GET single user
|
|
236
|
-
getById: async (id: string): Promise<User> => {
|
|
237
|
-
const response = await api.get(`/users/${id}`);
|
|
238
|
-
return response.data;
|
|
239
|
-
},
|
|
240
|
-
|
|
241
|
-
// POST create user
|
|
242
|
-
create: async (data: CreateUserDto): Promise<User> => {
|
|
243
|
-
const response = await api.post('/users', data);
|
|
244
|
-
return response.data;
|
|
245
|
-
},
|
|
246
|
-
|
|
247
|
-
// PUT update user
|
|
248
|
-
update: async (id: string, data: Partial<User>): Promise<User> => {
|
|
249
|
-
const response = await api.put(`/users/${id}`, data);
|
|
250
|
-
return response.data;
|
|
251
|
-
},
|
|
252
|
-
|
|
253
|
-
// DELETE user
|
|
254
|
-
delete: async (id: string): Promise<void> => {
|
|
255
|
-
await api.delete(`/users/${id}`);
|
|
256
|
-
},
|
|
257
|
-
};
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
### Mock Services
|
|
261
|
-
```typescript
|
|
262
|
-
// src/services/mockApi/user.mock.ts
|
|
263
|
-
import { User } from '@/types/user.types';
|
|
264
|
-
|
|
265
|
-
const mockUsers: User[] = [
|
|
266
|
-
{ id: '1', name: 'John Doe', email: 'john@example.com', role: 'admin' },
|
|
267
|
-
// ... more mock data
|
|
268
|
-
];
|
|
269
|
-
|
|
270
|
-
export const userMockService = {
|
|
271
|
-
getAll: async (): Promise<User[]> => {
|
|
272
|
-
// Simulate network delay
|
|
273
|
-
await new Promise(resolve => setTimeout(resolve, 500));
|
|
274
|
-
return mockUsers;
|
|
275
|
-
},
|
|
276
|
-
// ... other methods
|
|
277
|
-
};
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
### API Client
|
|
281
|
-
```typescript
|
|
282
|
-
// src/services/api.ts
|
|
283
|
-
import axios from 'axios';
|
|
284
|
-
import { storage } from '@/utils/storage';
|
|
285
|
-
|
|
286
|
-
export const api = axios.create({
|
|
287
|
-
baseURL: process.env.EXPO_PUBLIC_API_URL || 'http://localhost:3000',
|
|
288
|
-
timeout: 10000,
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
// Request interceptor (add auth token)
|
|
292
|
-
api.interceptors.request.use(async (config) => {
|
|
293
|
-
const token = await storage.getToken();
|
|
294
|
-
if (token) {
|
|
295
|
-
config.headers.Authorization = `Bearer ${token}`;
|
|
296
|
-
}
|
|
297
|
-
return config;
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
// Response interceptor (handle errors)
|
|
301
|
-
api.interceptors.response.use(
|
|
302
|
-
(response) => response,
|
|
303
|
-
(error) => {
|
|
304
|
-
if (error.response?.status === 401) {
|
|
305
|
-
// Handle unauthorized
|
|
306
|
-
storage.removeToken();
|
|
307
|
-
router.replace('/login');
|
|
308
|
-
}
|
|
309
|
-
return Promise.reject(error);
|
|
310
|
-
}
|
|
311
|
-
);
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
## 🪝 Custom Hooks Guide
|
|
315
|
-
|
|
316
|
-
### Pattern
|
|
317
|
-
```typescript
|
|
318
|
-
// src/hooks/useUsers.ts
|
|
319
|
-
import { useState, useEffect } from 'react';
|
|
320
|
-
import { userService } from '@/services/user.service';
|
|
321
|
-
import { User } from '@/types/user.types';
|
|
322
|
-
|
|
323
|
-
export const useUsers = () => {
|
|
324
|
-
const [users, setUsers] = useState<User[]>([]);
|
|
325
|
-
const [loading, setLoading] = useState(true);
|
|
326
|
-
const [error, setError] = useState<string | null>(null);
|
|
327
|
-
|
|
328
|
-
const fetchUsers = async () => {
|
|
329
|
-
try {
|
|
330
|
-
setLoading(true);
|
|
331
|
-
setError(null);
|
|
332
|
-
const data = await userService.getAll();
|
|
333
|
-
setUsers(data);
|
|
334
|
-
} catch (err) {
|
|
335
|
-
setError(err instanceof Error ? err.message : 'Failed to fetch users');
|
|
336
|
-
} finally {
|
|
337
|
-
setLoading(false);
|
|
338
|
-
}
|
|
339
|
-
};
|
|
340
|
-
|
|
341
|
-
useEffect(() => {
|
|
342
|
-
fetchUsers();
|
|
343
|
-
}, []);
|
|
344
|
-
|
|
345
|
-
const refresh = () => fetchUsers();
|
|
346
|
-
|
|
347
|
-
return { users, loading, error, refresh };
|
|
348
|
-
};
|
|
349
|
-
```
|
|
350
|
-
|
|
351
|
-
### Usage in Component
|
|
352
|
-
```typescript
|
|
353
|
-
const UserList = () => {
|
|
354
|
-
const { users, loading, error, refresh } = useUsers();
|
|
355
|
-
|
|
356
|
-
if (loading) return <Loading />;
|
|
357
|
-
if (error) return <ErrorState message={error} />;
|
|
358
|
-
|
|
359
|
-
return (
|
|
360
|
-
<FlatList
|
|
361
|
-
data={users}
|
|
362
|
-
renderItem={({ item }) => <UserCard user={item} />}
|
|
363
|
-
refreshing={loading}
|
|
364
|
-
onRefresh={refresh}
|
|
365
|
-
/>
|
|
366
|
-
);
|
|
367
|
-
};
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
## 📦 Component Patterns
|
|
371
|
-
|
|
372
|
-
### Reusable Component Template
|
|
373
|
-
```typescript
|
|
374
|
-
// src/components/common/Button.tsx
|
|
375
|
-
import { TouchableOpacity, Text, ActivityIndicator } from 'react-native';
|
|
376
|
-
|
|
377
|
-
interface ButtonProps {
|
|
378
|
-
title: string;
|
|
379
|
-
onPress: () => void;
|
|
380
|
-
variant?: 'primary' | 'secondary' | 'danger';
|
|
381
|
-
loading?: boolean;
|
|
382
|
-
disabled?: boolean;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
export const Button = ({
|
|
386
|
-
title,
|
|
387
|
-
onPress,
|
|
388
|
-
variant = 'primary',
|
|
389
|
-
loading = false,
|
|
390
|
-
disabled = false,
|
|
391
|
-
}: ButtonProps) => {
|
|
392
|
-
const baseClasses = 'px-6 py-3 rounded-lg items-center justify-center';
|
|
393
|
-
const variantClasses = {
|
|
394
|
-
primary: 'bg-blue-600',
|
|
395
|
-
secondary: 'bg-gray-600',
|
|
396
|
-
danger: 'bg-red-600',
|
|
397
|
-
};
|
|
398
|
-
|
|
399
|
-
return (
|
|
400
|
-
<TouchableOpacity
|
|
401
|
-
onPress={onPress}
|
|
402
|
-
disabled={disabled || loading}
|
|
403
|
-
className={`${baseClasses} ${variantClasses[variant]} ${
|
|
404
|
-
disabled ? 'opacity-50' : ''
|
|
405
|
-
}`}
|
|
406
|
-
>
|
|
407
|
-
{loading ? (
|
|
408
|
-
<ActivityIndicator color="white" />
|
|
409
|
-
) : (
|
|
410
|
-
<Text className="text-white font-semibold">{title}</Text>
|
|
411
|
-
)}
|
|
412
|
-
</TouchableOpacity>
|
|
413
|
-
);
|
|
414
|
-
};
|
|
415
|
-
```
|
|
416
|
-
|
|
417
|
-
### Feature Component Template
|
|
418
|
-
```typescript
|
|
419
|
-
// src/components/users/UserCard.tsx
|
|
420
|
-
import { View, Text } from 'react-native';
|
|
421
|
-
import { User } from '@/types/user.types';
|
|
422
|
-
import { Button } from '@/components/common/Button';
|
|
423
|
-
|
|
424
|
-
interface UserCardProps {
|
|
425
|
-
user: User;
|
|
426
|
-
onEdit?: (user: User) => void;
|
|
427
|
-
onDelete?: (userId: string) => void;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
export const UserCard = ({ user, onEdit, onDelete }: UserCardProps) => {
|
|
431
|
-
return (
|
|
432
|
-
<View className="bg-white p-4 rounded-lg shadow-sm mb-3">
|
|
433
|
-
<Text className="text-lg font-bold">{user.name}</Text>
|
|
434
|
-
<Text className="text-gray-600">{user.email}</Text>
|
|
435
|
-
<Text className="text-sm text-gray-500">{user.role}</Text>
|
|
436
|
-
|
|
437
|
-
<View className="flex-row gap-2 mt-3">
|
|
438
|
-
{onEdit && (
|
|
439
|
-
<Button title="Edit" onPress={() => onEdit(user)} variant="secondary" />
|
|
440
|
-
)}
|
|
441
|
-
{onDelete && (
|
|
442
|
-
<Button title="Delete" onPress={() => onDelete(user.id)} variant="danger" />
|
|
443
|
-
)}
|
|
444
|
-
</View>
|
|
445
|
-
</View>
|
|
446
|
-
);
|
|
447
|
-
};
|
|
448
|
-
```
|
|
449
|
-
|
|
450
|
-
## 🔤 TypeScript Patterns
|
|
451
|
-
|
|
452
|
-
### Type Definitions
|
|
453
|
-
```typescript
|
|
454
|
-
// src/types/user.types.ts
|
|
455
|
-
|
|
456
|
-
// Base entity
|
|
457
|
-
export interface User {
|
|
458
|
-
id: string;
|
|
459
|
-
email: string;
|
|
460
|
-
name: string;
|
|
461
|
-
role: UserRole;
|
|
462
|
-
createdAt: string;
|
|
463
|
-
updatedAt: string;
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
// Enums
|
|
467
|
-
export enum UserRole {
|
|
468
|
-
USER = 'user',
|
|
469
|
-
ADMIN = 'admin',
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
// DTOs (Data Transfer Objects)
|
|
473
|
-
export interface CreateUserDto {
|
|
474
|
-
email: string;
|
|
475
|
-
name: string;
|
|
476
|
-
password: string;
|
|
477
|
-
role?: UserRole;
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
export interface UpdateUserDto {
|
|
481
|
-
email?: string;
|
|
482
|
-
name?: string;
|
|
483
|
-
role?: UserRole;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
// Utility types
|
|
487
|
-
export type UserWithoutPassword = Omit<User, 'password'>;
|
|
488
|
-
export type PartialUser = Partial<User>;
|
|
489
|
-
```
|
|
490
|
-
|
|
491
|
-
### Generic API Response
|
|
492
|
-
```typescript
|
|
493
|
-
// src/types/api.types.ts
|
|
494
|
-
export interface ApiResponse<T> {
|
|
495
|
-
data: T;
|
|
496
|
-
message: string;
|
|
497
|
-
success: boolean;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
export interface PaginatedResponse<T> {
|
|
501
|
-
data: T[];
|
|
502
|
-
total: number;
|
|
503
|
-
page: number;
|
|
504
|
-
limit: number;
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
export interface ApiError {
|
|
508
|
-
message: string;
|
|
509
|
-
code: string;
|
|
510
|
-
details?: Record<string, string[]>;
|
|
511
|
-
}
|
|
512
|
-
```
|
|
513
|
-
|
|
514
|
-
## 🚨 Error Handling
|
|
515
|
-
|
|
516
|
-
### Service Level
|
|
517
|
-
```typescript
|
|
518
|
-
try {
|
|
519
|
-
const users = await userService.getAll();
|
|
520
|
-
return users;
|
|
521
|
-
} catch (error) {
|
|
522
|
-
if (axios.isAxiosError(error)) {
|
|
523
|
-
// Handle API errors
|
|
524
|
-
const message = error.response?.data?.message || 'Network error';
|
|
525
|
-
throw new Error(message);
|
|
526
|
-
}
|
|
527
|
-
throw error;
|
|
528
|
-
}
|
|
529
|
-
```
|
|
530
|
-
|
|
531
|
-
### Component Level
|
|
532
|
-
```typescript
|
|
533
|
-
const handleSubmit = async () => {
|
|
534
|
-
try {
|
|
535
|
-
setLoading(true);
|
|
536
|
-
await userService.create(formData);
|
|
537
|
-
Toast.show({ type: 'success', text1: 'User created successfully' });
|
|
538
|
-
router.back();
|
|
539
|
-
} catch (error) {
|
|
540
|
-
Toast.show({
|
|
541
|
-
type: 'error',
|
|
542
|
-
text1: 'Failed to create user',
|
|
543
|
-
text2: error instanceof Error ? error.message : 'Unknown error',
|
|
544
|
-
});
|
|
545
|
-
} finally {
|
|
546
|
-
setLoading(false);
|
|
547
|
-
}
|
|
548
|
-
};
|
|
549
|
-
```
|
|
550
|
-
|
|
551
|
-
## ✅ Best Practices
|
|
552
|
-
|
|
553
|
-
### Do's
|
|
554
|
-
- ✅ Use TypeScript strict mode
|
|
555
|
-
- ✅ Define proper interfaces for all data structures
|
|
556
|
-
- ✅ Use custom hooks for reusable logic
|
|
557
|
-
- ✅ Keep components small and focused (< 200 lines)
|
|
558
|
-
- ✅ Use meaningful variable and function names
|
|
559
|
-
- ✅ Add JSDoc comments for complex functions
|
|
560
|
-
- ✅ Handle loading and error states in UI
|
|
561
|
-
- ✅ Use async/await instead of promises
|
|
562
|
-
- ✅ Implement proper error boundaries
|
|
563
|
-
- ✅ Use React.memo for expensive renders
|
|
564
|
-
|
|
565
|
-
### Don'ts
|
|
566
|
-
- ❌ Don't use `any` type
|
|
567
|
-
- ❌ Don't put business logic in components
|
|
568
|
-
- ❌ Don't mutate state directly
|
|
569
|
-
- ❌ Don't forget to cleanup effects
|
|
570
|
-
- ❌ Don't hardcode strings (use constants)
|
|
571
|
-
- ❌ Don't ignore TypeScript errors
|
|
572
|
-
- ❌ Don't use inline styles when NativeWind available
|
|
573
|
-
- ❌ Don't fetch data in useEffect without dependency array
|
|
574
|
-
|
|
575
|
-
## 🧪 Testing Patterns
|
|
576
|
-
|
|
577
|
-
### Component Test Template
|
|
578
|
-
```typescript
|
|
579
|
-
// src/components/common/__tests__/Button.test.tsx
|
|
580
|
-
import { render, fireEvent } from '@testing-library/react-native';
|
|
581
|
-
import { Button } from '../Button';
|
|
582
|
-
|
|
583
|
-
describe('Button', () => {
|
|
584
|
-
it('renders correctly', () => {
|
|
585
|
-
const { getByText } = render(<Button title="Click me" onPress={() => {}} />);
|
|
586
|
-
expect(getByText('Click me')).toBeTruthy();
|
|
587
|
-
});
|
|
588
|
-
|
|
589
|
-
it('calls onPress when clicked', () => {
|
|
590
|
-
const onPress = jest.fn();
|
|
591
|
-
const { getByText } = render(<Button title="Click me" onPress={onPress} />);
|
|
592
|
-
fireEvent.press(getByText('Click me'));
|
|
593
|
-
expect(onPress).toHaveBeenCalled();
|
|
594
|
-
});
|
|
595
|
-
|
|
596
|
-
it('shows loading state', () => {
|
|
597
|
-
const { getByTestId } = render(
|
|
598
|
-
<Button title="Click me" onPress={() => {}} loading={true} />
|
|
599
|
-
);
|
|
600
|
-
expect(getByTestId('activity-indicator')).toBeTruthy();
|
|
601
|
-
});
|
|
602
|
-
});
|
|
603
|
-
```
|
|
604
|
-
|
|
605
|
-
## 🔧 Common Tasks
|
|
606
|
-
|
|
607
|
-
### Adding a New Feature
|
|
608
|
-
1. Define types in `src/types/feature.types.ts`
|
|
609
|
-
2. Create service in `src/services/feature.service.ts`
|
|
610
|
-
3. Create mock in `src/services/mockApi/feature.mock.ts`
|
|
611
|
-
4. Create custom hook in `src/hooks/useFeature.ts`
|
|
612
|
-
5. Create components in `src/components/feature/`
|
|
613
|
-
6. Add route in `app/(protected)/feature.tsx`
|
|
614
|
-
|
|
615
|
-
### Adding a New Route
|
|
616
|
-
1. Create file in appropriate route group
|
|
617
|
-
2. Add proper layout if needed
|
|
618
|
-
3. Implement authentication guard if protected
|
|
619
|
-
4. Update navigation types if using typed routes
|
|
620
|
-
|
|
621
|
-
### Adding a New Component
|
|
622
|
-
1. Create component file in appropriate folder
|
|
623
|
-
2. Define Props interface
|
|
624
|
-
3. Export as named export
|
|
625
|
-
4. Add to index.ts if grouping exports
|
|
626
|
-
|
|
627
|
-
## 📚 Key Files Reference
|
|
628
|
-
|
|
629
|
-
### Must-Read Files
|
|
630
|
-
- `app/_layout.tsx` - Root app layout
|
|
631
|
-
- `src/contexts/AuthContext.tsx` - Authentication state
|
|
632
|
-
- `src/services/api.ts` - API client setup
|
|
633
|
-
- `src/utils/storage.ts` - AsyncStorage wrapper
|
|
634
|
-
- `src/types/*.types.ts` - Type definitions
|
|
635
|
-
|
|
636
|
-
### Configuration Files
|
|
637
|
-
- `app.json` - Expo configuration
|
|
638
|
-
- `tailwind.config.js` - NativeWind/Tailwind config
|
|
639
|
-
- `tsconfig.json` - TypeScript configuration
|
|
640
|
-
- `babel.config.js` - Babel configuration
|
|
641
|
-
|
|
642
|
-
## 🚀 Development Workflow
|
|
643
|
-
|
|
644
|
-
### Starting Development
|
|
645
|
-
```bash
|
|
646
|
-
npm install
|
|
647
|
-
npm start
|
|
648
|
-
# Press 'i' for iOS, 'a' for Android, 'w' for Web
|
|
649
|
-
```
|
|
650
|
-
|
|
651
|
-
### Code Generation Guidelines
|
|
652
|
-
1. Always use TypeScript with proper types
|
|
653
|
-
2. Follow existing file structure
|
|
654
|
-
3. Use NativeWind for styling
|
|
655
|
-
4. Implement error handling
|
|
656
|
-
5. Add loading states
|
|
657
|
-
6. Keep components reusable
|
|
658
|
-
7. Write JSDoc for complex functions
|
|
659
|
-
|
|
660
|
-
---
|
|
661
|
-
|
|
662
|
-
**Last Updated**: 2025-01-29
|
|
663
|
-
**Version**: 1.0.0
|