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.
- package/CHANGELOG.md +81 -344
- package/README.md +93 -157
- package/lib/cli/index.js +29 -3
- package/lib/cli/interactive.js +26 -1
- package/lib/managers/ProjectManager.js +0 -4
- package/lib/templates/base/components.js +243 -0
- package/lib/templates/base/index.js +207 -0
- package/lib/templates/base/infrastructure.js +314 -0
- package/lib/templates/base/linting.js +359 -0
- package/lib/templates/base/pwa.js +103 -0
- package/lib/templates/base/services.js +362 -0
- package/lib/templates/blog/app.js +250 -0
- package/lib/templates/blog/components.js +360 -0
- package/lib/templates/blog/i18n.js +77 -0
- package/lib/templates/blog/index.js +126 -0
- package/lib/templates/blog/pages.js +554 -0
- package/lib/templates/blog/services.js +390 -0
- package/lib/templates/dashboard/app.js +320 -0
- package/lib/templates/dashboard/charts.js +305 -0
- package/lib/templates/dashboard/components.js +410 -0
- package/lib/templates/dashboard/i18n.js +340 -0
- package/lib/templates/dashboard/index.js +141 -0
- package/lib/templates/dashboard/layout.js +310 -0
- package/lib/templates/dashboard/pages.js +681 -0
- package/lib/templates/ecommerce/app.js +315 -0
- package/lib/templates/ecommerce/components.js +496 -0
- package/lib/templates/ecommerce/i18n.js +389 -0
- package/lib/templates/ecommerce/index.js +152 -0
- package/lib/templates/ecommerce/layout.js +270 -0
- package/lib/templates/ecommerce/pages.js +969 -0
- package/lib/templates/ecommerce/services.js +300 -0
- package/lib/templates/index.js +12 -0
- package/lib/templates/landing/index.js +1117 -0
- package/lib/templates/portfolio/index.js +1160 -0
- package/lib/templates/saas/index.js +1371 -0
- package/lib/templates/starter/app.js +364 -0
- package/lib/templates/starter/i18n.js +856 -0
- package/lib/templates/starter/index.js +53 -4055
- package/lib/templates/starter/layout.js +852 -0
- package/lib/templates/starter/pages.js +1241 -0
- package/package.json +1 -1
- package/lib/templates/starter/features.js +0 -867
- package/lib/utils/ai-config.js +0 -641
- /package/lib/templates/{starter → base}/advanced-features.js +0 -0
- /package/lib/templates/{starter → base}/seo-assets.js +0 -0
- /package/lib/templates/{starter → base}/seo-features.js +0 -0
- /package/lib/templates/{starter → base}/ui-features.js +0 -0
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
const fs = require("fs-extra");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Create i18n (translations) for dashboard
|
|
6
|
+
*/
|
|
7
|
+
async function createI18n(config) {
|
|
8
|
+
// Create Translation Service
|
|
9
|
+
const translationService = `import { Injectable, inject, signal, effect, PLATFORM_ID } from '@angular/core';
|
|
10
|
+
import { isPlatformBrowser } from '@angular/common';
|
|
11
|
+
import { TranslateService } from '@ngx-translate/core';
|
|
12
|
+
import { StorageService } from '@core/services/storage.service';
|
|
13
|
+
|
|
14
|
+
export type SupportedLanguage = 'en' | 'ar';
|
|
15
|
+
|
|
16
|
+
export interface LanguageOption {
|
|
17
|
+
code: SupportedLanguage;
|
|
18
|
+
name: string;
|
|
19
|
+
nativeName: string;
|
|
20
|
+
dir: 'ltr' | 'rtl';
|
|
21
|
+
flag: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@Injectable({
|
|
25
|
+
providedIn: 'root'
|
|
26
|
+
})
|
|
27
|
+
export class TranslationService {
|
|
28
|
+
private translateService = inject(TranslateService);
|
|
29
|
+
private storage = inject(StorageService);
|
|
30
|
+
private platformId = inject(PLATFORM_ID);
|
|
31
|
+
private isBrowser = isPlatformBrowser(this.platformId);
|
|
32
|
+
|
|
33
|
+
// Reactive current language
|
|
34
|
+
public currentLang = signal<SupportedLanguage>('en');
|
|
35
|
+
|
|
36
|
+
// Reactive text direction
|
|
37
|
+
public textDirection = signal<'ltr' | 'rtl'>('ltr');
|
|
38
|
+
|
|
39
|
+
// Available languages
|
|
40
|
+
public readonly languages: LanguageOption[] = [
|
|
41
|
+
{
|
|
42
|
+
code: 'en',
|
|
43
|
+
name: 'English',
|
|
44
|
+
nativeName: 'English',
|
|
45
|
+
dir: 'ltr',
|
|
46
|
+
flag: '🇺🇸'
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
code: 'ar',
|
|
50
|
+
name: 'Arabic',
|
|
51
|
+
nativeName: 'العربية',
|
|
52
|
+
dir: 'rtl',
|
|
53
|
+
flag: '🇸🇦'
|
|
54
|
+
}
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
constructor() {
|
|
58
|
+
// Initialize translation service
|
|
59
|
+
this.translateService.setDefaultLang('en');
|
|
60
|
+
|
|
61
|
+
// Load saved language or detect browser language
|
|
62
|
+
const savedLang = this.getSavedLanguage();
|
|
63
|
+
const browserLang = this.translateService.getBrowserLang() as SupportedLanguage;
|
|
64
|
+
const initialLang = savedLang || (this.isSupportedLanguage(browserLang) ? browserLang : 'en');
|
|
65
|
+
|
|
66
|
+
this.setLanguage(initialLang);
|
|
67
|
+
|
|
68
|
+
// Effect to update document direction when language changes (browser only)
|
|
69
|
+
if (this.isBrowser) {
|
|
70
|
+
effect(() => {
|
|
71
|
+
const dir = this.textDirection();
|
|
72
|
+
document.documentElement.setAttribute('dir', dir);
|
|
73
|
+
document.documentElement.setAttribute('lang', this.currentLang());
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Change the current language
|
|
80
|
+
*/
|
|
81
|
+
setLanguage(lang: SupportedLanguage): void {
|
|
82
|
+
if (!this.isSupportedLanguage(lang)) {
|
|
83
|
+
console.warn(\`Language '\${lang}' is not supported. Falling back to 'en'.\`);
|
|
84
|
+
lang = 'en';
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
this.translateService.use(lang);
|
|
88
|
+
this.currentLang.set(lang);
|
|
89
|
+
|
|
90
|
+
const languageOption = this.languages.find(l => l.code === lang);
|
|
91
|
+
if (languageOption) {
|
|
92
|
+
this.textDirection.set(languageOption.dir);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
this.saveLanguage(lang);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Get current language
|
|
100
|
+
*/
|
|
101
|
+
getCurrentLanguage(): SupportedLanguage {
|
|
102
|
+
return this.currentLang();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get language option by code
|
|
107
|
+
*/
|
|
108
|
+
getLanguageOption(code: SupportedLanguage): LanguageOption | undefined {
|
|
109
|
+
return this.languages.find(lang => lang.code === code);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Check if language is supported
|
|
114
|
+
*/
|
|
115
|
+
isSupportedLanguage(lang: string): lang is SupportedLanguage {
|
|
116
|
+
return ['en', 'ar'].includes(lang);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Toggle between English and Arabic
|
|
121
|
+
*/
|
|
122
|
+
toggleLanguage(): void {
|
|
123
|
+
const newLang: SupportedLanguage = this.currentLang() === 'en' ? 'ar' : 'en';
|
|
124
|
+
this.setLanguage(newLang);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Get instant translation
|
|
129
|
+
*/
|
|
130
|
+
instant(key: string, params?: object): string {
|
|
131
|
+
return this.translateService.instant(key, params);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Get translation as observable
|
|
136
|
+
*/
|
|
137
|
+
get(key: string | string[], params?: object) {
|
|
138
|
+
return this.translateService.get(key, params);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Save language preference to storage
|
|
143
|
+
*/
|
|
144
|
+
private saveLanguage(lang: SupportedLanguage): void {
|
|
145
|
+
this.storage.setItem('preferred_language', lang);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Get saved language from storage
|
|
150
|
+
*/
|
|
151
|
+
private getSavedLanguage(): SupportedLanguage | null {
|
|
152
|
+
const saved = this.storage.getItem('preferred_language');
|
|
153
|
+
return saved && this.isSupportedLanguage(saved) ? saved : null;
|
|
154
|
+
}
|
|
155
|
+
}`;
|
|
156
|
+
|
|
157
|
+
await fs.writeFile(
|
|
158
|
+
path.join(config.fullPath, "src/app/core/i18n/translation.service.ts"),
|
|
159
|
+
translationService
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
// Create English translations
|
|
163
|
+
const enTranslations = {
|
|
164
|
+
dashboard: {
|
|
165
|
+
title: "Dashboard",
|
|
166
|
+
overview: "Overview",
|
|
167
|
+
analytics: "Analytics",
|
|
168
|
+
users: "Users",
|
|
169
|
+
orders: "Orders",
|
|
170
|
+
settings: "Settings",
|
|
171
|
+
welcome: "Welcome back! Here's what's happening today.",
|
|
172
|
+
logout: "Logout",
|
|
173
|
+
},
|
|
174
|
+
stats: {
|
|
175
|
+
totalRevenue: "Total Revenue",
|
|
176
|
+
totalUsers: "Total Users",
|
|
177
|
+
totalOrders: "Total Orders",
|
|
178
|
+
conversionRate: "Conversion Rate",
|
|
179
|
+
pageViews: "Page Views",
|
|
180
|
+
uniqueVisitors: "Unique Visitors",
|
|
181
|
+
avgSession: "Avg. Session",
|
|
182
|
+
bounceRate: "Bounce Rate",
|
|
183
|
+
pending: "Pending",
|
|
184
|
+
completed: "Completed",
|
|
185
|
+
cancelled: "Cancelled",
|
|
186
|
+
},
|
|
187
|
+
charts: {
|
|
188
|
+
revenueOverTime: "Revenue Over Time",
|
|
189
|
+
trafficSources: "Traffic Sources",
|
|
190
|
+
salesByCategory: "Sales by Category",
|
|
191
|
+
visitorsOverTime: "Visitors Over Time",
|
|
192
|
+
topPages: "Top Pages",
|
|
193
|
+
},
|
|
194
|
+
table: {
|
|
195
|
+
search: "Search...",
|
|
196
|
+
noData: "No data available",
|
|
197
|
+
showing: "Showing",
|
|
198
|
+
to: "to",
|
|
199
|
+
of: "of",
|
|
200
|
+
results: "results",
|
|
201
|
+
orderId: "Order ID",
|
|
202
|
+
customer: "Customer",
|
|
203
|
+
items: "Items",
|
|
204
|
+
total: "Total",
|
|
205
|
+
status: "Status",
|
|
206
|
+
date: "Date",
|
|
207
|
+
name: "Name",
|
|
208
|
+
email: "Email",
|
|
209
|
+
role: "Role",
|
|
210
|
+
joined: "Joined",
|
|
211
|
+
},
|
|
212
|
+
actions: {
|
|
213
|
+
addUser: "Add User",
|
|
214
|
+
save: "Save Changes",
|
|
215
|
+
cancel: "Cancel",
|
|
216
|
+
delete: "Delete",
|
|
217
|
+
edit: "Edit",
|
|
218
|
+
},
|
|
219
|
+
settings: {
|
|
220
|
+
profile: "Profile Settings",
|
|
221
|
+
notifications: "Notifications",
|
|
222
|
+
security: "Security",
|
|
223
|
+
dangerZone: "Danger Zone",
|
|
224
|
+
fullName: "Full Name",
|
|
225
|
+
email: "Email",
|
|
226
|
+
bio: "Bio",
|
|
227
|
+
currentPassword: "Current Password",
|
|
228
|
+
newPassword: "New Password",
|
|
229
|
+
confirmPassword: "Confirm Password",
|
|
230
|
+
updatePassword: "Update Password",
|
|
231
|
+
deleteAccount: "Delete Account",
|
|
232
|
+
deleteWarning:
|
|
233
|
+
"Once you delete your account, there is no going back. Please be certain.",
|
|
234
|
+
emailNotifications: "Email Notifications",
|
|
235
|
+
pushNotifications: "Push Notifications",
|
|
236
|
+
weeklyReport: "Weekly Report",
|
|
237
|
+
marketing: "Marketing",
|
|
238
|
+
},
|
|
239
|
+
language: {
|
|
240
|
+
select: "Select Language",
|
|
241
|
+
english: "English",
|
|
242
|
+
arabic: "Arabic",
|
|
243
|
+
},
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
await fs.writeFile(
|
|
247
|
+
path.join(config.fullPath, "public/assets/i18n/en.json"),
|
|
248
|
+
JSON.stringify(enTranslations, null, 2)
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
// Create Arabic translations
|
|
252
|
+
const arTranslations = {
|
|
253
|
+
dashboard: {
|
|
254
|
+
title: "لوحة التحكم",
|
|
255
|
+
overview: "نظرة عامة",
|
|
256
|
+
analytics: "التحليلات",
|
|
257
|
+
users: "المستخدمين",
|
|
258
|
+
orders: "الطلبات",
|
|
259
|
+
settings: "الإعدادات",
|
|
260
|
+
welcome: "مرحبًا بعودتك! إليك ما يحدث اليوم.",
|
|
261
|
+
logout: "تسجيل الخروج",
|
|
262
|
+
},
|
|
263
|
+
stats: {
|
|
264
|
+
totalRevenue: "إجمالي الإيرادات",
|
|
265
|
+
totalUsers: "إجمالي المستخدمين",
|
|
266
|
+
totalOrders: "إجمالي الطلبات",
|
|
267
|
+
conversionRate: "معدل التحويل",
|
|
268
|
+
pageViews: "مشاهدات الصفحة",
|
|
269
|
+
uniqueVisitors: "الزوار الفريدين",
|
|
270
|
+
avgSession: "متوسط الجلسة",
|
|
271
|
+
bounceRate: "معدل الارتداد",
|
|
272
|
+
pending: "قيد الانتظار",
|
|
273
|
+
completed: "مكتمل",
|
|
274
|
+
cancelled: "ملغى",
|
|
275
|
+
},
|
|
276
|
+
charts: {
|
|
277
|
+
revenueOverTime: "الإيرادات عبر الوقت",
|
|
278
|
+
trafficSources: "مصادر الزيارات",
|
|
279
|
+
salesByCategory: "المبيعات حسب الفئة",
|
|
280
|
+
visitorsOverTime: "الزوار عبر الوقت",
|
|
281
|
+
topPages: "أهم الصفحات",
|
|
282
|
+
},
|
|
283
|
+
table: {
|
|
284
|
+
search: "بحث...",
|
|
285
|
+
noData: "لا توجد بيانات",
|
|
286
|
+
showing: "عرض",
|
|
287
|
+
to: "إلى",
|
|
288
|
+
of: "من",
|
|
289
|
+
results: "نتائج",
|
|
290
|
+
orderId: "رقم الطلب",
|
|
291
|
+
customer: "العميل",
|
|
292
|
+
items: "العناصر",
|
|
293
|
+
total: "المجموع",
|
|
294
|
+
status: "الحالة",
|
|
295
|
+
date: "التاريخ",
|
|
296
|
+
name: "الاسم",
|
|
297
|
+
email: "البريد الإلكتروني",
|
|
298
|
+
role: "الدور",
|
|
299
|
+
joined: "تاريخ الانضمام",
|
|
300
|
+
},
|
|
301
|
+
actions: {
|
|
302
|
+
addUser: "إضافة مستخدم",
|
|
303
|
+
save: "حفظ التغييرات",
|
|
304
|
+
cancel: "إلغاء",
|
|
305
|
+
delete: "حذف",
|
|
306
|
+
edit: "تعديل",
|
|
307
|
+
},
|
|
308
|
+
settings: {
|
|
309
|
+
profile: "إعدادات الملف الشخصي",
|
|
310
|
+
notifications: "الإشعارات",
|
|
311
|
+
security: "الأمان",
|
|
312
|
+
dangerZone: "منطقة الخطر",
|
|
313
|
+
fullName: "الاسم الكامل",
|
|
314
|
+
email: "البريد الإلكتروني",
|
|
315
|
+
bio: "نبذة",
|
|
316
|
+
currentPassword: "كلمة المرور الحالية",
|
|
317
|
+
newPassword: "كلمة المرور الجديدة",
|
|
318
|
+
confirmPassword: "تأكيد كلمة المرور",
|
|
319
|
+
updatePassword: "تحديث كلمة المرور",
|
|
320
|
+
deleteAccount: "حذف الحساب",
|
|
321
|
+
deleteWarning: "بمجرد حذف حسابك، لا يمكن التراجع. يرجى التأكد.",
|
|
322
|
+
emailNotifications: "إشعارات البريد",
|
|
323
|
+
pushNotifications: "الإشعارات الفورية",
|
|
324
|
+
weeklyReport: "التقرير الأسبوعي",
|
|
325
|
+
marketing: "التسويق",
|
|
326
|
+
},
|
|
327
|
+
language: {
|
|
328
|
+
select: "اختر اللغة",
|
|
329
|
+
english: "الإنجليزية",
|
|
330
|
+
arabic: "العربية",
|
|
331
|
+
},
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
await fs.writeFile(
|
|
335
|
+
path.join(config.fullPath, "public/assets/i18n/ar.json"),
|
|
336
|
+
JSON.stringify(arTranslations, null, 2)
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
module.exports = { createI18n };
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
const fs = require("fs-extra");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const base = require("../base");
|
|
4
|
+
|
|
5
|
+
// Import modular components
|
|
6
|
+
const { createComponents } = require("./components");
|
|
7
|
+
const { createCharts } = require("./charts");
|
|
8
|
+
const { createLayout } = require("./layout");
|
|
9
|
+
const { createPages } = require("./pages");
|
|
10
|
+
const { createI18n } = require("./i18n");
|
|
11
|
+
const {
|
|
12
|
+
createRouting,
|
|
13
|
+
createAppConfig,
|
|
14
|
+
createAppComponent,
|
|
15
|
+
createStyles,
|
|
16
|
+
installI18nPackages,
|
|
17
|
+
} = require("./app");
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Dashboard Template
|
|
21
|
+
* Extends base template with admin dashboard UI:
|
|
22
|
+
* - Sidebar navigation
|
|
23
|
+
* - Stats cards
|
|
24
|
+
* - Charts (bar, line, donut)
|
|
25
|
+
* - Data tables with sorting/pagination
|
|
26
|
+
* - Multiple dashboard pages
|
|
27
|
+
*/
|
|
28
|
+
const dashboard = {
|
|
29
|
+
info: {
|
|
30
|
+
name: "Dashboard",
|
|
31
|
+
description: "Admin dashboard with sidebar, charts, tables, and analytics",
|
|
32
|
+
features: [
|
|
33
|
+
...base.info.features,
|
|
34
|
+
"Collapsible sidebar navigation",
|
|
35
|
+
"Dashboard layout with header and sidebar",
|
|
36
|
+
"Stats cards with icons and trends",
|
|
37
|
+
"Chart components (Bar, Line, Donut)",
|
|
38
|
+
"Data table with sorting and pagination",
|
|
39
|
+
"Dashboard pages (Overview, Analytics, Users, Orders, Settings)",
|
|
40
|
+
"i18n translation support (English & Arabic) with RTL",
|
|
41
|
+
"Language switcher in header",
|
|
42
|
+
"Dark mode support",
|
|
43
|
+
"Responsive mobile-first design",
|
|
44
|
+
"Breadcrumb navigation",
|
|
45
|
+
"User profile dropdown",
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
async apply(config, spinner) {
|
|
50
|
+
this.spinner = spinner;
|
|
51
|
+
const chalk = require("chalk");
|
|
52
|
+
|
|
53
|
+
const completeStep = (message) => {
|
|
54
|
+
if (spinner) {
|
|
55
|
+
spinner.stop();
|
|
56
|
+
console.log(chalk.green(" ✔") + chalk.white(" " + message));
|
|
57
|
+
spinner.start();
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// Step 1: Apply base template (infrastructure)
|
|
62
|
+
if (spinner) spinner.update("Setting up base infrastructure...");
|
|
63
|
+
await base.apply(config, null);
|
|
64
|
+
|
|
65
|
+
// Step 2: Create dashboard-specific directories
|
|
66
|
+
if (spinner) spinner.update("Setting up dashboard UI...");
|
|
67
|
+
await this.createDashboardDirectories(config);
|
|
68
|
+
|
|
69
|
+
// Step 3: Create dashboard components (StatsCard, Breadcrumb, DataTable)
|
|
70
|
+
await createComponents(config);
|
|
71
|
+
|
|
72
|
+
// Step 4: Create chart components (Bar, Line, Donut)
|
|
73
|
+
await createCharts(config);
|
|
74
|
+
|
|
75
|
+
// Step 5: Create layout (sidebar + header)
|
|
76
|
+
await createLayout(config);
|
|
77
|
+
|
|
78
|
+
// Step 6: Create dashboard pages
|
|
79
|
+
await createPages(config);
|
|
80
|
+
|
|
81
|
+
// Step 7: Create i18n (translations)
|
|
82
|
+
await createI18n(config);
|
|
83
|
+
|
|
84
|
+
// Step 8: Create routing, app config, app component, styles
|
|
85
|
+
await createRouting(config);
|
|
86
|
+
await createAppConfig(config);
|
|
87
|
+
await createAppComponent(config);
|
|
88
|
+
await createStyles(config);
|
|
89
|
+
|
|
90
|
+
// Step 9: Install ngx-translate packages
|
|
91
|
+
await installI18nPackages(config);
|
|
92
|
+
|
|
93
|
+
// Step 10: Format code
|
|
94
|
+
await base.formatCode(config);
|
|
95
|
+
|
|
96
|
+
if (spinner) spinner.stop();
|
|
97
|
+
|
|
98
|
+
// Show summary
|
|
99
|
+
console.log("");
|
|
100
|
+
completeStep("Angular 20+ project created");
|
|
101
|
+
completeStep("Tailwind CSS v4 configured");
|
|
102
|
+
completeStep("Dashboard layout with sidebar");
|
|
103
|
+
completeStep("Chart components (Bar, Line, Donut)");
|
|
104
|
+
completeStep("Data table with sorting/pagination");
|
|
105
|
+
completeStep("Stats cards with trends");
|
|
106
|
+
completeStep("5 dashboard pages");
|
|
107
|
+
completeStep("i18n translation support (English & Arabic)");
|
|
108
|
+
completeStep("RTL/LTR language direction support");
|
|
109
|
+
completeStep("HTTP interceptors (Auth, Error, Loading, Cache)");
|
|
110
|
+
completeStep("Core services (9 services)");
|
|
111
|
+
completeStep("ESLint + Prettier + simple-git-hooks");
|
|
112
|
+
console.log("");
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
async createDashboardDirectories(config) {
|
|
116
|
+
const directories = [
|
|
117
|
+
"src/app/layout/sidebar",
|
|
118
|
+
"src/app/layout/dashboard-header",
|
|
119
|
+
"src/app/layout/dashboard",
|
|
120
|
+
"src/app/shared/components/stats-card",
|
|
121
|
+
"src/app/shared/components/chart-bar",
|
|
122
|
+
"src/app/shared/components/chart-line",
|
|
123
|
+
"src/app/shared/components/chart-donut",
|
|
124
|
+
"src/app/shared/components/data-table",
|
|
125
|
+
"src/app/shared/components/breadcrumb",
|
|
126
|
+
"src/app/features/dashboard/overview",
|
|
127
|
+
"src/app/features/dashboard/analytics",
|
|
128
|
+
"src/app/features/dashboard/users",
|
|
129
|
+
"src/app/features/dashboard/orders",
|
|
130
|
+
"src/app/features/dashboard/settings",
|
|
131
|
+
"src/app/core/i18n",
|
|
132
|
+
"public/assets/i18n",
|
|
133
|
+
];
|
|
134
|
+
|
|
135
|
+
for (const dir of directories) {
|
|
136
|
+
await fs.ensureDir(path.join(config.fullPath, dir));
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
module.exports = dashboard;
|