create-ng-tailwind 3.1.0 → 4.1.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.
Potentially problematic release.
This version of create-ng-tailwind might be problematic. Click here for more details.
- package/CHANGELOG.md +96 -341
- package/README.md +111 -157
- package/lib/cli/index.js +74 -3
- package/lib/cli/interactive.js +26 -1
- package/lib/managers/ProjectManager.js +2 -5
- 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 +52 -4060
- package/lib/templates/starter/layout.js +852 -0
- package/lib/templates/starter/pages.js +1241 -0
- package/lib/utils/nodeCompat.js +85 -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,390 @@
|
|
|
1
|
+
const fs = require("fs-extra");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Create Blog Services
|
|
6
|
+
*/
|
|
7
|
+
async function createServices(config) {
|
|
8
|
+
const blogService = `import { Injectable, signal, computed } from '@angular/core';
|
|
9
|
+
|
|
10
|
+
export interface Author {
|
|
11
|
+
id: number;
|
|
12
|
+
name: string;
|
|
13
|
+
avatar: string;
|
|
14
|
+
bio: string;
|
|
15
|
+
social?: {
|
|
16
|
+
twitter?: string;
|
|
17
|
+
github?: string;
|
|
18
|
+
linkedin?: string;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface Post {
|
|
23
|
+
id: number;
|
|
24
|
+
slug: string;
|
|
25
|
+
title: string;
|
|
26
|
+
excerpt: string;
|
|
27
|
+
content: string;
|
|
28
|
+
coverImage: string;
|
|
29
|
+
author: Author;
|
|
30
|
+
category: string;
|
|
31
|
+
tags: string[];
|
|
32
|
+
publishedAt: Date;
|
|
33
|
+
updatedAt?: Date;
|
|
34
|
+
readingTime: number;
|
|
35
|
+
featured?: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface Comment {
|
|
39
|
+
id: number;
|
|
40
|
+
postId: number;
|
|
41
|
+
author: string;
|
|
42
|
+
email: string;
|
|
43
|
+
content: string;
|
|
44
|
+
createdAt: Date;
|
|
45
|
+
replies?: Comment[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@Injectable({ providedIn: 'root' })
|
|
49
|
+
export class BlogService {
|
|
50
|
+
private postsSignal = signal<Post[]>(this.getMockPosts());
|
|
51
|
+
private commentsSignal = signal<Comment[]>(this.getMockComments());
|
|
52
|
+
|
|
53
|
+
posts = this.postsSignal.asReadonly();
|
|
54
|
+
comments = this.commentsSignal.asReadonly();
|
|
55
|
+
|
|
56
|
+
categories = computed(() => {
|
|
57
|
+
const cats = new Set(this.postsSignal().map(p => p.category));
|
|
58
|
+
return Array.from(cats);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
tags = computed(() => {
|
|
62
|
+
const allTags = this.postsSignal().flatMap(p => p.tags);
|
|
63
|
+
return Array.from(new Set(allTags));
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
featuredPosts = computed(() =>
|
|
67
|
+
this.postsSignal().filter(p => p.featured).slice(0, 3)
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
recentPosts = computed(() =>
|
|
71
|
+
[...this.postsSignal()]
|
|
72
|
+
.sort((a, b) => new Date(b.publishedAt).getTime() - new Date(a.publishedAt).getTime())
|
|
73
|
+
.slice(0, 5)
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
getPostBySlug(slug: string): Post | undefined {
|
|
77
|
+
return this.postsSignal().find(p => p.slug === slug);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
getPostsByCategory(category: string): Post[] {
|
|
81
|
+
return this.postsSignal().filter(p => p.category === category);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
getPostsByTag(tag: string): Post[] {
|
|
85
|
+
return this.postsSignal().filter(p => p.tags.includes(tag));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
getPostsByAuthor(authorId: number): Post[] {
|
|
89
|
+
return this.postsSignal().filter(p => p.author.id === authorId);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
getRelatedPosts(post: Post, limit = 3): Post[] {
|
|
93
|
+
return this.postsSignal()
|
|
94
|
+
.filter(p => p.id !== post.id && (p.category === post.category || p.tags.some(t => post.tags.includes(t))))
|
|
95
|
+
.slice(0, limit);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
searchPosts(query: string): Post[] {
|
|
99
|
+
const q = query.toLowerCase();
|
|
100
|
+
return this.postsSignal().filter(p =>
|
|
101
|
+
p.title.toLowerCase().includes(q) ||
|
|
102
|
+
p.excerpt.toLowerCase().includes(q) ||
|
|
103
|
+
p.tags.some(t => t.toLowerCase().includes(q))
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
getCommentsByPostId(postId: number): Comment[] {
|
|
108
|
+
return this.commentsSignal().filter(c => c.postId === postId);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
addComment(comment: Omit<Comment, 'id' | 'createdAt'>): void {
|
|
112
|
+
const newComment: Comment = {
|
|
113
|
+
...comment,
|
|
114
|
+
id: Date.now(),
|
|
115
|
+
createdAt: new Date(),
|
|
116
|
+
};
|
|
117
|
+
this.commentsSignal.update(comments => [...comments, newComment]);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private getMockPosts(): Post[] {
|
|
121
|
+
const authors: Author[] = [
|
|
122
|
+
{
|
|
123
|
+
id: 1,
|
|
124
|
+
name: 'Sarah Johnson',
|
|
125
|
+
avatar: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=150',
|
|
126
|
+
bio: 'Senior Frontend Developer passionate about Angular and modern web technologies.',
|
|
127
|
+
social: { twitter: 'sarahj', github: 'sarahj' }
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
id: 2,
|
|
131
|
+
name: 'Michael Chen',
|
|
132
|
+
avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=150',
|
|
133
|
+
bio: 'Full-stack developer and tech blogger. Love sharing knowledge.',
|
|
134
|
+
social: { twitter: 'mchen', linkedin: 'michaelchen' }
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: 3,
|
|
138
|
+
name: 'Emily Davis',
|
|
139
|
+
avatar: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=150',
|
|
140
|
+
bio: 'UX/UI Designer turned developer. Bridging design and code.',
|
|
141
|
+
social: { github: 'emilyd' }
|
|
142
|
+
},
|
|
143
|
+
];
|
|
144
|
+
|
|
145
|
+
return [
|
|
146
|
+
{
|
|
147
|
+
id: 1,
|
|
148
|
+
slug: 'getting-started-with-angular-signals',
|
|
149
|
+
title: 'Getting Started with Angular Signals',
|
|
150
|
+
excerpt: 'Learn how to use Angular Signals for reactive state management in your applications.',
|
|
151
|
+
content: \`# Getting Started with Angular Signals
|
|
152
|
+
|
|
153
|
+
Angular Signals represent a new way to handle reactive state in Angular applications. They provide a simpler, more predictable way to manage state compared to traditional approaches.
|
|
154
|
+
|
|
155
|
+
## What are Signals?
|
|
156
|
+
|
|
157
|
+
Signals are reactive primitives that hold a value and notify consumers when that value changes. They're similar to observables but with a simpler API.
|
|
158
|
+
|
|
159
|
+
\\\`\\\`\\\`typescript
|
|
160
|
+
import { signal, computed } from '@angular/core';
|
|
161
|
+
|
|
162
|
+
// Create a signal
|
|
163
|
+
const count = signal(0);
|
|
164
|
+
|
|
165
|
+
// Read the value
|
|
166
|
+
console.log(count()); // 0
|
|
167
|
+
|
|
168
|
+
// Update the value
|
|
169
|
+
count.set(1);
|
|
170
|
+
count.update(v => v + 1);
|
|
171
|
+
|
|
172
|
+
// Create computed signals
|
|
173
|
+
const doubled = computed(() => count() * 2);
|
|
174
|
+
\\\`\\\`\\\`
|
|
175
|
+
|
|
176
|
+
## Benefits of Signals
|
|
177
|
+
|
|
178
|
+
1. **Simplicity**: No need to manage subscriptions
|
|
179
|
+
2. **Performance**: Fine-grained reactivity
|
|
180
|
+
3. **Debugging**: Easier to trace state changes
|
|
181
|
+
4. **Integration**: Works seamlessly with Angular's change detection
|
|
182
|
+
|
|
183
|
+
## Conclusion
|
|
184
|
+
|
|
185
|
+
Signals are a powerful addition to Angular's reactive toolkit. Start using them today!\`,
|
|
186
|
+
coverImage: 'https://images.unsplash.com/photo-1555066931-4365d14bab8c?w=800',
|
|
187
|
+
author: authors[0],
|
|
188
|
+
category: 'Angular',
|
|
189
|
+
tags: ['Angular', 'Signals', 'State Management', 'Tutorial'],
|
|
190
|
+
publishedAt: new Date('2025-01-15'),
|
|
191
|
+
readingTime: 5,
|
|
192
|
+
featured: true,
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
id: 2,
|
|
196
|
+
slug: 'tailwind-css-v4-whats-new',
|
|
197
|
+
title: "Tailwind CSS v4: What's New",
|
|
198
|
+
excerpt: 'Explore the exciting new features in Tailwind CSS v4 including the new @theme directive.',
|
|
199
|
+
content: \`# Tailwind CSS v4: What's New
|
|
200
|
+
|
|
201
|
+
Tailwind CSS v4 brings significant improvements to how we style our applications. Let's explore the key changes.
|
|
202
|
+
|
|
203
|
+
## The @theme Directive
|
|
204
|
+
|
|
205
|
+
The new @theme directive allows you to define your design tokens directly in CSS:
|
|
206
|
+
|
|
207
|
+
\\\`\\\`\\\`css
|
|
208
|
+
@theme {
|
|
209
|
+
--color-primary-500: #3b82f6;
|
|
210
|
+
--color-secondary-500: #06b6d4;
|
|
211
|
+
}
|
|
212
|
+
\\\`\\\`\\\`
|
|
213
|
+
|
|
214
|
+
## Lightning CSS Integration
|
|
215
|
+
|
|
216
|
+
Tailwind v4 now uses Lightning CSS for faster builds and better CSS processing.
|
|
217
|
+
|
|
218
|
+
## No More Config File
|
|
219
|
+
|
|
220
|
+
You can now configure Tailwind entirely through CSS, no JavaScript config needed!
|
|
221
|
+
|
|
222
|
+
## Conclusion
|
|
223
|
+
|
|
224
|
+
Tailwind v4 is a major step forward for utility-first CSS.\`,
|
|
225
|
+
coverImage: 'https://images.unsplash.com/photo-1507721999472-8ed4421c4af2?w=800',
|
|
226
|
+
author: authors[1],
|
|
227
|
+
category: 'CSS',
|
|
228
|
+
tags: ['Tailwind', 'CSS', 'Frontend', 'Styling'],
|
|
229
|
+
publishedAt: new Date('2025-01-10'),
|
|
230
|
+
readingTime: 4,
|
|
231
|
+
featured: true,
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
id: 3,
|
|
235
|
+
slug: 'building-accessible-components',
|
|
236
|
+
title: 'Building Accessible Components',
|
|
237
|
+
excerpt: 'A comprehensive guide to creating accessible Angular components that work for everyone.',
|
|
238
|
+
content: \`# Building Accessible Components
|
|
239
|
+
|
|
240
|
+
Accessibility (a11y) is crucial for creating inclusive web applications. Here's how to build accessible Angular components.
|
|
241
|
+
|
|
242
|
+
## Key Principles
|
|
243
|
+
|
|
244
|
+
1. **Semantic HTML**: Use the right elements
|
|
245
|
+
2. **Keyboard Navigation**: Everything should be keyboard accessible
|
|
246
|
+
3. **ARIA Labels**: Provide context for screen readers
|
|
247
|
+
4. **Color Contrast**: Ensure readable text
|
|
248
|
+
|
|
249
|
+
## Testing Accessibility
|
|
250
|
+
|
|
251
|
+
Use tools like axe-core and Lighthouse to audit your components.\`,
|
|
252
|
+
coverImage: 'https://images.unsplash.com/photo-1573164713714-d95e436ab8d6?w=800',
|
|
253
|
+
author: authors[2],
|
|
254
|
+
category: 'Accessibility',
|
|
255
|
+
tags: ['Accessibility', 'A11y', 'Angular', 'Components'],
|
|
256
|
+
publishedAt: new Date('2025-01-05'),
|
|
257
|
+
readingTime: 6,
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
id: 4,
|
|
261
|
+
slug: 'typescript-best-practices-2025',
|
|
262
|
+
title: 'TypeScript Best Practices for 2025',
|
|
263
|
+
excerpt: 'Modern TypeScript patterns and best practices to write cleaner, safer code.',
|
|
264
|
+
content: \`# TypeScript Best Practices for 2025
|
|
265
|
+
|
|
266
|
+
TypeScript continues to evolve. Here are the best practices for writing modern TypeScript.
|
|
267
|
+
|
|
268
|
+
## Use Strict Mode
|
|
269
|
+
|
|
270
|
+
Always enable strict mode in your tsconfig.
|
|
271
|
+
|
|
272
|
+
## Prefer Unknown Over Any
|
|
273
|
+
|
|
274
|
+
Use unknown instead of any for better type safety.
|
|
275
|
+
|
|
276
|
+
## Conclusion
|
|
277
|
+
|
|
278
|
+
Following these practices will make your TypeScript code more maintainable and type-safe.\`,
|
|
279
|
+
coverImage: 'https://images.unsplash.com/photo-1516116216624-53e697fedbea?w=800',
|
|
280
|
+
author: authors[0],
|
|
281
|
+
category: 'TypeScript',
|
|
282
|
+
tags: ['TypeScript', 'JavaScript', 'Best Practices'],
|
|
283
|
+
publishedAt: new Date('2025-01-01'),
|
|
284
|
+
readingTime: 7,
|
|
285
|
+
featured: true,
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
id: 5,
|
|
289
|
+
slug: 'state-management-patterns',
|
|
290
|
+
title: 'State Management Patterns in Angular',
|
|
291
|
+
excerpt: 'Compare different state management approaches: Services, Signals, NgRx, and more.',
|
|
292
|
+
content: \`# State Management Patterns in Angular
|
|
293
|
+
|
|
294
|
+
Choosing the right state management approach is crucial. Let's compare the options.
|
|
295
|
+
|
|
296
|
+
## Simple Service with Signals
|
|
297
|
+
|
|
298
|
+
Best for small to medium apps.
|
|
299
|
+
|
|
300
|
+
## NgRx for Complex Apps
|
|
301
|
+
|
|
302
|
+
When you need predictable state with time-travel debugging.
|
|
303
|
+
|
|
304
|
+
## Conclusion
|
|
305
|
+
|
|
306
|
+
Start simple with signals, scale to NgRx when needed.\`,
|
|
307
|
+
coverImage: 'https://images.unsplash.com/photo-1558494949-ef010cbdcc31?w=800',
|
|
308
|
+
author: authors[1],
|
|
309
|
+
category: 'Angular',
|
|
310
|
+
tags: ['Angular', 'State Management', 'NgRx', 'Signals'],
|
|
311
|
+
publishedAt: new Date('2024-12-28'),
|
|
312
|
+
readingTime: 8,
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
id: 6,
|
|
316
|
+
slug: 'responsive-design-techniques',
|
|
317
|
+
title: 'Modern Responsive Design Techniques',
|
|
318
|
+
excerpt: 'Master responsive design with CSS Grid, Flexbox, and container queries.',
|
|
319
|
+
content: \`# Modern Responsive Design Techniques
|
|
320
|
+
|
|
321
|
+
Responsive design has evolved. Here are modern techniques to create adaptive layouts.
|
|
322
|
+
|
|
323
|
+
## CSS Grid
|
|
324
|
+
|
|
325
|
+
Perfect for two-dimensional layouts.
|
|
326
|
+
|
|
327
|
+
## Container Queries
|
|
328
|
+
|
|
329
|
+
Style based on container size, not viewport.
|
|
330
|
+
|
|
331
|
+
## Conclusion
|
|
332
|
+
|
|
333
|
+
Combine these techniques for truly responsive designs.\`,
|
|
334
|
+
coverImage: 'https://images.unsplash.com/photo-1517292987719-0369a794ec0f?w=800',
|
|
335
|
+
author: authors[2],
|
|
336
|
+
category: 'CSS',
|
|
337
|
+
tags: ['CSS', 'Responsive', 'Grid', 'Flexbox'],
|
|
338
|
+
publishedAt: new Date('2024-12-20'),
|
|
339
|
+
readingTime: 5,
|
|
340
|
+
},
|
|
341
|
+
];
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
private getMockComments(): Comment[] {
|
|
345
|
+
return [
|
|
346
|
+
{
|
|
347
|
+
id: 1,
|
|
348
|
+
postId: 1,
|
|
349
|
+
author: 'John Doe',
|
|
350
|
+
email: 'john@example.com',
|
|
351
|
+
content: 'Great article! Signals have really simplified my state management.',
|
|
352
|
+
createdAt: new Date('2025-01-16'),
|
|
353
|
+
replies: [
|
|
354
|
+
{
|
|
355
|
+
id: 2,
|
|
356
|
+
postId: 1,
|
|
357
|
+
author: 'Sarah Johnson',
|
|
358
|
+
email: 'sarah@example.com',
|
|
359
|
+
content: 'Thanks John! Glad you found it helpful.',
|
|
360
|
+
createdAt: new Date('2025-01-16'),
|
|
361
|
+
}
|
|
362
|
+
]
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
id: 3,
|
|
366
|
+
postId: 1,
|
|
367
|
+
author: 'Jane Smith',
|
|
368
|
+
email: 'jane@example.com',
|
|
369
|
+
content: 'Could you write a follow-up about computed signals?',
|
|
370
|
+
createdAt: new Date('2025-01-17'),
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
id: 4,
|
|
374
|
+
postId: 2,
|
|
375
|
+
author: 'Alex Turner',
|
|
376
|
+
email: 'alex@example.com',
|
|
377
|
+
content: 'The @theme directive is a game changer!',
|
|
378
|
+
createdAt: new Date('2025-01-11'),
|
|
379
|
+
},
|
|
380
|
+
];
|
|
381
|
+
}
|
|
382
|
+
}`;
|
|
383
|
+
|
|
384
|
+
await fs.writeFile(
|
|
385
|
+
path.join(config.fullPath, "src/app/core/services/blog.service.ts"),
|
|
386
|
+
blogService
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
module.exports = { createServices };
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
const fs = require("fs-extra");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Create routing configuration
|
|
6
|
+
*/
|
|
7
|
+
async function createRouting(config) {
|
|
8
|
+
const routesFile = `import { Routes } from '@angular/router';
|
|
9
|
+
import { DashboardLayoutComponent } from './layout/dashboard/dashboard-layout.component';
|
|
10
|
+
|
|
11
|
+
export const routes: Routes = [
|
|
12
|
+
{
|
|
13
|
+
path: '',
|
|
14
|
+
redirectTo: 'dashboard',
|
|
15
|
+
pathMatch: 'full',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
path: 'dashboard',
|
|
19
|
+
component: DashboardLayoutComponent,
|
|
20
|
+
children: [
|
|
21
|
+
{
|
|
22
|
+
path: '',
|
|
23
|
+
loadComponent: () =>
|
|
24
|
+
import('./features/dashboard/overview/overview.component').then(
|
|
25
|
+
(c) => c.OverviewComponent
|
|
26
|
+
),
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
path: 'analytics',
|
|
30
|
+
loadComponent: () =>
|
|
31
|
+
import('./features/dashboard/analytics/analytics.component').then(
|
|
32
|
+
(c) => c.AnalyticsComponent
|
|
33
|
+
),
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
path: 'users',
|
|
37
|
+
loadComponent: () =>
|
|
38
|
+
import('./features/dashboard/users/users.component').then(
|
|
39
|
+
(c) => c.UsersComponent
|
|
40
|
+
),
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
path: 'orders',
|
|
44
|
+
loadComponent: () =>
|
|
45
|
+
import('./features/dashboard/orders/orders.component').then(
|
|
46
|
+
(c) => c.OrdersComponent
|
|
47
|
+
),
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
path: 'settings',
|
|
51
|
+
loadComponent: () =>
|
|
52
|
+
import('./features/dashboard/settings/settings.component').then(
|
|
53
|
+
(c) => c.SettingsComponent
|
|
54
|
+
),
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
path: '**',
|
|
60
|
+
redirectTo: 'dashboard',
|
|
61
|
+
},
|
|
62
|
+
];`;
|
|
63
|
+
|
|
64
|
+
await fs.writeFile(
|
|
65
|
+
path.join(config.fullPath, "src/app/app.routes.ts"),
|
|
66
|
+
routesFile
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Create app configuration
|
|
72
|
+
*/
|
|
73
|
+
async function createAppConfig(config) {
|
|
74
|
+
const appConfigFile = `import { ApplicationConfig, provideZoneChangeDetection, importProvidersFrom } from '@angular/core';
|
|
75
|
+
import { provideRouter } from '@angular/router';
|
|
76
|
+
import { provideHttpClient, withInterceptors, HttpClient } from '@angular/common/http';
|
|
77
|
+
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
|
|
78
|
+
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
|
|
79
|
+
|
|
80
|
+
import { routes } from './app.routes';
|
|
81
|
+
import { authInterceptor } from '@core/interceptors/auth.interceptor';
|
|
82
|
+
import { errorInterceptor } from '@core/interceptors/error.interceptor';
|
|
83
|
+
import { loadingInterceptor } from '@core/interceptors/loading.interceptor';
|
|
84
|
+
import { cachingInterceptor } from '@core/interceptors/caching.interceptor';
|
|
85
|
+
|
|
86
|
+
// Translation loader factory
|
|
87
|
+
export function HttpLoaderFactory(http: HttpClient) {
|
|
88
|
+
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export const appConfig: ApplicationConfig = {
|
|
92
|
+
providers: [
|
|
93
|
+
provideZoneChangeDetection({ eventCoalescing: true }),
|
|
94
|
+
provideRouter(routes),
|
|
95
|
+
provideHttpClient(
|
|
96
|
+
withInterceptors([
|
|
97
|
+
authInterceptor,
|
|
98
|
+
cachingInterceptor,
|
|
99
|
+
loadingInterceptor,
|
|
100
|
+
errorInterceptor,
|
|
101
|
+
])
|
|
102
|
+
),
|
|
103
|
+
// Translation configuration
|
|
104
|
+
importProvidersFrom(
|
|
105
|
+
TranslateModule.forRoot({
|
|
106
|
+
loader: {
|
|
107
|
+
provide: TranslateLoader,
|
|
108
|
+
useFactory: HttpLoaderFactory,
|
|
109
|
+
deps: [HttpClient]
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
),
|
|
113
|
+
],
|
|
114
|
+
};`;
|
|
115
|
+
|
|
116
|
+
await fs.writeFile(
|
|
117
|
+
path.join(config.fullPath, "src/app/app.config.ts"),
|
|
118
|
+
appConfigFile
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Create app component
|
|
124
|
+
*/
|
|
125
|
+
async function createAppComponent(config) {
|
|
126
|
+
const appTs = `import { Component, inject } from '@angular/core';
|
|
127
|
+
import { RouterOutlet } from '@angular/router';
|
|
128
|
+
import { ToastComponent } from '@shared/components/toast/toast.component';
|
|
129
|
+
import { ModalComponent } from '@shared/components/modal/modal.component';
|
|
130
|
+
import { TranslationService } from '@core/i18n/translation.service';
|
|
131
|
+
|
|
132
|
+
@Component({
|
|
133
|
+
selector: 'app-root',
|
|
134
|
+
imports: [RouterOutlet, ToastComponent, ModalComponent],
|
|
135
|
+
templateUrl: './app.html',
|
|
136
|
+
})
|
|
137
|
+
export class App {
|
|
138
|
+
// Inject translation service to initialize it at app startup
|
|
139
|
+
private translationService = inject(TranslationService);
|
|
140
|
+
title = '${config.projectName}';
|
|
141
|
+
}`;
|
|
142
|
+
|
|
143
|
+
const appHtml = `<router-outlet />
|
|
144
|
+
<app-toast />
|
|
145
|
+
<app-modal />
|
|
146
|
+
`;
|
|
147
|
+
|
|
148
|
+
await fs.writeFile(path.join(config.fullPath, "src/app/app.ts"), appTs);
|
|
149
|
+
await fs.writeFile(path.join(config.fullPath, "src/app/app.html"), appHtml);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Create global styles
|
|
154
|
+
*/
|
|
155
|
+
async function createStyles(config) {
|
|
156
|
+
const stylesFile = `@import "tailwindcss";
|
|
157
|
+
|
|
158
|
+
/* Custom CSS Variables for theming */
|
|
159
|
+
@theme {
|
|
160
|
+
/* Primary colors */
|
|
161
|
+
--color-primary-50: #eff6ff;
|
|
162
|
+
--color-primary-100: #dbeafe;
|
|
163
|
+
--color-primary-200: #bfdbfe;
|
|
164
|
+
--color-primary-300: #93c5fd;
|
|
165
|
+
--color-primary-400: #60a5fa;
|
|
166
|
+
--color-primary-500: #3b82f6;
|
|
167
|
+
--color-primary-600: #2563eb;
|
|
168
|
+
--color-primary-700: #1d4ed8;
|
|
169
|
+
--color-primary-800: #1e40af;
|
|
170
|
+
--color-primary-900: #1e3a8a;
|
|
171
|
+
--color-primary-950: #172554;
|
|
172
|
+
|
|
173
|
+
/* Secondary colors */
|
|
174
|
+
--color-secondary-50: #f8fafc;
|
|
175
|
+
--color-secondary-100: #f1f5f9;
|
|
176
|
+
--color-secondary-200: #e2e8f0;
|
|
177
|
+
--color-secondary-300: #cbd5e1;
|
|
178
|
+
--color-secondary-400: #94a3b8;
|
|
179
|
+
--color-secondary-500: #64748b;
|
|
180
|
+
--color-secondary-600: #475569;
|
|
181
|
+
--color-secondary-700: #334155;
|
|
182
|
+
--color-secondary-800: #1e293b;
|
|
183
|
+
--color-secondary-900: #0f172a;
|
|
184
|
+
--color-secondary-950: #020617;
|
|
185
|
+
|
|
186
|
+
/* Accent colors */
|
|
187
|
+
--color-accent-50: #faf5ff;
|
|
188
|
+
--color-accent-100: #f3e8ff;
|
|
189
|
+
--color-accent-200: #e9d5ff;
|
|
190
|
+
--color-accent-300: #d8b4fe;
|
|
191
|
+
--color-accent-400: #c084fc;
|
|
192
|
+
--color-accent-500: #a855f7;
|
|
193
|
+
--color-accent-600: #9333ea;
|
|
194
|
+
--color-accent-700: #7e22ce;
|
|
195
|
+
--color-accent-800: #6b21a8;
|
|
196
|
+
--color-accent-900: #581c87;
|
|
197
|
+
--color-accent-950: #3b0764;
|
|
198
|
+
|
|
199
|
+
/* Success colors */
|
|
200
|
+
--color-success-50: #f0fdf4;
|
|
201
|
+
--color-success-100: #dcfce7;
|
|
202
|
+
--color-success-200: #bbf7d0;
|
|
203
|
+
--color-success-300: #86efac;
|
|
204
|
+
--color-success-400: #4ade80;
|
|
205
|
+
--color-success-500: #22c55e;
|
|
206
|
+
--color-success-600: #16a34a;
|
|
207
|
+
--color-success-700: #15803d;
|
|
208
|
+
--color-success-800: #166534;
|
|
209
|
+
--color-success-900: #14532d;
|
|
210
|
+
--color-success-950: #052e16;
|
|
211
|
+
|
|
212
|
+
/* Warning colors */
|
|
213
|
+
--color-warning-50: #fffbeb;
|
|
214
|
+
--color-warning-100: #fef3c7;
|
|
215
|
+
--color-warning-200: #fde68a;
|
|
216
|
+
--color-warning-300: #fcd34d;
|
|
217
|
+
--color-warning-400: #fbbf24;
|
|
218
|
+
--color-warning-500: #f59e0b;
|
|
219
|
+
--color-warning-600: #d97706;
|
|
220
|
+
--color-warning-700: #b45309;
|
|
221
|
+
--color-warning-800: #92400e;
|
|
222
|
+
--color-warning-900: #78350f;
|
|
223
|
+
--color-warning-950: #451a03;
|
|
224
|
+
|
|
225
|
+
/* Danger colors */
|
|
226
|
+
--color-danger-50: #fef2f2;
|
|
227
|
+
--color-danger-100: #fee2e2;
|
|
228
|
+
--color-danger-200: #fecaca;
|
|
229
|
+
--color-danger-300: #fca5a5;
|
|
230
|
+
--color-danger-400: #f87171;
|
|
231
|
+
--color-danger-500: #ef4444;
|
|
232
|
+
--color-danger-600: #dc2626;
|
|
233
|
+
--color-danger-700: #b91c1c;
|
|
234
|
+
--color-danger-800: #991b1b;
|
|
235
|
+
--color-danger-900: #7f1d1d;
|
|
236
|
+
--color-danger-950: #450a0a;
|
|
237
|
+
|
|
238
|
+
/* Info colors */
|
|
239
|
+
--color-info-50: #ecfeff;
|
|
240
|
+
--color-info-100: #cffafe;
|
|
241
|
+
--color-info-200: #a5f3fc;
|
|
242
|
+
--color-info-300: #67e8f9;
|
|
243
|
+
--color-info-400: #22d3ee;
|
|
244
|
+
--color-info-500: #06b6d4;
|
|
245
|
+
--color-info-600: #0891b2;
|
|
246
|
+
--color-info-700: #0e7490;
|
|
247
|
+
--color-info-800: #155e75;
|
|
248
|
+
--color-info-900: #164e63;
|
|
249
|
+
--color-info-950: #083344;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/* Smooth scrolling */
|
|
253
|
+
html {
|
|
254
|
+
scroll-behavior: smooth;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/* Custom scrollbar */
|
|
258
|
+
::-webkit-scrollbar {
|
|
259
|
+
width: 8px;
|
|
260
|
+
height: 8px;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
::-webkit-scrollbar-track {
|
|
264
|
+
background: #f1f5f9;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
::-webkit-scrollbar-thumb {
|
|
268
|
+
background: #cbd5e1;
|
|
269
|
+
border-radius: 4px;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
::-webkit-scrollbar-thumb:hover {
|
|
273
|
+
background: #94a3b8;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/* RTL Support */
|
|
277
|
+
html[dir="rtl"] {
|
|
278
|
+
direction: rtl;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
html[dir="ltr"] {
|
|
282
|
+
direction: ltr;
|
|
283
|
+
}`;
|
|
284
|
+
|
|
285
|
+
await fs.writeFile(path.join(config.fullPath, "src/styles.css"), stylesFile);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Install i18n packages
|
|
290
|
+
*/
|
|
291
|
+
async function installI18nPackages(config) {
|
|
292
|
+
if (!config.skipInstall) {
|
|
293
|
+
const execa = require("execa");
|
|
294
|
+
await execa.command(
|
|
295
|
+
"npm install @ngx-translate/core@^15.0.0 @ngx-translate/http-loader@^8.0.0 --force",
|
|
296
|
+
{
|
|
297
|
+
cwd: config.fullPath,
|
|
298
|
+
stdio: "pipe",
|
|
299
|
+
}
|
|
300
|
+
);
|
|
301
|
+
} else {
|
|
302
|
+
// Add to package.json if skipInstall
|
|
303
|
+
const packageJsonPath = path.join(config.fullPath, "package.json");
|
|
304
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
305
|
+
if (!packageJson.dependencies) {
|
|
306
|
+
packageJson.dependencies = {};
|
|
307
|
+
}
|
|
308
|
+
packageJson.dependencies["@ngx-translate/core"] = "^15.0.0";
|
|
309
|
+
packageJson.dependencies["@ngx-translate/http-loader"] = "^8.0.0";
|
|
310
|
+
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
module.exports = {
|
|
315
|
+
createRouting,
|
|
316
|
+
createAppConfig,
|
|
317
|
+
createAppComponent,
|
|
318
|
+
createStyles,
|
|
319
|
+
installI18nPackages,
|
|
320
|
+
};
|