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,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;