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,300 @@
1
+ const fs = require("fs-extra");
2
+ const path = require("path");
3
+
4
+ /**
5
+ * Ecommerce Services
6
+ * - ProductService (product data and queries)
7
+ * - CartService (shopping cart with computed totals)
8
+ * - WishlistService (wishlist management)
9
+ */
10
+
11
+ async function createServices(config) {
12
+ // Product Service
13
+ const productService = `import { Injectable, signal } from '@angular/core';
14
+
15
+ export interface Product {
16
+ id: number;
17
+ name: string;
18
+ description: string;
19
+ price: number;
20
+ originalPrice?: number;
21
+ image: string;
22
+ images?: string[];
23
+ category: string;
24
+ rating: number;
25
+ reviewCount: number;
26
+ badge?: string;
27
+ inStock: boolean;
28
+ features?: string[];
29
+ }
30
+
31
+ @Injectable({
32
+ providedIn: 'root'
33
+ })
34
+ export class ProductService {
35
+ private products = signal<Product[]>([
36
+ {
37
+ id: 1,
38
+ name: 'Wireless Bluetooth Headphones',
39
+ description: 'Premium noise-canceling wireless headphones with 30-hour battery life. Features active noise cancellation, comfortable over-ear design, and crystal-clear audio quality.',
40
+ price: 199.99,
41
+ originalPrice: 249.99,
42
+ image: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=400',
43
+ images: [
44
+ 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=800',
45
+ 'https://images.unsplash.com/photo-1484704849700-f032a568e944?w=800',
46
+ ],
47
+ category: 'Electronics',
48
+ rating: 4.5,
49
+ reviewCount: 128,
50
+ badge: 'Sale',
51
+ inStock: true,
52
+ features: ['Active Noise Cancellation', '30-hour Battery', 'Bluetooth 5.0', 'Foldable Design'],
53
+ },
54
+ {
55
+ id: 2,
56
+ name: 'Smart Watch Pro',
57
+ description: 'Advanced smartwatch with health monitoring, GPS tracking, and 7-day battery life. Water-resistant and compatible with iOS and Android.',
58
+ price: 299.99,
59
+ image: 'https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=400',
60
+ category: 'Electronics',
61
+ rating: 4.8,
62
+ reviewCount: 256,
63
+ badge: 'New',
64
+ inStock: true,
65
+ features: ['Heart Rate Monitor', 'GPS Tracking', 'Water Resistant', '7-day Battery'],
66
+ },
67
+ {
68
+ id: 3,
69
+ name: 'Premium Leather Backpack',
70
+ description: 'Handcrafted genuine leather backpack with laptop compartment. Perfect for work or travel with multiple pockets and durable construction.',
71
+ price: 149.99,
72
+ originalPrice: 189.99,
73
+ image: 'https://images.unsplash.com/photo-1553062407-98eeb64c6a62?w=400',
74
+ category: 'Fashion',
75
+ rating: 4.3,
76
+ reviewCount: 89,
77
+ inStock: true,
78
+ features: ['Genuine Leather', 'Laptop Compartment', 'Multiple Pockets', 'Adjustable Straps'],
79
+ },
80
+ {
81
+ id: 4,
82
+ name: 'Minimalist Desk Lamp',
83
+ description: 'Modern LED desk lamp with adjustable brightness and color temperature. Touch control and USB charging port included.',
84
+ price: 79.99,
85
+ image: 'https://images.unsplash.com/photo-1507473885765-e6ed057f782c?w=400',
86
+ category: 'Home',
87
+ rating: 4.6,
88
+ reviewCount: 67,
89
+ inStock: true,
90
+ features: ['LED Technology', 'Adjustable Brightness', 'USB Charging', 'Touch Control'],
91
+ },
92
+ {
93
+ id: 5,
94
+ name: 'Organic Coffee Beans',
95
+ description: 'Single-origin organic Arabica coffee beans from Colombia. Medium roast with notes of chocolate and citrus.',
96
+ price: 24.99,
97
+ image: 'https://images.unsplash.com/photo-1559056199-641a0ac8b55e?w=400',
98
+ category: 'Food',
99
+ rating: 4.9,
100
+ reviewCount: 312,
101
+ badge: 'Best Seller',
102
+ inStock: true,
103
+ features: ['100% Organic', 'Single Origin', 'Medium Roast', 'Fair Trade'],
104
+ },
105
+ {
106
+ id: 6,
107
+ name: 'Yoga Mat Premium',
108
+ description: 'Extra thick eco-friendly yoga mat with alignment lines. Non-slip surface and carrying strap included.',
109
+ price: 59.99,
110
+ originalPrice: 79.99,
111
+ image: 'https://images.unsplash.com/photo-1601925260368-ae2f83cf8b7f?w=400',
112
+ category: 'Sports',
113
+ rating: 4.4,
114
+ reviewCount: 156,
115
+ badge: 'Sale',
116
+ inStock: true,
117
+ features: ['Eco-Friendly', 'Extra Thick', 'Alignment Lines', 'Non-Slip'],
118
+ },
119
+ {
120
+ id: 7,
121
+ name: 'Wireless Charging Pad',
122
+ description: 'Fast wireless charging pad compatible with all Qi-enabled devices. Sleek design with LED indicator.',
123
+ price: 39.99,
124
+ image: 'https://images.unsplash.com/photo-1586816879360-004f5b0c51e3?w=400',
125
+ category: 'Electronics',
126
+ rating: 4.2,
127
+ reviewCount: 203,
128
+ inStock: true,
129
+ features: ['Fast Charging', 'Qi Compatible', 'LED Indicator', 'Compact Design'],
130
+ },
131
+ {
132
+ id: 8,
133
+ name: 'Canvas Tote Bag',
134
+ description: 'Sustainable canvas tote bag with reinforced handles. Perfect for shopping or everyday use.',
135
+ price: 29.99,
136
+ image: 'https://images.unsplash.com/photo-1544816155-12df9643f363?w=400',
137
+ category: 'Fashion',
138
+ rating: 4.1,
139
+ reviewCount: 78,
140
+ inStock: true,
141
+ features: ['Sustainable', 'Reinforced Handles', 'Machine Washable', 'Large Capacity'],
142
+ },
143
+ ]);
144
+
145
+ getProducts(): Product[] {
146
+ return this.products();
147
+ }
148
+
149
+ getProductById(id: number): Product | undefined {
150
+ return this.products().find(p => p.id === id);
151
+ }
152
+
153
+ getCategories(): string[] {
154
+ return [...new Set(this.products().map(p => p.category))];
155
+ }
156
+
157
+ getProductsByCategory(category: string): Product[] {
158
+ return this.products().filter(p => p.category === category);
159
+ }
160
+
161
+ searchProducts(query: string): Product[] {
162
+ const lowerQuery = query.toLowerCase();
163
+ return this.products().filter(p =>
164
+ p.name.toLowerCase().includes(lowerQuery) ||
165
+ p.description.toLowerCase().includes(lowerQuery) ||
166
+ p.category.toLowerCase().includes(lowerQuery)
167
+ );
168
+ }
169
+ }`;
170
+
171
+ await fs.writeFile(
172
+ path.join(config.fullPath, "src/app/core/services/product.service.ts"),
173
+ productService
174
+ );
175
+
176
+ // Cart Service
177
+ const cartService = `import { Injectable, signal, computed } from '@angular/core';
178
+ import { Product } from './product.service';
179
+
180
+ export interface CartItem {
181
+ product: Product;
182
+ quantity: number;
183
+ }
184
+
185
+ @Injectable({
186
+ providedIn: 'root'
187
+ })
188
+ export class CartService {
189
+ private items = signal<CartItem[]>([]);
190
+
191
+ readonly cartItems = this.items.asReadonly();
192
+
193
+ readonly itemCount = computed(() =>
194
+ this.items().reduce((sum, item) => sum + item.quantity, 0)
195
+ );
196
+
197
+ readonly subtotal = computed(() =>
198
+ this.items().reduce((sum, item) => sum + (item.product.price * item.quantity), 0)
199
+ );
200
+
201
+ readonly shipping = computed(() => this.subtotal() > 100 ? 0 : 9.99);
202
+
203
+ readonly tax = computed(() => this.subtotal() * 0.08);
204
+
205
+ readonly total = computed(() => this.subtotal() + this.shipping() + this.tax());
206
+
207
+ addItem(product: Product, quantity = 1): void {
208
+ const currentItems = this.items();
209
+ const existingIndex = currentItems.findIndex(item => item.product.id === product.id);
210
+
211
+ if (existingIndex >= 0) {
212
+ const updated = [...currentItems];
213
+ updated[existingIndex] = {
214
+ ...updated[existingIndex],
215
+ quantity: updated[existingIndex].quantity + quantity,
216
+ };
217
+ this.items.set(updated);
218
+ } else {
219
+ this.items.set([...currentItems, { product, quantity }]);
220
+ }
221
+ }
222
+
223
+ removeItem(productId: number): void {
224
+ this.items.set(this.items().filter(item => item.product.id !== productId));
225
+ }
226
+
227
+ updateQuantity(productId: number, quantity: number): void {
228
+ if (quantity <= 0) {
229
+ this.removeItem(productId);
230
+ return;
231
+ }
232
+
233
+ const updated = this.items().map(item =>
234
+ item.product.id === productId ? { ...item, quantity } : item
235
+ );
236
+ this.items.set(updated);
237
+ }
238
+
239
+ clearCart(): void {
240
+ this.items.set([]);
241
+ }
242
+
243
+ isInCart(productId: number): boolean {
244
+ return this.items().some(item => item.product.id === productId);
245
+ }
246
+ }`;
247
+
248
+ await fs.writeFile(
249
+ path.join(config.fullPath, "src/app/core/services/cart.service.ts"),
250
+ cartService
251
+ );
252
+
253
+ // Wishlist Service
254
+ const wishlistService = `import { Injectable, signal, computed } from '@angular/core';
255
+ import { Product } from './product.service';
256
+
257
+ @Injectable({
258
+ providedIn: 'root'
259
+ })
260
+ export class WishlistService {
261
+ private items = signal<Product[]>([]);
262
+
263
+ readonly wishlistItems = this.items.asReadonly();
264
+
265
+ readonly itemCount = computed(() => this.items().length);
266
+
267
+ toggle(product: Product): void {
268
+ if (this.isInWishlist(product.id)) {
269
+ this.remove(product.id);
270
+ } else {
271
+ this.add(product);
272
+ }
273
+ }
274
+
275
+ add(product: Product): void {
276
+ if (!this.isInWishlist(product.id)) {
277
+ this.items.set([...this.items(), product]);
278
+ }
279
+ }
280
+
281
+ remove(productId: number): void {
282
+ this.items.set(this.items().filter(item => item.id !== productId));
283
+ }
284
+
285
+ isInWishlist(productId: number): boolean {
286
+ return this.items().some(item => item.id === productId);
287
+ }
288
+
289
+ clearWishlist(): void {
290
+ this.items.set([]);
291
+ }
292
+ }`;
293
+
294
+ await fs.writeFile(
295
+ path.join(config.fullPath, "src/app/core/services/wishlist.service.ts"),
296
+ wishlistService
297
+ );
298
+ }
299
+
300
+ module.exports = { createServices };
@@ -1,7 +1,19 @@
1
1
  const minimal = require("./minimal");
2
2
  const starter = require("./starter");
3
+ const dashboard = require("./dashboard");
4
+ const ecommerce = require("./ecommerce");
5
+ const blog = require("./blog");
6
+ const saas = require("./saas");
7
+ const portfolio = require("./portfolio");
8
+ const landing = require("./landing");
3
9
 
4
10
  module.exports = {
5
11
  minimal,
6
12
  starter,
13
+ dashboard,
14
+ ecommerce,
15
+ blog,
16
+ saas,
17
+ portfolio,
18
+ landing,
7
19
  };