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,270 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Ecommerce Layout Components
|
|
6
|
+
* - Header (with navigation, cart icon, language switcher)
|
|
7
|
+
* - Footer (with newsletter, links)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
async function createLayout(config) {
|
|
11
|
+
// Header Component
|
|
12
|
+
const headerComponent = `import { Component, inject } from '@angular/core';
|
|
13
|
+
import { RouterModule } from '@angular/router';
|
|
14
|
+
import { NgIconComponent, provideIcons } from '@ng-icons/core';
|
|
15
|
+
import { TranslateModule } from '@ngx-translate/core';
|
|
16
|
+
import {
|
|
17
|
+
heroShoppingBag,
|
|
18
|
+
heroHeart,
|
|
19
|
+
heroUser,
|
|
20
|
+
heroMagnifyingGlass,
|
|
21
|
+
heroBars3,
|
|
22
|
+
heroXMark,
|
|
23
|
+
heroLanguage,
|
|
24
|
+
heroChevronDown,
|
|
25
|
+
} from '@ng-icons/heroicons/outline';
|
|
26
|
+
import { CartService } from '@core/services/cart.service';
|
|
27
|
+
import { WishlistService } from '@core/services/wishlist.service';
|
|
28
|
+
import { TranslationService, type SupportedLanguage, type LanguageOption } from '@core/i18n/translation.service';
|
|
29
|
+
|
|
30
|
+
@Component({
|
|
31
|
+
selector: 'app-header',
|
|
32
|
+
standalone: true,
|
|
33
|
+
imports: [RouterModule, NgIconComponent, TranslateModule],
|
|
34
|
+
viewProviders: [provideIcons({ heroShoppingBag, heroHeart, heroUser, heroMagnifyingGlass, heroBars3, heroXMark, heroLanguage, heroChevronDown })],
|
|
35
|
+
template: \`
|
|
36
|
+
<header class="sticky top-0 z-50 border-b border-gray-200 bg-white/95 backdrop-blur-sm">
|
|
37
|
+
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
|
38
|
+
<div class="flex h-16 items-center justify-between">
|
|
39
|
+
<!-- Logo -->
|
|
40
|
+
<a routerLink="/" class="flex items-center gap-2">
|
|
41
|
+
<div class="flex h-10 w-10 items-center justify-center rounded-xl bg-linear-to-br from-primary-500 to-primary-600 shadow-lg shadow-primary-500/30">
|
|
42
|
+
<ng-icon name="heroShoppingBag" size="20" style="color: white;"></ng-icon>
|
|
43
|
+
</div>
|
|
44
|
+
<span class="text-xl font-bold text-gray-900">${config.projectName}</span>
|
|
45
|
+
</a>
|
|
46
|
+
|
|
47
|
+
<!-- Desktop Navigation -->
|
|
48
|
+
<nav class="hidden md:flex items-center gap-8">
|
|
49
|
+
<a routerLink="/" routerLinkActive="text-primary-600" [routerLinkActiveOptions]="{exact: true}"
|
|
50
|
+
class="text-gray-600 hover:text-primary-600 transition-colors font-medium">
|
|
51
|
+
{{ 'nav.home' | translate }}
|
|
52
|
+
</a>
|
|
53
|
+
<a routerLink="/products" routerLinkActive="text-primary-600"
|
|
54
|
+
class="text-gray-600 hover:text-primary-600 transition-colors font-medium">
|
|
55
|
+
{{ 'nav.shop' | translate }}
|
|
56
|
+
</a>
|
|
57
|
+
<a routerLink="/wishlist" routerLinkActive="text-primary-600"
|
|
58
|
+
class="text-gray-600 hover:text-primary-600 transition-colors font-medium">
|
|
59
|
+
{{ 'nav.wishlist' | translate }}
|
|
60
|
+
</a>
|
|
61
|
+
</nav>
|
|
62
|
+
|
|
63
|
+
<!-- Right Side -->
|
|
64
|
+
<div class="flex items-center gap-4">
|
|
65
|
+
<!-- Search -->
|
|
66
|
+
<button class="hidden sm:flex h-10 w-10 items-center justify-center rounded-full text-gray-600 hover:bg-gray-100 transition-colors">
|
|
67
|
+
<ng-icon name="heroMagnifyingGlass" size="20"></ng-icon>
|
|
68
|
+
</button>
|
|
69
|
+
|
|
70
|
+
<!-- Language Switcher -->
|
|
71
|
+
<div class="relative">
|
|
72
|
+
<button
|
|
73
|
+
(click)="toggleLanguageDropdown()"
|
|
74
|
+
class="flex items-center gap-1 rounded-full px-3 py-2 text-gray-600 hover:bg-gray-100 transition-colors">
|
|
75
|
+
<ng-icon name="heroLanguage" size="20"></ng-icon>
|
|
76
|
+
<span class="hidden sm:inline text-sm">{{ currentLanguage.flag }}</span>
|
|
77
|
+
<ng-icon name="heroChevronDown" size="14" [class.rotate-180]="languageDropdownOpen"></ng-icon>
|
|
78
|
+
</button>
|
|
79
|
+
|
|
80
|
+
@if (languageDropdownOpen) {
|
|
81
|
+
<div class="absolute end-0 top-full mt-1 w-36 rounded-lg border border-gray-200 bg-white py-1 shadow-lg z-50">
|
|
82
|
+
@for (lang of translationService.languages; track lang.code) {
|
|
83
|
+
<button
|
|
84
|
+
(click)="setLanguage(lang.code)"
|
|
85
|
+
class="flex w-full items-center gap-2 px-4 py-2 text-sm hover:bg-gray-50 transition-colors"
|
|
86
|
+
[class.bg-primary-50]="lang.code === currentLang"
|
|
87
|
+
[class.text-primary-600]="lang.code === currentLang">
|
|
88
|
+
<span>{{ lang.flag }}</span>
|
|
89
|
+
<span>{{ lang.nativeName }}</span>
|
|
90
|
+
</button>
|
|
91
|
+
}
|
|
92
|
+
</div>
|
|
93
|
+
}
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<!-- Wishlist -->
|
|
97
|
+
<a routerLink="/wishlist" class="relative flex h-10 w-10 items-center justify-center rounded-full text-gray-600 hover:bg-gray-100 transition-colors">
|
|
98
|
+
<ng-icon name="heroHeart" size="20"></ng-icon>
|
|
99
|
+
@if (wishlistCount > 0) {
|
|
100
|
+
<span class="absolute -end-1 -top-1 flex h-5 w-5 items-center justify-center rounded-full bg-danger-500 text-xs font-bold text-white">
|
|
101
|
+
{{ wishlistCount }}
|
|
102
|
+
</span>
|
|
103
|
+
}
|
|
104
|
+
</a>
|
|
105
|
+
|
|
106
|
+
<!-- Cart -->
|
|
107
|
+
<a routerLink="/cart" class="relative flex h-10 w-10 items-center justify-center rounded-full bg-primary-500 text-white shadow-lg shadow-primary-500/30 hover:bg-primary-600 transition-colors">
|
|
108
|
+
<ng-icon name="heroShoppingBag" size="20"></ng-icon>
|
|
109
|
+
@if (cartCount > 0) {
|
|
110
|
+
<span class="absolute -end-1 -top-1 flex h-5 w-5 items-center justify-center rounded-full bg-danger-500 text-xs font-bold text-white">
|
|
111
|
+
{{ cartCount }}
|
|
112
|
+
</span>
|
|
113
|
+
}
|
|
114
|
+
</a>
|
|
115
|
+
|
|
116
|
+
<!-- Mobile Menu Button -->
|
|
117
|
+
<button
|
|
118
|
+
(click)="mobileMenuOpen = !mobileMenuOpen"
|
|
119
|
+
class="flex md:hidden h-10 w-10 items-center justify-center rounded-full text-gray-600 hover:bg-gray-100 transition-colors">
|
|
120
|
+
<ng-icon [name]="mobileMenuOpen ? 'heroXMark' : 'heroBars3'" size="24"></ng-icon>
|
|
121
|
+
</button>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
|
|
125
|
+
<!-- Mobile Navigation -->
|
|
126
|
+
@if (mobileMenuOpen) {
|
|
127
|
+
<nav class="md:hidden border-t border-gray-200 py-4 space-y-2">
|
|
128
|
+
<a routerLink="/" (click)="mobileMenuOpen = false"
|
|
129
|
+
class="block px-4 py-2 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors">
|
|
130
|
+
{{ 'nav.home' | translate }}
|
|
131
|
+
</a>
|
|
132
|
+
<a routerLink="/products" (click)="mobileMenuOpen = false"
|
|
133
|
+
class="block px-4 py-2 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors">
|
|
134
|
+
{{ 'nav.shop' | translate }}
|
|
135
|
+
</a>
|
|
136
|
+
<a routerLink="/wishlist" (click)="mobileMenuOpen = false"
|
|
137
|
+
class="block px-4 py-2 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors">
|
|
138
|
+
{{ 'nav.wishlist' | translate }}
|
|
139
|
+
</a>
|
|
140
|
+
</nav>
|
|
141
|
+
}
|
|
142
|
+
</div>
|
|
143
|
+
</header>
|
|
144
|
+
\`,
|
|
145
|
+
})
|
|
146
|
+
export class HeaderComponent {
|
|
147
|
+
private cartService = inject(CartService);
|
|
148
|
+
private wishlistService = inject(WishlistService);
|
|
149
|
+
translationService = inject(TranslationService);
|
|
150
|
+
|
|
151
|
+
mobileMenuOpen = false;
|
|
152
|
+
languageDropdownOpen = false;
|
|
153
|
+
currentLang: SupportedLanguage = 'en';
|
|
154
|
+
currentLanguage: LanguageOption = this.translationService.languages[0];
|
|
155
|
+
|
|
156
|
+
constructor() {
|
|
157
|
+
this.currentLang = this.translationService.getCurrentLanguage();
|
|
158
|
+
this.updateCurrentLanguage();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
get cartCount(): number {
|
|
162
|
+
return this.cartService.itemCount();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
get wishlistCount(): number {
|
|
166
|
+
return this.wishlistService.itemCount();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
toggleLanguageDropdown(): void {
|
|
170
|
+
this.languageDropdownOpen = !this.languageDropdownOpen;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
setLanguage(lang: SupportedLanguage): void {
|
|
174
|
+
this.translationService.setLanguage(lang);
|
|
175
|
+
this.currentLang = lang;
|
|
176
|
+
this.updateCurrentLanguage();
|
|
177
|
+
this.languageDropdownOpen = false;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private updateCurrentLanguage(): void {
|
|
181
|
+
const langOption = this.translationService.getLanguageOption(this.currentLang);
|
|
182
|
+
if (langOption) {
|
|
183
|
+
this.currentLanguage = langOption;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}`;
|
|
187
|
+
|
|
188
|
+
await fs.writeFile(
|
|
189
|
+
path.join(config.fullPath, 'src/app/layout/header/header.component.ts'),
|
|
190
|
+
headerComponent
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
// Footer Component
|
|
194
|
+
const footerComponent = `import { Component } from '@angular/core';
|
|
195
|
+
import { RouterModule } from '@angular/router';
|
|
196
|
+
import { TranslateModule } from '@ngx-translate/core';
|
|
197
|
+
import { NgIconComponent, provideIcons } from '@ng-icons/core';
|
|
198
|
+
import { heroEnvelope } from '@ng-icons/heroicons/outline';
|
|
199
|
+
|
|
200
|
+
@Component({
|
|
201
|
+
selector: 'app-footer',
|
|
202
|
+
standalone: true,
|
|
203
|
+
imports: [RouterModule, TranslateModule, NgIconComponent],
|
|
204
|
+
viewProviders: [provideIcons({ heroEnvelope })],
|
|
205
|
+
template: \`
|
|
206
|
+
<footer class="border-t border-gray-200 bg-gray-50">
|
|
207
|
+
<div class="mx-auto max-w-7xl px-4 py-12 sm:px-6 lg:px-8">
|
|
208
|
+
<div class="grid grid-cols-1 gap-8 md:grid-cols-4">
|
|
209
|
+
<!-- Brand -->
|
|
210
|
+
<div class="col-span-1 md:col-span-2">
|
|
211
|
+
<h3 class="text-lg font-bold text-gray-900">${config.projectName}</h3>
|
|
212
|
+
<p class="mt-2 text-gray-600">{{ 'footer.description' | translate }}</p>
|
|
213
|
+
|
|
214
|
+
<!-- Newsletter -->
|
|
215
|
+
<div class="mt-6">
|
|
216
|
+
<p class="font-medium text-gray-900">{{ 'footer.newsletter' | translate }}</p>
|
|
217
|
+
<div class="mt-2 flex gap-2">
|
|
218
|
+
<input
|
|
219
|
+
type="email"
|
|
220
|
+
[placeholder]="'footer.emailPlaceholder' | translate"
|
|
221
|
+
class="flex-1 rounded-lg border border-gray-300 px-4 py-2 focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500"
|
|
222
|
+
/>
|
|
223
|
+
<button class="rounded-lg bg-primary-500 px-4 py-2 text-white hover:bg-primary-600 transition-colors">
|
|
224
|
+
<ng-icon name="heroEnvelope" size="20"></ng-icon>
|
|
225
|
+
</button>
|
|
226
|
+
</div>
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
229
|
+
|
|
230
|
+
<!-- Quick Links -->
|
|
231
|
+
<div>
|
|
232
|
+
<h4 class="font-semibold text-gray-900">{{ 'footer.quickLinks' | translate }}</h4>
|
|
233
|
+
<ul class="mt-4 space-y-2">
|
|
234
|
+
<li><a routerLink="/" class="text-gray-600 hover:text-primary-600 transition-colors">{{ 'nav.home' | translate }}</a></li>
|
|
235
|
+
<li><a routerLink="/products" class="text-gray-600 hover:text-primary-600 transition-colors">{{ 'nav.shop' | translate }}</a></li>
|
|
236
|
+
<li><a routerLink="/cart" class="text-gray-600 hover:text-primary-600 transition-colors">{{ 'nav.cart' | translate }}</a></li>
|
|
237
|
+
<li><a routerLink="/wishlist" class="text-gray-600 hover:text-primary-600 transition-colors">{{ 'nav.wishlist' | translate }}</a></li>
|
|
238
|
+
</ul>
|
|
239
|
+
</div>
|
|
240
|
+
|
|
241
|
+
<!-- Support -->
|
|
242
|
+
<div>
|
|
243
|
+
<h4 class="font-semibold text-gray-900">{{ 'footer.support' | translate }}</h4>
|
|
244
|
+
<ul class="mt-4 space-y-2">
|
|
245
|
+
<li><a href="#" class="text-gray-600 hover:text-primary-600 transition-colors">{{ 'footer.faq' | translate }}</a></li>
|
|
246
|
+
<li><a href="#" class="text-gray-600 hover:text-primary-600 transition-colors">{{ 'footer.shipping' | translate }}</a></li>
|
|
247
|
+
<li><a href="#" class="text-gray-600 hover:text-primary-600 transition-colors">{{ 'footer.returns' | translate }}</a></li>
|
|
248
|
+
<li><a href="#" class="text-gray-600 hover:text-primary-600 transition-colors">{{ 'footer.contact' | translate }}</a></li>
|
|
249
|
+
</ul>
|
|
250
|
+
</div>
|
|
251
|
+
</div>
|
|
252
|
+
|
|
253
|
+
<div class="mt-8 border-t border-gray-200 pt-8 text-center text-sm text-gray-500">
|
|
254
|
+
<p>© {{ currentYear }} ${config.projectName}. {{ 'footer.rights' | translate }}</p>
|
|
255
|
+
</div>
|
|
256
|
+
</div>
|
|
257
|
+
</footer>
|
|
258
|
+
\`,
|
|
259
|
+
})
|
|
260
|
+
export class FooterComponent {
|
|
261
|
+
currentYear = new Date().getFullYear();
|
|
262
|
+
}`;
|
|
263
|
+
|
|
264
|
+
await fs.writeFile(
|
|
265
|
+
path.join(config.fullPath, 'src/app/layout/footer/footer.component.ts'),
|
|
266
|
+
footerComponent
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
module.exports = { createLayout };
|