create-ern-boilerplate 0.0.49 → 0.0.51

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.
@@ -0,0 +1,9 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(cat:*)"
5
+ ],
6
+ "deny": [],
7
+ "ask": []
8
+ }
9
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-ern-boilerplate",
3
- "version": "0.0.49",
3
+ "version": "0.0.51",
4
4
  "description": "Expo React Native boilerplate generator",
5
5
  "bin": {
6
6
  "create-ern-boilerplate": "./create.js",
@@ -131,15 +131,17 @@ Does your app need user accounts?
131
131
 
132
132
  Quick reference:
133
133
  ```
134
- app/(tabs)/ → Screens
135
- src/components/ → Components
136
- src/hooks/ → Custom hooks
137
- src/services/ → API services
138
- src/services/mockApi/ → Mock API implementations
139
- src/store/ → Zustand stores
140
- src/types/ → TypeScript types
141
- server/db.json → Mock data
142
- examples/ → Code examples
134
+ app/(tabs)/ → Screens
135
+ src/components/ → Components
136
+ src/hooks/ → Custom hooks
137
+ src/services/ → API services
138
+ src/services/mockApi/ → Mock API implementations (auth, users only)
139
+ src/store/ → Zustand stores
140
+ src/types/ → TypeScript types
141
+ server/db.json → Mock data (users only by default)
142
+ examples/ → Code examples
143
+ examples/services/mockApi/ → Mock API pattern examples (basic & advanced CRUD)
144
+ examples/types/ → Type definition examples (generic resource types)
143
145
  ```
144
146
 
145
147
  ## ⚠️ Common Mistakes to Avoid
@@ -177,12 +179,14 @@ cat SERVER_GUIDE.md # Mock API guide
177
179
  # Check examples
178
180
  ls examples/screens/ # Screen patterns
179
181
  ls examples/services/ # Service patterns
182
+ ls examples/services/mockApi/ # Mock API pattern examples
183
+ ls examples/types/ # Type definition examples
180
184
  ls examples/apps/ # Complete app examples
181
185
 
182
186
  # Check current structure
183
- cat server/db.json # Mock data
184
- ls src/types/ # Type definitions
185
- ls src/services/mockApi/ # Mock API files
187
+ cat server/db.json # Mock data (users only)
188
+ ls src/types/ # Type definitions (auth, user types)
189
+ ls src/services/mockApi/ # Active mock API files (auth, users)
186
190
  ```
187
191
 
188
192
  ## ✅ Success Checklist
@@ -43,22 +43,29 @@ The `server/db.json` file contains **minimal sample/starter data** for demonstra
43
43
  ```
44
44
  templates/agent-generator/
45
45
  ├── server/
46
- │ └── db.json # Mock database (dummy data)
46
+ │ └── db.json # Mock database (users only by default)
47
47
 
48
48
  ├── src/
49
49
  │ ├── types/ # TypeScript interfaces
50
50
  │ │ ├── auth.types.ts # User & Auth types
51
- │ │ ├── user.types.ts # User data types
52
- │ │ └── product.types.ts # Product & Category types
51
+ │ │ └── user.types.ts # User data types
53
52
  │ │
54
53
  │ └── services/
55
54
  │ ├── mockApi/ # Mock API implementations
56
55
  │ │ ├── auth.mock.ts # Auth endpoints (login, register, etc)
57
56
  │ │ ├── users.mock.ts # User CRUD endpoints
58
- │ │ ├── products.mock.ts # Product CRUD endpoints
59
- │ │ └── categories.mock.ts # Category CRUD endpoints
57
+ │ │ └── index.ts # Mock API router
60
58
  │ │
61
59
  │ └── api.ts # API client (switches between mock/real)
60
+
61
+ └── examples/ # Pattern examples for AI generation
62
+ ├── types/
63
+ │ └── resource.types.example.ts # Example: Generic resource & tag types
64
+
65
+ └── services/
66
+ └── mockApi/
67
+ ├── resource-advanced.mock.example.ts # Example: Advanced CRUD (filtering, sorting, pagination)
68
+ └── resource-basic.mock.example.ts # Example: Basic CRUD operations
62
69
  ```
63
70
 
64
71
  ## 📋 Understanding server/db.json
@@ -69,23 +76,26 @@ The `server/db.json` file is the **mock database** that contains sample/starter
69
76
 
70
77
  **Current Sample Data:**
71
78
  - **3 users** (1 admin, 1 active user, 1 inactive user) - demonstrates different roles and statuses
72
- - **3 categories** (Electronics, Food, Clothing) - shows category structure
73
- - **4 products** (includes 1 low stock item) - demonstrates normal and edge cases
74
79
 
75
80
  **Format:**
76
81
  ```json
77
82
  {
78
- "users": [...], // Array of User objects (sample: 3 items)
79
- "categories": [...], // Array of Category objects (sample: 3 items)
80
- "products": [...] // Array of Product objects (sample: 4 items)
83
+ "users": [...] // Array of User objects (sample: 3 items)
81
84
  }
82
85
  ```
83
86
 
84
- **⚠️ This is minimal starter data!** When you add new features (e.g., News app, Todo app), you'll add new arrays or replace existing ones with data specific to your app.
87
+ **⚠️ This is minimal starter data!** When you add new features (e.g., News app, Todo app, Product management), you'll add new arrays with data specific to your app.
88
+
89
+ **📚 Example Data Models:**
90
+ For reference, check `examples/types/` and `examples/services/mockApi/` which contain generic pattern implementations:
91
+ - **Resource** - Generic data model with title, content, status, tags
92
+ - **Tag** - Simple categorization/grouping system
93
+
94
+ These examples are intentionally generic and show you how to structure data models with filtering, sorting, and relationships. Adapt them for your specific use case (News → Article, Todo → Task, E-commerce → Product, etc.).
85
95
 
86
96
  ### Current Data Models
87
97
 
88
- #### 1. Users
98
+ #### Users
89
99
 
90
100
  Based on `User` interface from `src/types/auth.types.ts`:
91
101
 
@@ -124,69 +134,19 @@ interface User {
124
134
  }
125
135
  ```
126
136
 
127
- #### 2. Categories
137
+ ### Example Data Models (For Reference)
128
138
 
129
- Based on `Category` interface from `src/types/product.types.ts`:
130
-
131
- ```typescript
132
- interface Category {
133
- id: string; // Required: Unique identifier
134
- name: string; // Required: Category name
135
- slug: string; // Required: URL-friendly name
136
- description: string; // Required: Category description
137
- icon: string; // Required: Emoji or icon
138
- color: string; // Required: Hex color code
139
- }
140
- ```
139
+ For generic data model pattern examples, see:
140
+ - `examples/types/resource.types.example.ts` - Generic Resource and Tag type definitions
141
+ - `examples/services/mockApi/resource-advanced.mock.example.ts` - Advanced CRUD with filtering, sorting, pagination
142
+ - `examples/services/mockApi/resource-basic.mock.example.ts` - Basic CRUD operations
141
143
 
142
- **Example:**
143
- ```json
144
- {
145
- "id": "1",
146
- "name": "Electronics",
147
- "slug": "electronics",
148
- "description": "Electronic devices and accessories",
149
- "icon": "📱",
150
- "color": "#3b82f6"
151
- }
152
- ```
153
-
154
- #### 3. Products
155
-
156
- Based on `Product` interface from `src/types/product.types.ts`:
157
-
158
- ```typescript
159
- interface Product {
160
- id: string; // Required: Unique identifier
161
- name: string; // Required: Product name
162
- categoryId: string; // Required: Foreign key to categories
163
- unit: string; // Required: Unit of measurement (pcs, kg, liter, etc)
164
- stockSystem: number; // Required: Stock in system
165
- stockPhysical: number; // Required: Actual physical stock
166
- minStock: number; // Required: Minimum stock threshold
167
- price: number; // Required: Product price
168
- description: string; // Required: Product description
169
- createdAt: string; // Required: ISO date string
170
- updatedAt: string; // Required: ISO date string
171
- }
172
- ```
173
-
174
- **Example:**
175
- ```json
176
- {
177
- "id": "1",
178
- "name": "iPhone 15 Pro",
179
- "categoryId": "1",
180
- "unit": "pcs",
181
- "stockSystem": 50,
182
- "stockPhysical": 48,
183
- "minStock": 10,
184
- "price": 15000000,
185
- "description": "Latest iPhone with A17 Pro chip",
186
- "createdAt": "2024-01-10T00:00:00.000Z",
187
- "updatedAt": "2024-01-10T00:00:00.000Z"
188
- }
189
- ```
144
+ These examples demonstrate:
145
+ - Complex filtering (search, tags, status)
146
+ - Multiple sort options (date, title)
147
+ - Pagination implementation
148
+ - Foreign key relationships (tagId)
149
+ - Generic patterns adaptable to any domain
190
150
 
191
151
  ## 🎯 How Mock API Works
192
152
 
@@ -195,58 +155,70 @@ interface Product {
195
155
  Each mock file imports data from `server/db.json`:
196
156
 
197
157
  ```typescript
198
- // src/services/mockApi/products.mock.ts
158
+ // Active example: src/services/mockApi/users.mock.ts
199
159
  import * as db from 'server/db.json';
200
160
 
201
- let products: Product[] = [...db.products]; // Clone the data
161
+ let users: User[] = [...db.users]; // Clone the data
202
162
  ```
203
163
 
164
+ For reference, see `examples/services/mockApi/resource-advanced.mock.example.ts` and `resource-basic.mock.example.ts` which follow the same pattern.
165
+
204
166
  ### 2. CRUD Operations
205
167
 
206
- Mock files provide functions that simulate API endpoints:
168
+ Mock files provide functions that simulate API endpoints. Here's the pattern (see `src/services/mockApi/users.mock.ts` or `examples/services/mockApi/resource-advanced.mock.example.ts`):
207
169
 
208
170
  ```typescript
209
- // GET /products
171
+ // GET /resource
210
172
  export async function get<T = any>(url: string): Promise<T> {
211
- if (url === '/products') {
212
- return products as unknown as T;
173
+ if (url === '/resource') {
174
+ return items as unknown as T;
175
+ }
176
+
177
+ // Get single item by ID
178
+ const match = url.match(/\/resource\/(\w+)/);
179
+ if (match) {
180
+ const item = items.find(i => i.id === match[1]);
181
+ if (!item) throw new Error('Item not found');
182
+ return item as unknown as T;
213
183
  }
214
- // ...
215
184
  }
216
185
 
217
- // POST /products
218
- export async function post<T = any>(url: string, body: CreateProductData): Promise<T> {
219
- const newProduct = {
186
+ // POST /resource
187
+ export async function post<T = any>(url: string, body: CreateData): Promise<T> {
188
+ const newItem = {
220
189
  id: String(Date.now()),
221
190
  ...body,
222
191
  createdAt: new Date().toISOString(),
223
192
  updatedAt: new Date().toISOString(),
224
193
  };
225
- products.push(newProduct);
226
- return newProduct as unknown as T;
194
+ items.push(newItem);
195
+ return newItem as unknown as T;
227
196
  }
228
197
 
229
- // PUT /products/:id
230
- export async function put<T = any>(url: string, body: UpdateProductData): Promise<T> {
231
- const match = url.match(/\/products\/(\w+)/);
198
+ // PUT /resource/:id
199
+ export async function put<T = any>(url: string, body: UpdateData): Promise<T> {
200
+ const match = url.match(/\/resource\/(\w+)/);
232
201
  if (match) {
233
202
  const id = match[1];
234
- const index = products.findIndex(p => p.id === id);
235
- products[index] = { ...products[index], ...body };
236
- return products[index] as unknown as T;
203
+ const index = items.findIndex(i => i.id === id);
204
+ if (index === -1) throw new Error('Item not found');
205
+ items[index] = { ...items[index], ...body, updatedAt: new Date().toISOString() };
206
+ return items[index] as unknown as T;
237
207
  }
238
208
  }
239
209
 
240
- // DELETE /products/:id
210
+ // DELETE /resource/:id
241
211
  export async function del<T = any>(url: string): Promise<T> {
242
- const match = url.match(/\/products\/(\w+)/);
212
+ const match = url.match(/\/resource\/(\w+)/);
243
213
  if (match) {
244
- products = products.filter(p => p.id !== match[1]);
245
- return { message: 'Deleted' } as unknown as T;
214
+ items = items.filter(i => i.id !== match[1]);
215
+ return { message: 'Deleted successfully' } as unknown as T;
246
216
  }
247
217
  }
248
218
  ```
249
219
 
220
+ **💡 Tip:** For complex filtering, sorting, and pagination examples, see `examples/services/mockApi/resource-advanced.mock.example.ts`
221
+
250
222
  ### 3. API Client Integration
251
223
 
252
224
  The API client (`src/services/api.ts`) switches between mock and real API:
@@ -555,8 +527,12 @@ When generating code that needs data:
555
527
  ### 1. Check Existing Types First
556
528
 
557
529
  ```bash
558
- # Read the type definition
559
- cat src/types/product.types.ts
530
+ # Read active type definitions
531
+ cat src/types/auth.types.ts
532
+ cat src/types/user.types.ts
533
+
534
+ # Read example type definitions
535
+ cat examples/types/resource.types.example.ts
560
536
  ```
561
537
 
562
538
  ### 2. Check db.json Structure
@@ -569,7 +545,8 @@ cat server/db.json
569
545
  ### 3. Follow the Pattern
570
546
 
571
547
  Copy existing mock API patterns:
572
- - Read `src/services/mockApi/products.mock.ts`
548
+ - For basic CRUD: Read `src/services/mockApi/users.mock.ts` or `examples/services/mockApi/resource-basic.mock.example.ts`
549
+ - For advanced features: Read `examples/services/mockApi/resource-advanced.mock.example.ts`
573
550
  - Copy the CRUD structure
574
551
  - Adapt for your new data model
575
552
 
@@ -732,10 +709,13 @@ For db.json:
732
709
  ## 📞 Need Help?
733
710
 
734
711
  Check these files for examples:
735
- - `server/db.json` - Current dummy data
736
- - `src/types/*.types.ts` - All type definitions
737
- - `src/services/mockApi/*.mock.ts` - Mock API implementations
738
- - `examples/service.example.ts` - Service pattern
712
+ - `server/db.json` - Current dummy data (users only)
713
+ - `src/types/*.types.ts` - Active type definitions (auth, user)
714
+ - `src/services/mockApi/*.mock.ts` - Active mock API implementations (auth, users)
715
+ - `examples/types/resource.types.example.ts` - Generic resource & tag type definitions
716
+ - `examples/services/mockApi/resource-advanced.mock.example.ts` - Advanced CRUD pattern
717
+ - `examples/services/mockApi/resource-basic.mock.example.ts` - Basic CRUD pattern
718
+ - `examples/services/*.example.ts` - Service patterns
739
719
 
740
720
  ---
741
721
 
@@ -12,6 +12,8 @@
12
12
  "examples": "examples",
13
13
  "examplesScreens": "examples/screens",
14
14
  "examplesServices": "examples/services",
15
+ "examplesMockApi": "examples/services/mockApi",
16
+ "examplesTypes": "examples/types",
15
17
  "examplesApps": "examples/apps",
16
18
  "aiGuide": "AI_GUIDE.md",
17
19
  "serverGuide": "SERVER_GUIDE.md",
@@ -0,0 +1,132 @@
1
+ /**
2
+ * EXAMPLE: Advanced Mock API Pattern
3
+ *
4
+ * This demonstrates a complete CRUD API with:
5
+ * - Filtering (search, tag, status)
6
+ * - Sorting (multiple options)
7
+ * - Pagination
8
+ *
9
+ * Adapt for your use case:
10
+ * - News app: resources → articles, tags → topics
11
+ * - Todo app: resources → tasks, tags → projects
12
+ * - E-commerce: resources → products, tags → categories
13
+ */
14
+
15
+ import { CreateResourceData, Resource, ResourceFilters, UpdateResourceData } from '@/types/resource.types.example';
16
+ import * as db from 'server/db.json';
17
+
18
+ // Initialize from db.json (adapt this to your data structure)
19
+ let resources: Resource[] = [...(db.resources || [])];
20
+
21
+ export async function get<T = any>(url: string, config?: { params?: ResourceFilters }): Promise<T> {
22
+ if (url === '/resources') {
23
+ const { params } = config || {};
24
+ let filtered = [...resources];
25
+
26
+ if (params) {
27
+ const { search, tagId, status, sortBy, limit = 10, page = 1 } = params;
28
+
29
+ // Filter by search (title and content)
30
+ if (search) {
31
+ const q = search.toLowerCase();
32
+ filtered = filtered.filter(
33
+ r => r.title.toLowerCase().includes(q) ||
34
+ r.content?.toLowerCase().includes(q)
35
+ );
36
+ }
37
+
38
+ // Filter by tag
39
+ if (tagId) {
40
+ filtered = filtered.filter(r => r.tagId === tagId);
41
+ }
42
+
43
+ // Filter by status
44
+ if (status) {
45
+ filtered = filtered.filter(r => r.status === status);
46
+ }
47
+
48
+ // Sort
49
+ if (sortBy === 'latest') {
50
+ filtered.sort((a, b) => +new Date(b.createdAt) - +new Date(a.createdAt));
51
+ } else if (sortBy === 'oldest') {
52
+ filtered.sort((a, b) => +new Date(a.createdAt) - +new Date(b.createdAt));
53
+ } else if (sortBy === 'title') {
54
+ filtered.sort((a, b) => a.title.localeCompare(b.title));
55
+ }
56
+
57
+ // Pagination
58
+ const start = (page - 1) * limit;
59
+ return filtered.slice(start, start + limit) as unknown as T;
60
+ }
61
+
62
+ return resources as unknown as T;
63
+ }
64
+
65
+ // Get single resource by ID
66
+ const match = url.match(/\/resources\/(\w+)/);
67
+ if (match) {
68
+ const resource = resources.find(r => r.id === match[1]);
69
+ if (!resource) throw new Error('Resource not found');
70
+ return resource as unknown as T;
71
+ }
72
+
73
+ throw new Error(`Unknown GET /resources endpoint: ${url}`);
74
+ }
75
+
76
+ export async function post<T = any>(url: string, body?: CreateResourceData): Promise<T> {
77
+ if (url === '/resources') {
78
+ if (!body) throw new Error('Resource data is required');
79
+
80
+ const newResource: Resource = {
81
+ id: String(Date.now()),
82
+ title: body.title,
83
+ content: body.content,
84
+ tagId: body.tagId,
85
+ status: body.status || 'draft',
86
+ metadata: body.metadata,
87
+ createdAt: new Date().toISOString(),
88
+ updatedAt: new Date().toISOString(),
89
+ };
90
+
91
+ resources.push(newResource);
92
+ return newResource as unknown as T;
93
+ }
94
+
95
+ throw new Error(`Unknown POST /resources endpoint: ${url}`);
96
+ }
97
+
98
+ export async function put<T = any>(url: string, body: UpdateResourceData): Promise<T> {
99
+ const match = url.match(/\/resources\/(\w+)/);
100
+ if (match) {
101
+ const id = match[1];
102
+ const index = resources.findIndex(r => r.id === id);
103
+ if (index === -1) throw new Error('Resource not found');
104
+
105
+ resources[index] = {
106
+ ...resources[index],
107
+ ...body,
108
+ updatedAt: new Date().toISOString()
109
+ };
110
+
111
+ return resources[index] as unknown as T;
112
+ }
113
+
114
+ throw new Error(`Unknown PUT /resources endpoint: ${url}`);
115
+ }
116
+
117
+ export async function del<T = any>(url: string): Promise<T> {
118
+ const match = url.match(/\/resources\/(\w+)/);
119
+ if (match) {
120
+ const id = match[1];
121
+ const initialLength = resources.length;
122
+ resources = resources.filter(r => r.id !== id);
123
+
124
+ if (resources.length === initialLength) {
125
+ throw new Error('Resource not found');
126
+ }
127
+
128
+ return { message: 'Resource deleted successfully' } as unknown as T;
129
+ }
130
+
131
+ throw new Error(`Unknown DELETE /resources endpoint: ${url}`);
132
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * EXAMPLE: Basic Mock API Pattern
3
+ *
4
+ * This demonstrates a simple CRUD API without filtering/pagination.
5
+ * Use this pattern for simpler data models.
6
+ *
7
+ * Adapt for your use case:
8
+ * - News app: tags → topics
9
+ * - Todo app: tags → projects
10
+ * - E-commerce: tags → categories
11
+ */
12
+
13
+ import { Tag } from '@/types/resource.types.example';
14
+ import * as db from 'server/db.json';
15
+
16
+ // Initialize from db.json (adapt this to your data structure)
17
+ let tags: Tag[] = [...(db.tags || [])];
18
+
19
+ export async function get<T = any>(url: string, config?: any): Promise<T> {
20
+ if (url === '/tags') {
21
+ return tags as unknown as T;
22
+ }
23
+
24
+ // Get single tag by ID
25
+ const match = url.match(/\/tags\/(\w+)/);
26
+ if (match) {
27
+ const tag = tags.find(t => t.id === match[1]);
28
+ if (!tag) throw new Error('Tag not found');
29
+ return tag as unknown as T;
30
+ }
31
+
32
+ throw new Error(`Unknown GET /tags endpoint: ${url}`);
33
+ }
34
+
35
+ export async function post<T = any>(url: string, body?: Partial<Tag>): Promise<T> {
36
+ if (url === '/tags') {
37
+ if (!body) throw new Error('Tag data is required');
38
+
39
+ const newTag: Tag = {
40
+ id: String(Date.now()),
41
+ name: body.name || '',
42
+ slug: body.slug || body.name?.toLowerCase().replace(/\s+/g, '-') || '',
43
+ description: body.description || '',
44
+ icon: body.icon || '🏷️',
45
+ color: body.color || '#6366f1',
46
+ };
47
+
48
+ tags.push(newTag);
49
+ return newTag as unknown as T;
50
+ }
51
+
52
+ throw new Error(`Unknown POST /tags endpoint: ${url}`);
53
+ }
54
+
55
+ export async function put<T = any>(url: string, body: Partial<Tag>): Promise<T> {
56
+ const match = url.match(/\/tags\/(\w+)/);
57
+ if (match) {
58
+ const id = match[1];
59
+ const index = tags.findIndex(t => t.id === id);
60
+ if (index === -1) throw new Error('Tag not found');
61
+
62
+ tags[index] = {
63
+ ...tags[index],
64
+ ...body
65
+ };
66
+
67
+ return tags[index] as unknown as T;
68
+ }
69
+
70
+ throw new Error(`Unknown PUT /tags endpoint: ${url}`);
71
+ }
72
+
73
+ export async function del<T = any>(url: string): Promise<T> {
74
+ const match = url.match(/\/tags\/(\w+)/);
75
+ if (match) {
76
+ const id = match[1];
77
+ const initialLength = tags.length;
78
+ tags = tags.filter(t => t.id !== id);
79
+
80
+ if (tags.length === initialLength) {
81
+ throw new Error('Tag not found');
82
+ }
83
+
84
+ return { message: 'Tag deleted successfully' } as unknown as T;
85
+ }
86
+
87
+ throw new Error(`Unknown DELETE /tags endpoint: ${url}`);
88
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * EXAMPLE: Generic Resource Types
3
+ *
4
+ * This file demonstrates type definitions for a generic resource model.
5
+ * Adapt these patterns for your specific use case:
6
+ * - News app: Resource → Article, Tag → Topic
7
+ * - Todo app: Resource → Task, Tag → Project
8
+ * - E-commerce: Resource → Product, Tag → Category
9
+ * - Blog: Resource → Post, Tag → Category
10
+ */
11
+
12
+ // Basic tag/category system
13
+ export interface Tag {
14
+ id: string;
15
+ name: string;
16
+ slug: string;
17
+ description: string;
18
+ icon: string;
19
+ color: string;
20
+ }
21
+
22
+ // Main resource with common fields
23
+ export interface Resource {
24
+ id: string;
25
+ title: string;
26
+ content: string;
27
+ tagId: string;
28
+ status: 'active' | 'inactive' | 'draft';
29
+ metadata?: string; // JSON string for flexible extra data
30
+ createdAt: string;
31
+ updatedAt: string;
32
+ }
33
+
34
+ // Filtering options (pagination, search, sorting)
35
+ export interface ResourceFilters {
36
+ search?: string;
37
+ tagId?: string;
38
+ status?: 'active' | 'inactive' | 'draft';
39
+ sortBy?: 'latest' | 'oldest' | 'title';
40
+ limit?: number;
41
+ page?: number;
42
+ }
43
+
44
+ // Resource with related tag data
45
+ export interface ResourceWithTag extends Resource {
46
+ tag?: Tag;
47
+ }
48
+
49
+ // Create resource DTO
50
+ export interface CreateResourceData {
51
+ title: string;
52
+ content: string;
53
+ tagId: string;
54
+ status?: 'active' | 'inactive' | 'draft';
55
+ metadata?: string;
56
+ }
57
+
58
+ // Update resource DTO
59
+ export interface UpdateResourceData extends Partial<CreateResourceData> {
60
+ id: string;
61
+ }
@@ -48,6 +48,7 @@
48
48
  "expo-checkbox": "~5.0.7",
49
49
  "expo-constants": "~18.0.9",
50
50
  "expo-dev-client": "~6.0.18",
51
+ "expo-document-picker": "^14.0.7",
51
52
  "expo-file-system": "~19.0.17",
52
53
  "expo-font": "~14.0.8",
53
54
  "expo-haptics": "~15.0.7",
@@ -36,85 +36,5 @@
36
36
  "createdAt": "2024-02-01T08:00:00.000Z",
37
37
  "updatedAt": "2024-02-01T08:00:00.000Z"
38
38
  }
39
- ],
40
- "categories": [
41
- {
42
- "id": "1",
43
- "name": "Electronics",
44
- "slug": "electronics",
45
- "description": "Electronic devices and accessories",
46
- "icon": "📱",
47
- "color": "#3b82f6"
48
- },
49
- {
50
- "id": "2",
51
- "name": "Food & Beverage",
52
- "slug": "food-beverage",
53
- "description": "Food items and beverages",
54
- "icon": "🍔",
55
- "color": "#ef4444"
56
- },
57
- {
58
- "id": "3",
59
- "name": "Clothing",
60
- "slug": "clothing",
61
- "description": "Clothing and fashion items",
62
- "icon": "👕",
63
- "color": "#8b5cf6"
64
- }
65
- ],
66
- "products": [
67
- {
68
- "id": "1",
69
- "name": "Smartphone",
70
- "categoryId": "1",
71
- "unit": "pcs",
72
- "stockSystem": 50,
73
- "stockPhysical": 48,
74
- "minStock": 10,
75
- "price": 5000000,
76
- "description": "Latest smartphone with advanced features",
77
- "createdAt": "2024-01-10T00:00:00.000Z",
78
- "updatedAt": "2024-01-10T00:00:00.000Z"
79
- },
80
- {
81
- "id": "2",
82
- "name": "Wireless Earbuds",
83
- "categoryId": "1",
84
- "unit": "pcs",
85
- "stockSystem": 8,
86
- "stockPhysical": 8,
87
- "minStock": 15,
88
- "price": 500000,
89
- "description": "Bluetooth earbuds with noise cancellation (Low Stock)",
90
- "createdAt": "2024-01-15T00:00:00.000Z",
91
- "updatedAt": "2024-01-15T00:00:00.000Z"
92
- },
93
- {
94
- "id": "3",
95
- "name": "Mineral Water",
96
- "categoryId": "2",
97
- "unit": "bottle",
98
- "stockSystem": 200,
99
- "stockPhysical": 195,
100
- "minStock": 50,
101
- "price": 5000,
102
- "description": "Pure mineral water 600ml",
103
- "createdAt": "2024-01-20T00:00:00.000Z",
104
- "updatedAt": "2024-01-20T00:00:00.000Z"
105
- },
106
- {
107
- "id": "4",
108
- "name": "T-Shirt",
109
- "categoryId": "3",
110
- "unit": "pcs",
111
- "stockSystem": 100,
112
- "stockPhysical": 98,
113
- "minStock": 20,
114
- "price": 150000,
115
- "description": "Cotton t-shirt, various colors",
116
- "createdAt": "2024-01-25T00:00:00.000Z",
117
- "updatedAt": "2024-01-25T00:00:00.000Z"
118
- }
119
39
  ]
120
40
  }
@@ -1,7 +1,5 @@
1
1
  import * as authHandlers from './auth.mock';
2
2
  import * as userHandlers from './users.mock';
3
- import * as productHandlers from './products.mock';
4
- import * as categoryHandlers from './categories.mock';
5
3
 
6
4
  const delay = (ms = 150) => new Promise(r => setTimeout(r, ms));
7
5
 
@@ -15,12 +13,6 @@ export const mockApi = {
15
13
  if (url.startsWith('/users')) {
16
14
  return userHandlers.get(url, config);
17
15
  }
18
- if (url.startsWith('/products')) {
19
- return productHandlers.get(url, config);
20
- }
21
- if (url.startsWith('/categories')) {
22
- return categoryHandlers.get(url, config);
23
- }
24
16
 
25
17
  throw new Error(`Unknown GET endpoint: ${url}`);
26
18
  },
@@ -34,12 +26,6 @@ export const mockApi = {
34
26
  if (url.startsWith('/users')) {
35
27
  return userHandlers.post(url, body);
36
28
  }
37
- if (url.startsWith('/products')) {
38
- return productHandlers.post(url, body);
39
- }
40
- if (url.startsWith('/categories')) {
41
- return categoryHandlers.post(url, body);
42
- }
43
29
 
44
30
 
45
31
  throw new Error(`Unknown POST endpoint: ${url}`);
@@ -51,12 +37,6 @@ export const mockApi = {
51
37
  if (url.startsWith('/users/')) {
52
38
  return userHandlers.put(url, body);
53
39
  }
54
- if (url.startsWith('/products/')) {
55
- return productHandlers.put(url, body);
56
- }
57
- if (url.startsWith('/categories/')) {
58
- return categoryHandlers.put(url, body);
59
- }
60
40
 
61
41
  throw new Error(`Unknown PUT endpoint: ${url}`);
62
42
  },
@@ -67,12 +47,6 @@ export const mockApi = {
67
47
  if (url.startsWith('/users/')) {
68
48
  return userHandlers.del(url);
69
49
  }
70
- if (url.startsWith('/products/')) {
71
- return productHandlers.del(url);
72
- }
73
- if (url.startsWith('/categories/')) {
74
- return categoryHandlers.del(url);
75
- }
76
50
 
77
51
  throw new Error(`Unknown DELETE endpoint: ${url}`);
78
52
  },
@@ -1,75 +0,0 @@
1
- import { Category } from '@/types/product.types';
2
- import * as db from 'server/db.json';
3
-
4
- let categories: Category[] = [...db.categories];
5
-
6
- export async function get<T = any>(url: string, config?: any): Promise<T> {
7
- if (url === '/categories') {
8
- return categories as unknown as T;
9
- }
10
-
11
- // Get single category
12
- const match = url.match(/\/categories\/(\w+)/);
13
- if (match) {
14
- const category = categories.find(c => c.id === match[1]);
15
- if (!category) throw new Error('Category not found');
16
- return category as unknown as T;
17
- }
18
-
19
- throw new Error(`Unknown GET /categories endpoint: ${url}`);
20
- }
21
-
22
- export async function post<T = any>(url: string, body?: Partial<Category>): Promise<T> {
23
- if (url === '/categories') {
24
- if (!body) throw new Error('Category data is required');
25
-
26
- const newCategory: Category = {
27
- id: String(Date.now()),
28
- name: body.name || '',
29
- slug: body.slug || body.name?.toLowerCase().replace(/\s+/g, '-') || '',
30
- description: body.description || '',
31
- icon: body.icon || '📦',
32
- color: body.color || '#6366f1',
33
- };
34
-
35
- categories.push(newCategory);
36
- return newCategory as unknown as T;
37
- }
38
-
39
- throw new Error(`Unknown POST /categories endpoint: ${url}`);
40
- }
41
-
42
- export async function put<T = any>(url: string, body: Partial<Category>): Promise<T> {
43
- const match = url.match(/\/categories\/(\w+)/);
44
- if (match) {
45
- const id = match[1];
46
- const index = categories.findIndex(c => c.id === id);
47
- if (index === -1) throw new Error('Category not found');
48
-
49
- categories[index] = {
50
- ...categories[index],
51
- ...body
52
- };
53
-
54
- return categories[index] as unknown as T;
55
- }
56
-
57
- throw new Error(`Unknown PUT /categories endpoint: ${url}`);
58
- }
59
-
60
- export async function del<T = any>(url: string): Promise<T> {
61
- const match = url.match(/\/categories\/(\w+)/);
62
- if (match) {
63
- const id = match[1];
64
- const initialLength = categories.length;
65
- categories = categories.filter(c => c.id !== id);
66
-
67
- if (categories.length === initialLength) {
68
- throw new Error('Category not found');
69
- }
70
-
71
- return { message: 'Category deleted successfully' } as unknown as T;
72
- }
73
-
74
- throw new Error(`Unknown DELETE /categories endpoint: ${url}`);
75
- }
@@ -1,125 +0,0 @@
1
- // products.mock.ts
2
- import { CreateProductData, Product, ProductFilters, UpdateProductData } from '@/types/product.types';
3
- import * as db from 'server/db.json';
4
-
5
- let products: Product[] = [...db.products];
6
-
7
- export async function get<T = any>(url: string, config?: { params?: ProductFilters }): Promise<T> {
8
- if (url === '/products') {
9
- const { params } = config || {};
10
- let filtered = [...products];
11
-
12
- if (params) {
13
- const { search, categoryId, lowStock, sortBy, limit = 10, page = 1 } = params;
14
-
15
- // Filter by search
16
- if (search) {
17
- const q = search.toLowerCase();
18
- filtered = filtered.filter(
19
- p => p.name.toLowerCase().includes(q) ||
20
- p.description?.toLowerCase().includes(q)
21
- );
22
- }
23
-
24
- // Filter by category
25
- if (categoryId) {
26
- filtered = filtered.filter(p => p.categoryId === categoryId);
27
- }
28
-
29
- // Filter by low stock
30
- if (lowStock) {
31
- filtered = filtered.filter(p => p.stockSystem <= p.minStock);
32
- }
33
-
34
- // Sort
35
- if (sortBy === 'latest') {
36
- filtered.sort((a, b) => +new Date(b.createdAt) - +new Date(a.createdAt));
37
- } else if (sortBy === 'oldest') {
38
- filtered.sort((a, b) => +new Date(a.createdAt) - +new Date(b.createdAt));
39
- } else if (sortBy === 'name') {
40
- filtered.sort((a, b) => a.name.localeCompare(b.name));
41
- } else if (sortBy === 'price-asc') {
42
- filtered.sort((a, b) => a.price - b.price);
43
- } else if (sortBy === 'price-desc') {
44
- filtered.sort((a, b) => b.price - a.price);
45
- }
46
-
47
- // Pagination
48
- const start = (page - 1) * limit;
49
- return filtered.slice(start, start + limit) as unknown as T;
50
- }
51
-
52
- return products as unknown as T;
53
- }
54
-
55
- // Get single product
56
- const match = url.match(/\/products\/(\w+)/);
57
- if (match) {
58
- const product = products.find(p => p.id === match[1]);
59
- if (!product) throw new Error('Product not found');
60
- return product as unknown as T;
61
- }
62
-
63
- throw new Error(`Unknown GET /products endpoint: ${url}`);
64
- }
65
-
66
- export async function post<T = any>(url: string, body?: CreateProductData): Promise<T> {
67
- if (url === '/products') {
68
- if (!body) throw new Error('Product data is required');
69
-
70
- const newProduct: Product = {
71
- id: String(Date.now()),
72
- name: body.name,
73
- categoryId: body.categoryId,
74
- unit: body.unit,
75
- stockSystem: body.stockSystem,
76
- stockPhysical: body.stockSystem, // Initial physical stock same as system
77
- minStock: body.minStock,
78
- price: body.price,
79
- description: body.description || '',
80
- createdAt: new Date().toISOString(),
81
- updatedAt: new Date().toISOString(),
82
- };
83
-
84
- products.push(newProduct);
85
- return newProduct as unknown as T;
86
- }
87
-
88
- throw new Error(`Unknown POST /products endpoint: ${url}`);
89
- }
90
-
91
- export async function put<T = any>(url: string, body: UpdateProductData): Promise<T> {
92
- const match = url.match(/\/products\/(\w+)/);
93
- if (match) {
94
- const id = match[1];
95
- const index = products.findIndex(p => p.id === id);
96
- if (index === -1) throw new Error('Product not found');
97
-
98
- products[index] = {
99
- ...products[index],
100
- ...body,
101
- updatedAt: new Date().toISOString()
102
- };
103
-
104
- return products[index] as unknown as T;
105
- }
106
-
107
- throw new Error(`Unknown PUT /products endpoint: ${url}`);
108
- }
109
-
110
- export async function del<T = any>(url: string): Promise<T> {
111
- const match = url.match(/\/products\/(\w+)/);
112
- if (match) {
113
- const id = match[1];
114
- const initialLength = products.length;
115
- products = products.filter(p => p.id !== id);
116
-
117
- if (products.length === initialLength) {
118
- throw new Error('Product not found');
119
- }
120
-
121
- return { message: 'Product deleted successfully' } as unknown as T;
122
- }
123
-
124
- throw new Error(`Unknown DELETE /products endpoint: ${url}`);
125
- }
@@ -1,49 +0,0 @@
1
- export interface Category {
2
- id: string;
3
- name: string;
4
- slug: string;
5
- description: string;
6
- icon: string;
7
- color: string;
8
- }
9
-
10
- export interface Product {
11
- id: string;
12
- name: string;
13
- categoryId: string;
14
- unit: string;
15
- stockSystem: number;
16
- stockPhysical: number;
17
- minStock: number;
18
- price: number;
19
- description: string;
20
- createdAt: string;
21
- updatedAt: string;
22
- }
23
-
24
- export interface ProductFilters {
25
- search?: string;
26
- categoryId?: string;
27
- lowStock?: boolean;
28
- sortBy?: 'latest' | 'oldest' | 'name' | 'price-asc' | 'price-desc';
29
- limit?: number;
30
- page?: number;
31
- }
32
-
33
- export interface ProductWithCategory extends Product {
34
- category?: Category;
35
- }
36
-
37
- export interface CreateProductData {
38
- name: string;
39
- categoryId: string;
40
- unit: string;
41
- stockSystem: number;
42
- minStock: number;
43
- price: number;
44
- description?: string;
45
- }
46
-
47
- export interface UpdateProductData extends Partial<CreateProductData> {
48
- id: string;
49
- }