create-ng-tailwind 3.0.1 → 4.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.
Files changed (47) hide show
  1. package/CHANGELOG.md +81 -344
  2. package/README.md +93 -157
  3. package/lib/cli/index.js +29 -3
  4. package/lib/cli/interactive.js +26 -1
  5. package/lib/managers/ProjectManager.js +0 -4
  6. package/lib/templates/base/components.js +243 -0
  7. package/lib/templates/base/index.js +207 -0
  8. package/lib/templates/base/infrastructure.js +314 -0
  9. package/lib/templates/base/linting.js +359 -0
  10. package/lib/templates/base/pwa.js +103 -0
  11. package/lib/templates/base/services.js +362 -0
  12. package/lib/templates/blog/app.js +250 -0
  13. package/lib/templates/blog/components.js +360 -0
  14. package/lib/templates/blog/i18n.js +77 -0
  15. package/lib/templates/blog/index.js +126 -0
  16. package/lib/templates/blog/pages.js +554 -0
  17. package/lib/templates/blog/services.js +390 -0
  18. package/lib/templates/dashboard/app.js +320 -0
  19. package/lib/templates/dashboard/charts.js +305 -0
  20. package/lib/templates/dashboard/components.js +410 -0
  21. package/lib/templates/dashboard/i18n.js +340 -0
  22. package/lib/templates/dashboard/index.js +141 -0
  23. package/lib/templates/dashboard/layout.js +310 -0
  24. package/lib/templates/dashboard/pages.js +681 -0
  25. package/lib/templates/ecommerce/app.js +315 -0
  26. package/lib/templates/ecommerce/components.js +496 -0
  27. package/lib/templates/ecommerce/i18n.js +389 -0
  28. package/lib/templates/ecommerce/index.js +152 -0
  29. package/lib/templates/ecommerce/layout.js +270 -0
  30. package/lib/templates/ecommerce/pages.js +969 -0
  31. package/lib/templates/ecommerce/services.js +300 -0
  32. package/lib/templates/index.js +12 -0
  33. package/lib/templates/landing/index.js +1117 -0
  34. package/lib/templates/portfolio/index.js +1160 -0
  35. package/lib/templates/saas/index.js +1371 -0
  36. package/lib/templates/starter/app.js +364 -0
  37. package/lib/templates/starter/i18n.js +856 -0
  38. package/lib/templates/starter/index.js +53 -4055
  39. package/lib/templates/starter/layout.js +852 -0
  40. package/lib/templates/starter/pages.js +1241 -0
  41. package/package.json +1 -1
  42. package/lib/templates/starter/features.js +0 -867
  43. package/lib/utils/ai-config.js +0 -641
  44. /package/lib/templates/{starter → base}/advanced-features.js +0 -0
  45. /package/lib/templates/{starter → base}/seo-assets.js +0 -0
  46. /package/lib/templates/{starter → base}/seo-features.js +0 -0
  47. /package/lib/templates/{starter → base}/ui-features.js +0 -0
@@ -0,0 +1,681 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * Create dashboard pages (Overview, Analytics, Users, Orders, Settings)
6
+ */
7
+ async function createPages(config) {
8
+ // Overview Page
9
+ const overviewComponent = `import { Component, inject } from '@angular/core';
10
+ import { TranslateModule } from '@ngx-translate/core';
11
+ import { StatsCardComponent } from '@shared/components/stats-card/stats-card.component';
12
+ import { ChartBarComponent, BarChartData } from '@shared/components/chart-bar/chart-bar.component';
13
+ import { ChartLineComponent, LineChartData } from '@shared/components/chart-line/chart-line.component';
14
+ import { ChartDonutComponent, DonutChartData } from '@shared/components/chart-donut/chart-donut.component';
15
+ import { DataTableComponent, TableColumn } from '@shared/components/data-table/data-table.component';
16
+ import { BreadcrumbComponent } from '@shared/components/breadcrumb/breadcrumb.component';
17
+ import { TranslationService } from '@core/i18n/translation.service';
18
+
19
+ @Component({
20
+ selector: 'app-overview',
21
+ standalone: true,
22
+ imports: [
23
+ TranslateModule,
24
+ StatsCardComponent,
25
+ ChartBarComponent,
26
+ ChartLineComponent,
27
+ ChartDonutComponent,
28
+ DataTableComponent,
29
+ BreadcrumbComponent,
30
+ ],
31
+ template: \`
32
+ <div class="space-y-6">
33
+ <!-- Header -->
34
+ <div>
35
+ <app-breadcrumb [items]="[{ label: ('dashboard.overview' | translate) }]"></app-breadcrumb>
36
+ <h1 class="mt-2 text-2xl font-bold text-gray-900">{{ 'dashboard.title' | translate }}</h1>
37
+ <p class="text-gray-500">{{ 'dashboard.welcome' | translate }}</p>
38
+ </div>
39
+
40
+ <!-- Stats Cards -->
41
+ <div class="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-4">
42
+ <app-stats-card
43
+ [label]="'stats.totalRevenue' | translate"
44
+ value="$45,231"
45
+ icon="heroCurrencyDollar"
46
+ iconBgClass="bg-linear-to-br from-emerald-500 to-green-600 shadow-lg shadow-emerald-500/30"
47
+ iconColor="white"
48
+ gradientClass="from-emerald-50/50 to-green-50/50"
49
+ [trend]="12.5">
50
+ </app-stats-card>
51
+ <app-stats-card
52
+ [label]="'stats.totalUsers' | translate"
53
+ value="2,350"
54
+ icon="heroUsers"
55
+ iconBgClass="bg-linear-to-br from-blue-500 to-indigo-600 shadow-lg shadow-blue-500/30"
56
+ iconColor="white"
57
+ gradientClass="from-blue-50/50 to-indigo-50/50"
58
+ [trend]="8.2">
59
+ </app-stats-card>
60
+ <app-stats-card
61
+ [label]="'stats.totalOrders' | translate"
62
+ value="1,247"
63
+ icon="heroShoppingCart"
64
+ iconBgClass="bg-linear-to-br from-amber-500 to-orange-600 shadow-lg shadow-amber-500/30"
65
+ iconColor="white"
66
+ gradientClass="from-amber-50/50 to-orange-50/50"
67
+ [trend]="-2.4">
68
+ </app-stats-card>
69
+ <app-stats-card
70
+ [label]="'stats.conversionRate' | translate"
71
+ value="3.2%"
72
+ icon="heroChartBar"
73
+ iconBgClass="bg-linear-to-br from-violet-500 to-purple-600 shadow-lg shadow-violet-500/30"
74
+ iconColor="white"
75
+ gradientClass="from-violet-50/50 to-purple-50/50"
76
+ [trend]="4.1">
77
+ </app-stats-card>
78
+ </div>
79
+
80
+ <!-- Charts Row -->
81
+ <div class="grid grid-cols-1 gap-6 lg:grid-cols-3">
82
+ <!-- Line Chart -->
83
+ <div class="col-span-2 rounded-xl border border-gray-200 bg-white p-6 shadow-sm">
84
+ <app-chart-line
85
+ [title]="'charts.revenueOverTime' | translate"
86
+ [data]="revenueData"
87
+ lineColor="#3b82f6">
88
+ </app-chart-line>
89
+ </div>
90
+
91
+ <!-- Donut Chart -->
92
+ <div class="rounded-xl border border-gray-200 bg-white p-6 shadow-sm">
93
+ <app-chart-donut
94
+ [title]="'charts.trafficSources' | translate"
95
+ [data]="trafficData">
96
+ </app-chart-donut>
97
+ </div>
98
+ </div>
99
+
100
+ <!-- Bar Chart & Recent Orders -->
101
+ <div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
102
+ <!-- Bar Chart -->
103
+ <div class="rounded-xl border border-gray-200 bg-white p-6 shadow-sm">
104
+ <app-chart-bar
105
+ [title]="'charts.salesByCategory' | translate"
106
+ [data]="salesData">
107
+ </app-chart-bar>
108
+ </div>
109
+
110
+ <!-- Recent Orders Table -->
111
+ <div class="rounded-xl border border-gray-200 bg-white p-6 shadow-sm">
112
+ <h3 class="mb-4 text-lg font-semibold text-gray-900">{{ 'table.orderId' | translate }}</h3>
113
+ <app-data-table
114
+ [data]="recentOrders"
115
+ [columns]="orderColumns"
116
+ [searchable]="false"
117
+ [paginate]="false"
118
+ [pageSize]="5">
119
+ </app-data-table>
120
+ </div>
121
+ </div>
122
+ </div>
123
+ \`,
124
+ })
125
+ export class OverviewComponent {
126
+ revenueData: LineChartData[] = [
127
+ { label: 'Jan', value: 4000 },
128
+ { label: 'Feb', value: 3000 },
129
+ { label: 'Mar', value: 5000 },
130
+ { label: 'Apr', value: 4500 },
131
+ { label: 'May', value: 6000 },
132
+ { label: 'Jun', value: 5500 },
133
+ { label: 'Jul', value: 7000 },
134
+ ];
135
+
136
+ trafficData: DonutChartData[] = [
137
+ { label: 'Direct', value: 4500, color: '#3b82f6' },
138
+ { label: 'Organic', value: 3200, color: '#10b981' },
139
+ { label: 'Referral', value: 2100, color: '#f59e0b' },
140
+ { label: 'Social', value: 1800, color: '#8b5cf6' },
141
+ ];
142
+
143
+ salesData: BarChartData[] = [
144
+ { label: 'Electronics', value: 12500, color: '#3b82f6' },
145
+ { label: 'Clothing', value: 9800, color: '#10b981' },
146
+ { label: 'Home', value: 7600, color: '#f59e0b' },
147
+ { label: 'Books', value: 5400, color: '#8b5cf6' },
148
+ { label: 'Sports', value: 4200, color: '#ec4899' },
149
+ ];
150
+
151
+ orderColumns: TableColumn[] = [
152
+ { key: 'id', label: 'Order ID', width: '100px' },
153
+ { key: 'customer', label: 'Customer' },
154
+ { key: 'amount', label: 'Amount', type: 'number' },
155
+ { key: 'status', label: 'Status', type: 'status' },
156
+ ];
157
+
158
+ recentOrders = [
159
+ { id: '#3210', customer: 'John Doe', amount: 125.00, status: 'Completed' },
160
+ { id: '#3209', customer: 'Jane Smith', amount: 89.50, status: 'Processing' },
161
+ { id: '#3208', customer: 'Bob Wilson', amount: 234.00, status: 'Pending' },
162
+ { id: '#3207', customer: 'Alice Brown', amount: 156.75, status: 'Completed' },
163
+ { id: '#3206', customer: 'Charlie Davis', amount: 67.25, status: 'Cancelled' },
164
+ ];
165
+ }`;
166
+
167
+ await fs.writeFile(
168
+ path.join(config.fullPath, 'src/app/features/dashboard/overview/overview.component.ts'),
169
+ overviewComponent
170
+ );
171
+
172
+ // Analytics Page
173
+ const analyticsComponent = `import { Component } from '@angular/core';
174
+ import { TranslateModule } from '@ngx-translate/core';
175
+ import { ChartLineComponent, LineChartData } from '@shared/components/chart-line/chart-line.component';
176
+ import { ChartBarComponent, BarChartData } from '@shared/components/chart-bar/chart-bar.component';
177
+ import { StatsCardComponent } from '@shared/components/stats-card/stats-card.component';
178
+ import { BreadcrumbComponent } from '@shared/components/breadcrumb/breadcrumb.component';
179
+
180
+ @Component({
181
+ selector: 'app-analytics',
182
+ standalone: true,
183
+ imports: [
184
+ TranslateModule,
185
+ ChartLineComponent,
186
+ ChartBarComponent,
187
+ StatsCardComponent,
188
+ BreadcrumbComponent,
189
+ ],
190
+ template: \`
191
+ <div class="space-y-6">
192
+ <div>
193
+ <app-breadcrumb [items]="[{ label: ('dashboard.analytics' | translate) }]"></app-breadcrumb>
194
+ <h1 class="mt-2 text-2xl font-bold text-gray-900">{{ 'dashboard.analytics' | translate }}</h1>
195
+ <p class="text-gray-500">{{ 'dashboard.welcome' | translate }}</p>
196
+ </div>
197
+
198
+ <!-- Stats -->
199
+ <div class="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-4">
200
+ <app-stats-card
201
+ [label]="'stats.pageViews' | translate"
202
+ value="124,592"
203
+ icon="heroEye"
204
+ iconBgClass="bg-linear-to-br from-cyan-500 to-blue-600 shadow-lg shadow-cyan-500/30"
205
+ iconColor="white"
206
+ gradientClass="from-cyan-50/50 to-blue-50/50"
207
+ [trend]="15.3">
208
+ </app-stats-card>
209
+ <app-stats-card
210
+ [label]="'stats.uniqueVisitors' | translate"
211
+ value="48,352"
212
+ icon="heroUserGroup"
213
+ iconBgClass="bg-linear-to-br from-teal-500 to-emerald-600 shadow-lg shadow-teal-500/30"
214
+ iconColor="white"
215
+ gradientClass="from-teal-50/50 to-emerald-50/50"
216
+ [trend]="8.7">
217
+ </app-stats-card>
218
+ <app-stats-card
219
+ [label]="'stats.avgSession' | translate"
220
+ value="4m 32s"
221
+ icon="heroClock"
222
+ iconBgClass="bg-linear-to-br from-amber-500 to-yellow-600 shadow-lg shadow-amber-500/30"
223
+ iconColor="white"
224
+ gradientClass="from-amber-50/50 to-yellow-50/50"
225
+ [trend]="2.1">
226
+ </app-stats-card>
227
+ <app-stats-card
228
+ [label]="'stats.bounceRate' | translate"
229
+ value="32.4%"
230
+ icon="heroArrowPath"
231
+ iconBgClass="bg-linear-to-br from-rose-500 to-red-600 shadow-lg shadow-rose-500/30"
232
+ iconColor="white"
233
+ gradientClass="from-rose-50/50 to-red-50/50"
234
+ [trend]="-5.2">
235
+ </app-stats-card>
236
+ </div>
237
+
238
+ <!-- Charts -->
239
+ <div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
240
+ <div class="rounded-xl border border-gray-200 bg-white p-6 shadow-sm">
241
+ <app-chart-line
242
+ [title]="'charts.visitorsOverTime' | translate"
243
+ [data]="visitorsData"
244
+ lineColor="#10b981">
245
+ </app-chart-line>
246
+ </div>
247
+ <div class="rounded-xl border border-gray-200 bg-white p-6 shadow-sm">
248
+ <app-chart-bar
249
+ [title]="'charts.topPages' | translate"
250
+ [data]="pagesData">
251
+ </app-chart-bar>
252
+ </div>
253
+ </div>
254
+ </div>
255
+ \`,
256
+ })
257
+ export class AnalyticsComponent {
258
+ visitorsData: LineChartData[] = [
259
+ { label: 'Mon', value: 1200 },
260
+ { label: 'Tue', value: 1900 },
261
+ { label: 'Wed', value: 1500 },
262
+ { label: 'Thu', value: 2200 },
263
+ { label: 'Fri', value: 2800 },
264
+ { label: 'Sat', value: 2100 },
265
+ { label: 'Sun', value: 1600 },
266
+ ];
267
+
268
+ pagesData: BarChartData[] = [
269
+ { label: 'Homepage', value: 45000, color: '#3b82f6' },
270
+ { label: 'Products', value: 32000, color: '#10b981' },
271
+ { label: 'About', value: 18000, color: '#f59e0b' },
272
+ { label: 'Contact', value: 12000, color: '#8b5cf6' },
273
+ { label: 'Blog', value: 9500, color: '#ec4899' },
274
+ ];
275
+ }`;
276
+
277
+ await fs.writeFile(
278
+ path.join(config.fullPath, 'src/app/features/dashboard/analytics/analytics.component.ts'),
279
+ analyticsComponent
280
+ );
281
+
282
+ // Users Page
283
+ const usersComponent = `import { Component, inject } from '@angular/core';
284
+ import { ReactiveFormsModule, FormBuilder, Validators } from '@angular/forms';
285
+ import { TranslateModule } from '@ngx-translate/core';
286
+ import { DataTableComponent, TableColumn } from '@shared/components/data-table/data-table.component';
287
+ import { BreadcrumbComponent } from '@shared/components/breadcrumb/breadcrumb.component';
288
+ import { ButtonComponent } from '@shared/components/button/button.component';
289
+ import { NgIconComponent, provideIcons } from '@ng-icons/core';
290
+ import { heroPlus, heroXMark } from '@ng-icons/heroicons/outline';
291
+
292
+ @Component({
293
+ selector: 'app-users',
294
+ standalone: true,
295
+ imports: [ReactiveFormsModule, TranslateModule, DataTableComponent, BreadcrumbComponent, ButtonComponent, NgIconComponent],
296
+ viewProviders: [provideIcons({ heroPlus, heroXMark })],
297
+ template: \`
298
+ <div class="space-y-6">
299
+ <div class="flex items-center justify-between">
300
+ <div>
301
+ <app-breadcrumb [items]="[{ label: ('dashboard.users' | translate) }]"></app-breadcrumb>
302
+ <h1 class="mt-2 text-2xl font-bold text-gray-900">{{ 'dashboard.users' | translate }}</h1>
303
+ <p class="text-gray-500">{{ 'dashboard.welcome' | translate }}</p>
304
+ </div>
305
+ <app-button (click)="openAddUserModal()">
306
+ <ng-icon name="heroPlus" size="18" class="me-2"></ng-icon>
307
+ {{ 'actions.addUser' | translate }}
308
+ </app-button>
309
+ </div>
310
+
311
+ <app-data-table
312
+ [data]="users"
313
+ [columns]="columns"
314
+ [pageSize]="10">
315
+ </app-data-table>
316
+ </div>
317
+
318
+ <!-- Add User Modal -->
319
+ @if (showModal) {
320
+ <div class="fixed inset-0 z-50 flex items-center justify-center">
321
+ <div class="absolute inset-0 bg-black/50" (click)="closeModal()"></div>
322
+ <div class="relative w-full max-w-md rounded-xl bg-white p-6 shadow-xl">
323
+ <div class="mb-6 flex items-center justify-between">
324
+ <h2 class="text-xl font-semibold text-gray-900">{{ 'actions.addUser' | translate }}</h2>
325
+ <button type="button" (click)="closeModal()" class="rounded-lg p-1 text-gray-400 hover:bg-gray-100 hover:text-gray-600">
326
+ <ng-icon name="heroXMark" size="24"></ng-icon>
327
+ </button>
328
+ </div>
329
+ <form [formGroup]="userForm" (ngSubmit)="onSubmit()" class="space-y-4">
330
+ <div>
331
+ <label for="userName" class="block text-sm font-medium text-gray-700">{{ 'settings.fullName' | translate }}</label>
332
+ <input id="userName" type="text" formControlName="name"
333
+ class="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500"
334
+ [class.border-danger-500]="userForm.get('name')?.invalid && userForm.get('name')?.touched" />
335
+ @if (userForm.get('name')?.invalid && userForm.get('name')?.touched) {
336
+ <p class="mt-1 text-sm text-danger-600">Name is required (min 2 characters)</p>
337
+ }
338
+ </div>
339
+ <div>
340
+ <label for="userEmail" class="block text-sm font-medium text-gray-700">{{ 'settings.email' | translate }}</label>
341
+ <input id="userEmail" type="email" formControlName="email"
342
+ class="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500"
343
+ [class.border-danger-500]="userForm.get('email')?.invalid && userForm.get('email')?.touched" />
344
+ @if (userForm.get('email')?.invalid && userForm.get('email')?.touched) {
345
+ <p class="mt-1 text-sm text-danger-600">Valid email is required</p>
346
+ }
347
+ </div>
348
+ <div>
349
+ <label for="userRole" class="block text-sm font-medium text-gray-700">{{ 'table.role' | translate }}</label>
350
+ <select id="userRole" formControlName="role"
351
+ class="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500">
352
+ <option value="">Select a role</option>
353
+ <option value="Admin">Admin</option>
354
+ <option value="Editor">Editor</option>
355
+ <option value="User">User</option>
356
+ </select>
357
+ @if (userForm.get('role')?.invalid && userForm.get('role')?.touched) {
358
+ <p class="mt-1 text-sm text-danger-600">Role is required</p>
359
+ }
360
+ </div>
361
+ <div>
362
+ <label for="userStatus" class="block text-sm font-medium text-gray-700">{{ 'table.status' | translate }}</label>
363
+ <select id="userStatus" formControlName="status"
364
+ class="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500">
365
+ <option value="Active">Active</option>
366
+ <option value="Inactive">Inactive</option>
367
+ <option value="Pending">Pending</option>
368
+ </select>
369
+ </div>
370
+ <div class="mt-6 flex justify-end gap-3">
371
+ <app-button type="button" variant="secondary" (click)="closeModal()">{{ 'actions.cancel' | translate }}</app-button>
372
+ <app-button type="submit" [disabled]="userForm.invalid">{{ 'actions.addUser' | translate }}</app-button>
373
+ </div>
374
+ </form>
375
+ </div>
376
+ </div>
377
+ }
378
+ \`,
379
+ })
380
+ export class UsersComponent {
381
+ private fb = inject(FormBuilder);
382
+ showModal = false;
383
+
384
+ userForm = this.fb.group({
385
+ name: ['', [Validators.required, Validators.minLength(2)]],
386
+ email: ['', [Validators.required, Validators.email]],
387
+ role: ['', Validators.required],
388
+ status: ['Active'],
389
+ });
390
+ columns: TableColumn[] = [
391
+ { key: 'id', label: 'ID', width: '80px', sortable: true },
392
+ { key: 'name', label: 'Name', sortable: true },
393
+ { key: 'email', label: 'Email', sortable: true },
394
+ { key: 'role', label: 'Role', sortable: true },
395
+ { key: 'status', label: 'Status', type: 'status' },
396
+ { key: 'createdAt', label: 'Joined', type: 'date', sortable: true },
397
+ ];
398
+
399
+ users = [
400
+ { id: 1, name: 'John Doe', email: 'john@example.com', role: 'Admin', status: 'Active', createdAt: '2024-01-15' },
401
+ { id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'Editor', status: 'Active', createdAt: '2024-02-20' },
402
+ { id: 3, name: 'Bob Wilson', email: 'bob@example.com', role: 'User', status: 'Inactive', createdAt: '2024-03-10' },
403
+ { id: 4, name: 'Alice Brown', email: 'alice@example.com', role: 'User', status: 'Active', createdAt: '2024-03-15' },
404
+ { id: 5, name: 'Charlie Davis', email: 'charlie@example.com', role: 'Editor', status: 'Active', createdAt: '2024-04-01' },
405
+ { id: 6, name: 'Diana Miller', email: 'diana@example.com', role: 'User', status: 'Pending', createdAt: '2024-04-10' },
406
+ { id: 7, name: 'Edward Jones', email: 'edward@example.com', role: 'Admin', status: 'Active', createdAt: '2024-04-15' },
407
+ { id: 8, name: 'Fiona Garcia', email: 'fiona@example.com', role: 'User', status: 'Active', createdAt: '2024-05-01' },
408
+ { id: 9, name: 'George Martinez', email: 'george@example.com', role: 'User', status: 'Inactive', createdAt: '2024-05-10' },
409
+ { id: 10, name: 'Helen Anderson', email: 'helen@example.com', role: 'Editor', status: 'Active', createdAt: '2024-05-15' },
410
+ { id: 11, name: 'Ivan Taylor', email: 'ivan@example.com', role: 'User', status: 'Active', createdAt: '2024-06-01' },
411
+ { id: 12, name: 'Julia Thomas', email: 'julia@example.com', role: 'User', status: 'Pending', createdAt: '2024-06-10' },
412
+ ];
413
+
414
+ openAddUserModal(): void {
415
+ this.userForm.reset({ status: 'Active' });
416
+ this.showModal = true;
417
+ }
418
+
419
+ closeModal(): void {
420
+ this.showModal = false;
421
+ }
422
+
423
+ onSubmit(): void {
424
+ if (this.userForm.valid) {
425
+ const newUser = {
426
+ id: this.users.length + 1,
427
+ ...this.userForm.value,
428
+ createdAt: new Date().toISOString().split('T')[0],
429
+ };
430
+ this.users = [newUser as typeof this.users[0], ...this.users];
431
+ this.closeModal();
432
+ }
433
+ }
434
+ }`;
435
+
436
+ await fs.writeFile(
437
+ path.join(config.fullPath, 'src/app/features/dashboard/users/users.component.ts'),
438
+ usersComponent
439
+ );
440
+
441
+ // Orders Page
442
+ const ordersComponent = `import { Component } from '@angular/core';
443
+ import { TranslateModule } from '@ngx-translate/core';
444
+ import { DataTableComponent, TableColumn } from '@shared/components/data-table/data-table.component';
445
+ import { BreadcrumbComponent } from '@shared/components/breadcrumb/breadcrumb.component';
446
+ import { StatsCardComponent } from '@shared/components/stats-card/stats-card.component';
447
+
448
+ @Component({
449
+ selector: 'app-orders',
450
+ standalone: true,
451
+ imports: [TranslateModule, DataTableComponent, BreadcrumbComponent, StatsCardComponent],
452
+ template: \`
453
+ <div class="space-y-6">
454
+ <div>
455
+ <app-breadcrumb [items]="[{ label: ('dashboard.orders' | translate) }]"></app-breadcrumb>
456
+ <h1 class="mt-2 text-2xl font-bold text-gray-900">{{ 'dashboard.orders' | translate }}</h1>
457
+ <p class="text-gray-500">{{ 'dashboard.welcome' | translate }}</p>
458
+ </div>
459
+
460
+ <!-- Order Stats -->
461
+ <div class="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-4">
462
+ <app-stats-card
463
+ [label]="'stats.totalOrders' | translate"
464
+ value="1,247"
465
+ icon="heroShoppingCart"
466
+ iconBgClass="bg-linear-to-br from-indigo-500 to-blue-600 shadow-lg shadow-indigo-500/30"
467
+ iconColor="white"
468
+ gradientClass="from-indigo-50/50 to-blue-50/50">
469
+ </app-stats-card>
470
+ <app-stats-card
471
+ [label]="'stats.pending' | translate"
472
+ value="23"
473
+ icon="heroClock"
474
+ iconBgClass="bg-linear-to-br from-amber-500 to-orange-600 shadow-lg shadow-amber-500/30"
475
+ iconColor="white"
476
+ gradientClass="from-amber-50/50 to-orange-50/50">
477
+ </app-stats-card>
478
+ <app-stats-card
479
+ [label]="'stats.completed' | translate"
480
+ value="1,180"
481
+ icon="heroCheck"
482
+ iconBgClass="bg-linear-to-br from-emerald-500 to-green-600 shadow-lg shadow-emerald-500/30"
483
+ iconColor="white"
484
+ gradientClass="from-emerald-50/50 to-green-50/50">
485
+ </app-stats-card>
486
+ <app-stats-card
487
+ [label]="'stats.cancelled' | translate"
488
+ value="44"
489
+ icon="heroXMark"
490
+ iconBgClass="bg-linear-to-br from-rose-500 to-red-600 shadow-lg shadow-rose-500/30"
491
+ iconColor="white"
492
+ gradientClass="from-rose-50/50 to-red-50/50">
493
+ </app-stats-card>
494
+ </div>
495
+
496
+ <app-data-table
497
+ [data]="orders"
498
+ [columns]="columns"
499
+ [pageSize]="10">
500
+ </app-data-table>
501
+ </div>
502
+ \`,
503
+ })
504
+ export class OrdersComponent {
505
+ columns: TableColumn[] = [
506
+ { key: 'id', label: 'Order ID', width: '100px', sortable: true },
507
+ { key: 'customer', label: 'Customer', sortable: true },
508
+ { key: 'items', label: 'Items', type: 'number' },
509
+ { key: 'total', label: 'Total', sortable: true },
510
+ { key: 'status', label: 'Status', type: 'status' },
511
+ { key: 'date', label: 'Date', type: 'date', sortable: true },
512
+ ];
513
+
514
+ orders = [
515
+ { id: '#3210', customer: 'John Doe', items: 3, total: '$125.00', status: 'Completed', date: '2024-06-15' },
516
+ { id: '#3209', customer: 'Jane Smith', items: 2, total: '$89.50', status: 'Processing', date: '2024-06-15' },
517
+ { id: '#3208', customer: 'Bob Wilson', items: 5, total: '$234.00', status: 'Pending', date: '2024-06-14' },
518
+ { id: '#3207', customer: 'Alice Brown', items: 1, total: '$156.75', status: 'Completed', date: '2024-06-14' },
519
+ { id: '#3206', customer: 'Charlie Davis', items: 4, total: '$67.25', status: 'Cancelled', date: '2024-06-13' },
520
+ { id: '#3205', customer: 'Diana Miller', items: 2, total: '$198.00', status: 'Completed', date: '2024-06-13' },
521
+ { id: '#3204', customer: 'Edward Jones', items: 3, total: '$145.50', status: 'Processing', date: '2024-06-12' },
522
+ { id: '#3203', customer: 'Fiona Garcia', items: 1, total: '$78.00', status: 'Completed', date: '2024-06-12' },
523
+ { id: '#3202', customer: 'George Martinez', items: 6, total: '$312.00', status: 'Pending', date: '2024-06-11' },
524
+ { id: '#3201', customer: 'Helen Anderson', items: 2, total: '$95.25', status: 'Completed', date: '2024-06-11' },
525
+ ];
526
+ }`;
527
+
528
+ await fs.writeFile(
529
+ path.join(config.fullPath, 'src/app/features/dashboard/orders/orders.component.ts'),
530
+ ordersComponent
531
+ );
532
+
533
+ // Settings Page
534
+ const settingsComponent = `import { Component, inject } from '@angular/core';
535
+ import { FormsModule } from '@angular/forms';
536
+ import { TranslateModule } from '@ngx-translate/core';
537
+ import { BreadcrumbComponent } from '@shared/components/breadcrumb/breadcrumb.component';
538
+ import { ButtonComponent } from '@shared/components/button/button.component';
539
+ import { CardComponent } from '@shared/components/card/card.component';
540
+ import { TranslationService } from '@core/i18n/translation.service';
541
+
542
+ @Component({
543
+ selector: 'app-settings',
544
+ standalone: true,
545
+ imports: [FormsModule, TranslateModule, BreadcrumbComponent, ButtonComponent, CardComponent],
546
+ template: \`
547
+ <div class="space-y-6">
548
+ <div>
549
+ <app-breadcrumb [items]="[{ label: ('dashboard.settings' | translate) }]"></app-breadcrumb>
550
+ <h1 class="mt-2 text-2xl font-bold text-gray-900">{{ 'dashboard.settings' | translate }}</h1>
551
+ <p class="text-gray-500">{{ 'dashboard.welcome' | translate }}</p>
552
+ </div>
553
+
554
+ <div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
555
+ <!-- Profile Settings -->
556
+ <app-card [title]="'settings.profile' | translate">
557
+ <div class="space-y-4">
558
+ <div>
559
+ <label for="fullName" class="block text-sm font-medium text-gray-700">{{ 'settings.fullName' | translate }}</label>
560
+ <input
561
+ id="fullName"
562
+ type="text"
563
+ [(ngModel)]="profile.name"
564
+ class="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500"
565
+ />
566
+ </div>
567
+ <div>
568
+ <label for="email" class="block text-sm font-medium text-gray-700">{{ 'settings.email' | translate }}</label>
569
+ <input
570
+ id="email"
571
+ type="email"
572
+ [(ngModel)]="profile.email"
573
+ class="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500"
574
+ />
575
+ </div>
576
+ <div>
577
+ <label for="bio" class="block text-sm font-medium text-gray-700">{{ 'settings.bio' | translate }}</label>
578
+ <textarea
579
+ id="bio"
580
+ [(ngModel)]="profile.bio"
581
+ rows="3"
582
+ class="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500">
583
+ </textarea>
584
+ </div>
585
+ <app-button>{{ 'actions.save' | translate }}</app-button>
586
+ </div>
587
+ </app-card>
588
+
589
+ <!-- Notification Settings -->
590
+ <app-card [title]="'settings.notifications' | translate">
591
+ <div class="space-y-4">
592
+ @for (item of notifications; track item.key) {
593
+ <div class="flex items-center justify-between">
594
+ <div>
595
+ <p class="font-medium text-gray-900">{{ item.labelKey | translate }}</p>
596
+ <p class="text-sm text-gray-500">{{ item.descriptionKey | translate }}</p>
597
+ </div>
598
+ <button
599
+ type="button"
600
+ (click)="item.enabled = !item.enabled"
601
+ [attr.aria-pressed]="item.enabled"
602
+ class="relative inline-flex h-6 w-11 cursor-pointer items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-primary-300"
603
+ [class]="item.enabled ? 'bg-primary-600' : 'bg-gray-200'">
604
+ <span
605
+ class="inline-block h-5 w-5 transform rounded-full bg-white border border-gray-300 transition-transform"
606
+ [class]="item.enabled ? 'translate-x-5' : 'translate-x-0.5'">
607
+ </span>
608
+ </button>
609
+ </div>
610
+ }
611
+ </div>
612
+ </app-card>
613
+
614
+ <!-- Security Settings -->
615
+ <app-card [title]="'settings.security' | translate">
616
+ <div class="space-y-4">
617
+ <div>
618
+ <label for="currentPassword" class="block text-sm font-medium text-gray-700">{{ 'settings.currentPassword' | translate }}</label>
619
+ <input
620
+ id="currentPassword"
621
+ type="password"
622
+ class="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500"
623
+ />
624
+ </div>
625
+ <div>
626
+ <label for="newPassword" class="block text-sm font-medium text-gray-700">{{ 'settings.newPassword' | translate }}</label>
627
+ <input
628
+ id="newPassword"
629
+ type="password"
630
+ class="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500"
631
+ />
632
+ </div>
633
+ <div>
634
+ <label for="confirmPassword" class="block text-sm font-medium text-gray-700">{{ 'settings.confirmPassword' | translate }}</label>
635
+ <input
636
+ id="confirmPassword"
637
+ type="password"
638
+ class="mt-1 block w-full rounded-lg border border-gray-300 px-3 py-2 focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500"
639
+ />
640
+ </div>
641
+ <app-button>{{ 'settings.updatePassword' | translate }}</app-button>
642
+ </div>
643
+ </app-card>
644
+
645
+ <!-- Danger Zone -->
646
+ <app-card [title]="'settings.dangerZone' | translate">
647
+ <div class="space-y-4">
648
+ <p class="text-sm text-gray-500">
649
+ {{ 'settings.deleteWarning' | translate }}
650
+ </p>
651
+ <app-button variant="danger">{{ 'settings.deleteAccount' | translate }}</app-button>
652
+ </div>
653
+ </app-card>
654
+ </div>
655
+ </div>
656
+ \`,
657
+ })
658
+ export class SettingsComponent {
659
+ private translationService = inject(TranslationService);
660
+
661
+ profile = {
662
+ name: 'Admin User',
663
+ email: 'admin@example.com',
664
+ bio: 'Dashboard administrator',
665
+ };
666
+
667
+ notifications = [
668
+ { key: 'email', labelKey: 'settings.emailNotifications', descriptionKey: 'settings.emailNotifications', enabled: true },
669
+ { key: 'push', labelKey: 'settings.pushNotifications', descriptionKey: 'settings.pushNotifications', enabled: false },
670
+ { key: 'weekly', labelKey: 'settings.weeklyReport', descriptionKey: 'settings.weeklyReport', enabled: true },
671
+ { key: 'marketing', labelKey: 'settings.marketing', descriptionKey: 'settings.marketing', enabled: false },
672
+ ];
673
+ }`;
674
+
675
+ await fs.writeFile(
676
+ path.join(config.fullPath, 'src/app/features/dashboard/settings/settings.component.ts'),
677
+ settingsComponent
678
+ );
679
+ }
680
+
681
+ module.exports = { createPages };