create-ng-tailwind 2.0.2 → 3.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 +120 -0
- package/README.md +115 -177
- package/lib/managers/ProjectManager.js +19 -27
- package/lib/templates/starter/advanced-features.js +9 -10
- package/lib/templates/starter/features.js +228 -61
- package/lib/templates/starter/index.js +272 -216
- package/lib/templates/starter/seo-assets.js +141 -0
- package/lib/templates/starter/seo-features.js +290 -0
- package/lib/templates/starter/ui-features.js +1 -2
- package/lib/utils/ai-config.js +97 -58
- package/package.json +3 -4
- package/CLAUDE.md +0 -178
|
@@ -27,6 +27,17 @@ const {
|
|
|
27
27
|
createTooltipDirective,
|
|
28
28
|
} = require('./ui-features');
|
|
29
29
|
|
|
30
|
+
const { createSEOService, createStructuredDataUtil, createRobotsTxt } = require('./seo-features');
|
|
31
|
+
|
|
32
|
+
const {
|
|
33
|
+
createDefaultOGImage,
|
|
34
|
+
createFavicon,
|
|
35
|
+
createAppleTouchIcon,
|
|
36
|
+
createAndroidIcon,
|
|
37
|
+
createAndroidIconLarge,
|
|
38
|
+
createLogo,
|
|
39
|
+
} = require('./seo-assets');
|
|
40
|
+
|
|
30
41
|
const starter = {
|
|
31
42
|
info: {
|
|
32
43
|
name: 'Starter',
|
|
@@ -37,6 +48,10 @@ const starter = {
|
|
|
37
48
|
'HTTP Interceptors (Auth, Error, Loading, Caching)',
|
|
38
49
|
'i18n translation support (English & Arabic) with RTL',
|
|
39
50
|
'Language switcher in header (desktop & mobile)',
|
|
51
|
+
'Comprehensive SEO service (meta tags, Open Graph, Twitter Cards)',
|
|
52
|
+
'Structured data (JSON-LD) for rich search results',
|
|
53
|
+
'Multi-language SEO with hreflang tags',
|
|
54
|
+
'robots.txt and favicon set included',
|
|
40
55
|
'Toast/Notification system with auto-dismiss',
|
|
41
56
|
'Modal/Dialog system with confirm & alert',
|
|
42
57
|
'Essential UI components (Button, Card, Spinner, Toast, Modal)',
|
|
@@ -46,7 +61,7 @@ const starter = {
|
|
|
46
61
|
'Authentication UI (Login, Register, Forgot Password)',
|
|
47
62
|
'3 example pages (Home, About, Contact)',
|
|
48
63
|
'Reactive forms with validation',
|
|
49
|
-
'API, Auth, Storage, Loading, Cache services',
|
|
64
|
+
'API, Auth, Storage, Loading, Cache, SEO services',
|
|
50
65
|
'TypeScript interfaces and models',
|
|
51
66
|
'Responsive Tailwind design system',
|
|
52
67
|
'ESLint + Prettier + Husky pre-commit hooks',
|
|
@@ -74,6 +89,7 @@ const starter = {
|
|
|
74
89
|
await this.createDirectoryStructure(config);
|
|
75
90
|
await this.createEnvironments(config);
|
|
76
91
|
await this.createCoreServices(config);
|
|
92
|
+
await this.createSEOFeatures(config);
|
|
77
93
|
await this.createHttpInterceptors(config);
|
|
78
94
|
await this.createI18n(config);
|
|
79
95
|
await this.createGuards(config);
|
|
@@ -97,17 +113,18 @@ const starter = {
|
|
|
97
113
|
await this.setupLinting(config);
|
|
98
114
|
await this.setupPWA(config);
|
|
99
115
|
await this.formatCode(config);
|
|
100
|
-
|
|
116
|
+
|
|
101
117
|
// Stop spinner and show summary
|
|
102
118
|
if (spinner) spinner.stop();
|
|
103
|
-
|
|
119
|
+
|
|
104
120
|
// Show minimal, professional summary
|
|
105
121
|
console.log('');
|
|
106
122
|
completeStep('Angular 20+ project created');
|
|
107
123
|
completeStep('Tailwind CSS v4 configured');
|
|
108
124
|
completeStep('i18n translation support (English & Arabic)');
|
|
125
|
+
completeStep('SEO optimization (meta tags, Open Graph, structured data)');
|
|
109
126
|
completeStep('HTTP interceptors (Auth, Error, Loading, Cache)');
|
|
110
|
-
completeStep('Core services (
|
|
127
|
+
completeStep('Core services (9 services including SEO)');
|
|
111
128
|
completeStep('UI components (Button, Card, Toast, Modal, Spinner)');
|
|
112
129
|
completeStep('Utility pipes & directives');
|
|
113
130
|
completeStep('Authentication pages & guards');
|
|
@@ -123,6 +140,7 @@ const starter = {
|
|
|
123
140
|
'src/app/core/guards',
|
|
124
141
|
'src/app/core/i18n',
|
|
125
142
|
'src/app/core/interceptors',
|
|
143
|
+
'src/app/core/utils',
|
|
126
144
|
'src/app/shared/components/button',
|
|
127
145
|
'src/app/shared/components/card',
|
|
128
146
|
'src/app/shared/components/loading-spinner',
|
|
@@ -142,8 +160,7 @@ const starter = {
|
|
|
142
160
|
'src/app/layout/auth',
|
|
143
161
|
'public/assets/images',
|
|
144
162
|
'public/assets/i18n',
|
|
145
|
-
'public/
|
|
146
|
-
'public/styles',
|
|
163
|
+
'public/assets/icons',
|
|
147
164
|
];
|
|
148
165
|
|
|
149
166
|
for (const dir of directories) {
|
|
@@ -545,6 +562,58 @@ export class StorageService {
|
|
|
545
562
|
);
|
|
546
563
|
},
|
|
547
564
|
|
|
565
|
+
async createSEOFeatures(config) {
|
|
566
|
+
// Create SEO Service
|
|
567
|
+
const seoService = createSEOService();
|
|
568
|
+
await fs.writeFile(
|
|
569
|
+
path.join(config.fullPath, 'src/app/core/services/seo.service.ts'),
|
|
570
|
+
seoService
|
|
571
|
+
);
|
|
572
|
+
|
|
573
|
+
// Create Structured Data Utility
|
|
574
|
+
const structuredDataUtil = createStructuredDataUtil();
|
|
575
|
+
await fs.writeFile(
|
|
576
|
+
path.join(config.fullPath, 'src/app/core/utils/structured-data.ts'),
|
|
577
|
+
structuredDataUtil
|
|
578
|
+
);
|
|
579
|
+
|
|
580
|
+
// Create robots.txt in public folder
|
|
581
|
+
const robotsTxt = createRobotsTxt();
|
|
582
|
+
await fs.writeFile(path.join(config.fullPath, 'public/robots.txt'), robotsTxt);
|
|
583
|
+
|
|
584
|
+
// Create default OG image (SVG as placeholder)
|
|
585
|
+
const ogImage = createDefaultOGImage(config.projectName);
|
|
586
|
+
await fs.writeFile(path.join(config.fullPath, 'public/assets/images/og-default.svg'), ogImage);
|
|
587
|
+
|
|
588
|
+
// Create favicon
|
|
589
|
+
const favicon = createFavicon(config.projectName);
|
|
590
|
+
await fs.writeFile(path.join(config.fullPath, 'public/favicon.svg'), favicon);
|
|
591
|
+
|
|
592
|
+
// Create Apple Touch Icon (SVG as placeholder)
|
|
593
|
+
const appleTouchIcon = createAppleTouchIcon(config.projectName);
|
|
594
|
+
await fs.writeFile(
|
|
595
|
+
path.join(config.fullPath, 'public/assets/icons/apple-touch-icon.svg'),
|
|
596
|
+
appleTouchIcon
|
|
597
|
+
);
|
|
598
|
+
|
|
599
|
+
// Create Android Chrome icons (SVG as placeholder)
|
|
600
|
+
const androidIcon = createAndroidIcon(config.projectName);
|
|
601
|
+
await fs.writeFile(
|
|
602
|
+
path.join(config.fullPath, 'public/assets/icons/android-chrome-192x192.svg'),
|
|
603
|
+
androidIcon
|
|
604
|
+
);
|
|
605
|
+
|
|
606
|
+
const androidIconLarge = createAndroidIconLarge(config.projectName);
|
|
607
|
+
await fs.writeFile(
|
|
608
|
+
path.join(config.fullPath, 'public/assets/icons/android-chrome-512x512.svg'),
|
|
609
|
+
androidIconLarge
|
|
610
|
+
);
|
|
611
|
+
|
|
612
|
+
// Create logo
|
|
613
|
+
const logo = createLogo(config.projectName);
|
|
614
|
+
await fs.writeFile(path.join(config.fullPath, 'public/assets/images/logo.svg'), logo);
|
|
615
|
+
},
|
|
616
|
+
|
|
548
617
|
async createHttpInterceptors(config) {
|
|
549
618
|
// Create all HTTP interceptors
|
|
550
619
|
await createAuthInterceptor(config);
|
|
@@ -1037,8 +1106,7 @@ export class TranslationService {
|
|
|
1037
1106
|
'Built with Angular and Tailwind CSS. A modern, fast, and responsive web application starter template.',
|
|
1038
1107
|
quickLinks: 'Quick Links',
|
|
1039
1108
|
builtWith: 'Built With',
|
|
1040
|
-
copyright: '© {{year}} {{name}}.
|
|
1041
|
-
madeWith: 'Made with ❤️ by developers, for developers',
|
|
1109
|
+
copyright: '© {{year}} {{name}}. All rights reserved.',
|
|
1042
1110
|
},
|
|
1043
1111
|
language: {
|
|
1044
1112
|
select: 'Select Language',
|
|
@@ -1354,8 +1422,7 @@ export class TranslationService {
|
|
|
1354
1422
|
description: 'مبني بـ Angular و Tailwind CSS. قالب تطبيق ويب حديث وسريع ومتجاوب.',
|
|
1355
1423
|
quickLinks: 'روابط سريعة',
|
|
1356
1424
|
builtWith: 'مبني بـ',
|
|
1357
|
-
copyright: '© {{year}} {{name}}.
|
|
1358
|
-
madeWith: 'صُنع بـ ❤️ من قبل المطورين، للمطورين',
|
|
1425
|
+
copyright: '© {{year}} {{name}}. جميع الحقوق محفوظة.',
|
|
1359
1426
|
},
|
|
1360
1427
|
language: {
|
|
1361
1428
|
select: 'اختر اللغة',
|
|
@@ -1433,7 +1500,6 @@ export const roleGuard = (requiredRole: string): CanActivateFn => {
|
|
|
1433
1500
|
async createSharedComponents(config) {
|
|
1434
1501
|
// Button Component
|
|
1435
1502
|
const buttonComponent = `import { Component, Input, Output, EventEmitter } from '@angular/core';
|
|
1436
|
-
import { CommonModule } from '@angular/common';
|
|
1437
1503
|
|
|
1438
1504
|
export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost' | 'danger';
|
|
1439
1505
|
export type ButtonSize = 'sm' | 'md' | 'lg';
|
|
@@ -1441,7 +1507,7 @@ export type ButtonSize = 'sm' | 'md' | 'lg';
|
|
|
1441
1507
|
@Component({
|
|
1442
1508
|
selector: 'app-button',
|
|
1443
1509
|
standalone: true,
|
|
1444
|
-
imports: [
|
|
1510
|
+
imports: [],
|
|
1445
1511
|
template: \`
|
|
1446
1512
|
<button
|
|
1447
1513
|
[class]="buttonClasses"
|
|
@@ -1471,7 +1537,7 @@ export class ButtonComponent {
|
|
|
1471
1537
|
@Output() clicked = new EventEmitter<Event>();
|
|
1472
1538
|
|
|
1473
1539
|
get buttonClasses(): string {
|
|
1474
|
-
const baseClasses = 'inline-flex items-center justify-center font-medium rounded-lg transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-
|
|
1540
|
+
const baseClasses = 'inline-flex items-center justify-center font-medium rounded-lg transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50';
|
|
1475
1541
|
|
|
1476
1542
|
const sizeClasses = {
|
|
1477
1543
|
sm: 'text-sm px-3 py-2 h-9',
|
|
@@ -1480,11 +1546,11 @@ export class ButtonComponent {
|
|
|
1480
1546
|
};
|
|
1481
1547
|
|
|
1482
1548
|
const variantClasses = {
|
|
1483
|
-
primary: 'bg-
|
|
1484
|
-
secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300',
|
|
1485
|
-
outline: 'border-2 border-
|
|
1486
|
-
ghost: 'hover:bg-gray-100 hover:text-gray-900',
|
|
1487
|
-
danger: 'bg-
|
|
1549
|
+
primary: 'bg-primary-600 text-white hover:bg-primary-700 focus:ring-primary-500',
|
|
1550
|
+
secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300 focus:ring-gray-400',
|
|
1551
|
+
outline: 'border-2 border-primary-600 text-primary-600 hover:bg-primary-600 hover:text-white focus:ring-primary-500',
|
|
1552
|
+
ghost: 'hover:bg-gray-100 hover:text-gray-900 focus:ring-gray-400',
|
|
1553
|
+
danger: 'bg-danger-600 text-white hover:bg-danger-700 focus:ring-danger-500'
|
|
1488
1554
|
};
|
|
1489
1555
|
|
|
1490
1556
|
const widthClass = this.fullWidth ? 'w-full' : '';
|
|
@@ -1506,12 +1572,11 @@ export class ButtonComponent {
|
|
|
1506
1572
|
|
|
1507
1573
|
// Card Component
|
|
1508
1574
|
const cardComponent = `import { Component, Input } from '@angular/core';
|
|
1509
|
-
import { CommonModule } from '@angular/common';
|
|
1510
1575
|
|
|
1511
1576
|
@Component({
|
|
1512
1577
|
selector: 'app-card',
|
|
1513
1578
|
standalone: true,
|
|
1514
|
-
imports: [
|
|
1579
|
+
imports: [],
|
|
1515
1580
|
template: \`
|
|
1516
1581
|
<div [class]="cardClasses">
|
|
1517
1582
|
@if (title || subtitle) {
|
|
@@ -1569,14 +1634,13 @@ export class CardComponent {
|
|
|
1569
1634
|
|
|
1570
1635
|
// Loading Spinner Component
|
|
1571
1636
|
const spinnerComponent = `import { Component, Input } from '@angular/core';
|
|
1572
|
-
import { CommonModule } from '@angular/common';
|
|
1573
1637
|
|
|
1574
1638
|
export type SpinnerSize = 'sm' | 'md' | 'lg' | 'xl';
|
|
1575
1639
|
|
|
1576
1640
|
@Component({
|
|
1577
1641
|
selector: 'app-loading-spinner',
|
|
1578
1642
|
standalone: true,
|
|
1579
|
-
imports: [
|
|
1643
|
+
imports: [],
|
|
1580
1644
|
template: \`
|
|
1581
1645
|
<div [class]="containerClasses">
|
|
1582
1646
|
<svg [class]="spinnerClasses" fill="none" viewBox="0 0 24 24">
|
|
@@ -1602,7 +1666,7 @@ export class LoadingSpinnerComponent {
|
|
|
1602
1666
|
@Input() size: SpinnerSize = 'md';
|
|
1603
1667
|
@Input() text?: string;
|
|
1604
1668
|
@Input() centered = false;
|
|
1605
|
-
@Input() color = '
|
|
1669
|
+
@Input() color = 'primary';
|
|
1606
1670
|
|
|
1607
1671
|
private readonly sizeClasses = {
|
|
1608
1672
|
sm: 'h-4 w-4',
|
|
@@ -1612,11 +1676,15 @@ export class LoadingSpinnerComponent {
|
|
|
1612
1676
|
} as const;
|
|
1613
1677
|
|
|
1614
1678
|
private readonly colorClasses = {
|
|
1615
|
-
|
|
1679
|
+
primary: 'text-primary-600',
|
|
1680
|
+
secondary: 'text-secondary-600',
|
|
1681
|
+
accent: 'text-accent-600',
|
|
1682
|
+
success: 'text-success-600',
|
|
1683
|
+
danger: 'text-danger-600',
|
|
1684
|
+
warning: 'text-warning-600',
|
|
1685
|
+
info: 'text-info-600',
|
|
1616
1686
|
gray: 'text-gray-600',
|
|
1617
|
-
white: 'text-white'
|
|
1618
|
-
green: 'text-green-600',
|
|
1619
|
-
red: 'text-red-600'
|
|
1687
|
+
white: 'text-white'
|
|
1620
1688
|
} as const;
|
|
1621
1689
|
|
|
1622
1690
|
protected readonly textClasses = 'text-sm text-gray-600';
|
|
@@ -1631,7 +1699,7 @@ export class LoadingSpinnerComponent {
|
|
|
1631
1699
|
|
|
1632
1700
|
get spinnerClasses(): string {
|
|
1633
1701
|
const baseClasses = 'animate-spin';
|
|
1634
|
-
return \`\${baseClasses} \${this.sizeClasses[this.size]} \${this.colorClasses[this.color as keyof typeof this.colorClasses] || this.colorClasses.
|
|
1702
|
+
return \`\${baseClasses} \${this.sizeClasses[this.size]} \${this.colorClasses[this.color as keyof typeof this.colorClasses] || this.colorClasses.primary}\`.trim();
|
|
1635
1703
|
}
|
|
1636
1704
|
}`;
|
|
1637
1705
|
|
|
@@ -1807,7 +1875,6 @@ export interface ContactFormData {
|
|
|
1807
1875
|
async createLayout(config) {
|
|
1808
1876
|
// Header Component with Authentication and Language Switcher
|
|
1809
1877
|
const headerComponentTs = `import { Component, inject, OnInit } from '@angular/core';
|
|
1810
|
-
import { CommonModule } from '@angular/common';
|
|
1811
1878
|
import { RouterModule, Router } from '@angular/router';
|
|
1812
1879
|
import { NgIconComponent, provideIcons } from '@ng-icons/core';
|
|
1813
1880
|
import { heroHome, heroInformationCircle, heroEnvelope, heroUser, heroBars3, heroXMark, heroChevronDown, heroLanguage } from '@ng-icons/heroicons/outline';
|
|
@@ -1825,7 +1892,7 @@ interface User {
|
|
|
1825
1892
|
@Component({
|
|
1826
1893
|
selector: 'app-header',
|
|
1827
1894
|
standalone: true,
|
|
1828
|
-
imports: [
|
|
1895
|
+
imports: [RouterModule, NgIconComponent, TranslateModule],
|
|
1829
1896
|
viewProviders: [provideIcons({ heroHome, heroInformationCircle, heroEnvelope, heroUser, heroBars3, heroXMark, heroChevronDown, heroLanguage })],
|
|
1830
1897
|
templateUrl: './header.component.html'
|
|
1831
1898
|
})
|
|
@@ -1892,7 +1959,8 @@ export class HeaderComponent implements OnInit {
|
|
|
1892
1959
|
<!-- Logo -->
|
|
1893
1960
|
<div class="flex items-center space-x-4">
|
|
1894
1961
|
<a routerLink="/" class="flex items-center space-x-3">
|
|
1895
|
-
|
|
1962
|
+
<div class="flex rounded-lg">
|
|
1963
|
+
<img src="assets/images/logo.svg" alt="Logo" class="h-10 w-10" />
|
|
1896
1964
|
<span class="text-white font-bold text-sm">{{ projectName.charAt(0).toUpperCase() }}</span>
|
|
1897
1965
|
</div>
|
|
1898
1966
|
<span class="text-xl font-semibold text-gray-900">
|
|
@@ -1906,23 +1974,23 @@ export class HeaderComponent implements OnInit {
|
|
|
1906
1974
|
<div class="ml-10 flex items-center space-x-8">
|
|
1907
1975
|
<a
|
|
1908
1976
|
routerLink="/"
|
|
1909
|
-
routerLinkActive="text-
|
|
1977
|
+
routerLinkActive="text-primary-600"
|
|
1910
1978
|
[routerLinkActiveOptions]="{exact: true}"
|
|
1911
|
-
class="flex items-center gap-1.5 text-gray-700 hover:text-
|
|
1979
|
+
class="flex items-center gap-1.5 text-gray-700 hover:text-primary-600 transition-colors font-medium">
|
|
1912
1980
|
<ng-icon name="heroHome" size="18"></ng-icon>
|
|
1913
1981
|
{{ 'nav.home' | translate }}
|
|
1914
1982
|
</a>
|
|
1915
1983
|
<a
|
|
1916
1984
|
routerLink="/about"
|
|
1917
|
-
routerLinkActive="text-
|
|
1918
|
-
class="flex items-center gap-1.5 text-gray-700 hover:text-
|
|
1985
|
+
routerLinkActive="text-primary-600"
|
|
1986
|
+
class="flex items-center gap-1.5 text-gray-700 hover:text-primary-600 transition-colors font-medium">
|
|
1919
1987
|
<ng-icon name="heroInformationCircle" size="18"></ng-icon>
|
|
1920
1988
|
{{ 'nav.about' | translate }}
|
|
1921
1989
|
</a>
|
|
1922
1990
|
<a
|
|
1923
1991
|
routerLink="/contact"
|
|
1924
|
-
routerLinkActive="text-
|
|
1925
|
-
class="flex items-center gap-1.5 text-gray-700 hover:text-
|
|
1992
|
+
routerLinkActive="text-primary-600"
|
|
1993
|
+
class="flex items-center gap-1.5 text-gray-700 hover:text-primary-600 transition-colors font-medium">
|
|
1926
1994
|
<ng-icon name="heroEnvelope" size="18"></ng-icon>
|
|
1927
1995
|
{{ 'nav.contact' | translate }}
|
|
1928
1996
|
</a>
|
|
@@ -1936,8 +2004,8 @@ export class HeaderComponent implements OnInit {
|
|
|
1936
2004
|
<div class="hidden md:block relative">
|
|
1937
2005
|
<button
|
|
1938
2006
|
(click)="langMenuOpen = !langMenuOpen"
|
|
1939
|
-
class="flex items-center space-x-2 text-gray-700 hover:text-
|
|
1940
|
-
[class.text-
|
|
2007
|
+
class="flex items-center space-x-2 text-gray-700 hover:text-primary-600 transition-colors px-3 py-2 rounded-lg hover:bg-gray-100"
|
|
2008
|
+
[class.text-primary-600]="langMenuOpen"
|
|
1941
2009
|
[class.bg-gray-100]="langMenuOpen">
|
|
1942
2010
|
<ng-icon name="heroLanguage" size="20"></ng-icon>
|
|
1943
2011
|
<span class="font-medium text-sm">{{ currentLanguage.nativeName }}</span>
|
|
@@ -1952,8 +2020,8 @@ export class HeaderComponent implements OnInit {
|
|
|
1952
2020
|
<button
|
|
1953
2021
|
(click)="changeLanguage(lang.code)"
|
|
1954
2022
|
class="w-full text-left px-4 py-2 text-sm transition-colors flex items-center justify-between"
|
|
1955
|
-
[class.bg-
|
|
1956
|
-
[class.text-
|
|
2023
|
+
[class.bg-primary-50]="currentLang === lang.code"
|
|
2024
|
+
[class.text-primary-600]="currentLang === lang.code"
|
|
1957
2025
|
[class.text-gray-700]="currentLang !== lang.code"
|
|
1958
2026
|
[class.hover:bg-gray-100]="currentLang !== lang.code">
|
|
1959
2027
|
<span class="flex items-center space-x-2">
|
|
@@ -1978,12 +2046,12 @@ export class HeaderComponent implements OnInit {
|
|
|
1978
2046
|
@if (!isAuthenticated) {
|
|
1979
2047
|
<a
|
|
1980
2048
|
routerLink="/auth/login"
|
|
1981
|
-
class="text-gray-700 hover:text-
|
|
2049
|
+
class="text-gray-700 hover:text-primary-600 transition-colors font-medium">
|
|
1982
2050
|
{{ 'nav.login' | translate }}
|
|
1983
2051
|
</a>
|
|
1984
2052
|
<a
|
|
1985
2053
|
routerLink="/auth/register"
|
|
1986
|
-
class="bg-
|
|
2054
|
+
class="bg-primary-600 hover:bg-primary-700 text-white px-4 py-2 rounded-lg transition-colors font-medium">
|
|
1987
2055
|
{{ 'nav.register' | translate }}
|
|
1988
2056
|
</a>
|
|
1989
2057
|
}
|
|
@@ -1993,9 +2061,9 @@ export class HeaderComponent implements OnInit {
|
|
|
1993
2061
|
<div class="relative">
|
|
1994
2062
|
<button
|
|
1995
2063
|
(click)="userMenuOpen = !userMenuOpen"
|
|
1996
|
-
class="flex items-center space-x-2 text-gray-700 hover:text-
|
|
1997
|
-
[class.text-
|
|
1998
|
-
<div class="h-8 w-8 bg-
|
|
2064
|
+
class="flex items-center space-x-2 text-gray-700 hover:text-primary-600 transition-colors"
|
|
2065
|
+
[class.text-primary-600]="userMenuOpen">
|
|
2066
|
+
<div class="h-8 w-8 bg-primary-600 rounded-full flex items-center justify-center">
|
|
1999
2067
|
<ng-icon name="heroUser" size="16" style="color: white;"></ng-icon>
|
|
2000
2068
|
</div>
|
|
2001
2069
|
<span class="font-medium">{{ currentUser?.firstName || 'User' }}</span>
|
|
@@ -2020,7 +2088,7 @@ export class HeaderComponent implements OnInit {
|
|
|
2020
2088
|
<hr class="my-1">
|
|
2021
2089
|
<button
|
|
2022
2090
|
(click)="onLogout()"
|
|
2023
|
-
class="w-full text-left px-4 py-2 text-sm text-
|
|
2091
|
+
class="w-full text-left px-4 py-2 text-sm text-danger-600 hover:bg-danger-50 transition-colors">
|
|
2024
2092
|
{{ 'nav.logout' | translate }}
|
|
2025
2093
|
</button>
|
|
2026
2094
|
</div>
|
|
@@ -2051,24 +2119,24 @@ export class HeaderComponent implements OnInit {
|
|
|
2051
2119
|
<!-- Navigation Links -->
|
|
2052
2120
|
<a
|
|
2053
2121
|
routerLink="/"
|
|
2054
|
-
routerLinkActive="text-
|
|
2122
|
+
routerLinkActive="text-primary-600 bg-primary-50"
|
|
2055
2123
|
[routerLinkActiveOptions]="{exact: true}"
|
|
2056
2124
|
(click)="mobileMenuOpen = false"
|
|
2057
|
-
class="px-3 py-2 rounded-lg text-gray-700 hover:text-
|
|
2125
|
+
class="px-3 py-2 rounded-lg text-gray-700 hover:text-primary-600 hover:bg-gray-100 transition-colors font-medium">
|
|
2058
2126
|
{{ 'nav.home' | translate }}
|
|
2059
2127
|
</a>
|
|
2060
2128
|
<a
|
|
2061
2129
|
routerLink="/about"
|
|
2062
|
-
routerLinkActive="text-
|
|
2130
|
+
routerLinkActive="text-primary-600 bg-primary-50"
|
|
2063
2131
|
(click)="mobileMenuOpen = false"
|
|
2064
|
-
class="px-3 py-2 rounded-lg text-gray-700 hover:text-
|
|
2132
|
+
class="px-3 py-2 rounded-lg text-gray-700 hover:text-primary-600 hover:bg-gray-100 transition-colors font-medium">
|
|
2065
2133
|
{{ 'nav.about' | translate }}
|
|
2066
2134
|
</a>
|
|
2067
2135
|
<a
|
|
2068
2136
|
routerLink="/contact"
|
|
2069
|
-
routerLinkActive="text-
|
|
2137
|
+
routerLinkActive="text-primary-600 bg-primary-50"
|
|
2070
2138
|
(click)="mobileMenuOpen = false"
|
|
2071
|
-
class="px-3 py-2 rounded-lg text-gray-700 hover:text-
|
|
2139
|
+
class="px-3 py-2 rounded-lg text-gray-700 hover:text-primary-600 hover:bg-gray-100 transition-colors font-medium">
|
|
2072
2140
|
{{ 'nav.contact' | translate }}
|
|
2073
2141
|
</a>
|
|
2074
2142
|
|
|
@@ -2081,8 +2149,8 @@ export class HeaderComponent implements OnInit {
|
|
|
2081
2149
|
<button
|
|
2082
2150
|
(click)="changeLanguage(lang.code)"
|
|
2083
2151
|
class="w-full text-left px-3 py-2 rounded-lg text-sm transition-colors flex items-center justify-between"
|
|
2084
|
-
[class.bg-
|
|
2085
|
-
[class.text-
|
|
2152
|
+
[class.bg-primary-50]="currentLang === lang.code"
|
|
2153
|
+
[class.text-primary-600]="currentLang === lang.code"
|
|
2086
2154
|
[class.text-gray-700]="currentLang !== lang.code"
|
|
2087
2155
|
[class.hover:bg-gray-100]="currentLang !== lang.code">
|
|
2088
2156
|
<span class="flex items-center space-x-2">
|
|
@@ -2105,13 +2173,13 @@ export class HeaderComponent implements OnInit {
|
|
|
2105
2173
|
<a
|
|
2106
2174
|
routerLink="/auth/login"
|
|
2107
2175
|
(click)="mobileMenuOpen = false"
|
|
2108
|
-
class="px-3 py-2 rounded-lg text-gray-700 hover:text-
|
|
2176
|
+
class="px-3 py-2 rounded-lg text-gray-700 hover:text-primary-600 hover:bg-gray-100 transition-colors font-medium">
|
|
2109
2177
|
{{ 'nav.login' | translate }}
|
|
2110
2178
|
</a>
|
|
2111
2179
|
<a
|
|
2112
2180
|
routerLink="/auth/register"
|
|
2113
2181
|
(click)="mobileMenuOpen = false"
|
|
2114
|
-
class="px-3 py-2 rounded-lg bg-
|
|
2182
|
+
class="px-3 py-2 rounded-lg bg-primary-600 text-white hover:bg-primary-700 transition-colors font-medium text-center">
|
|
2115
2183
|
{{ 'nav.register' | translate }}
|
|
2116
2184
|
</a>
|
|
2117
2185
|
}
|
|
@@ -2126,7 +2194,7 @@ export class HeaderComponent implements OnInit {
|
|
|
2126
2194
|
</button>
|
|
2127
2195
|
<button
|
|
2128
2196
|
(click)="onLogout(); mobileMenuOpen = false"
|
|
2129
|
-
class="w-full text-left px-3 py-2 rounded-lg text-
|
|
2197
|
+
class="w-full text-left px-3 py-2 rounded-lg text-danger-600 hover:bg-danger-50 transition-colors">
|
|
2130
2198
|
{{ 'nav.logout' | translate }}
|
|
2131
2199
|
</button>
|
|
2132
2200
|
}
|
|
@@ -2160,13 +2228,12 @@ export class HeaderComponent implements OnInit {
|
|
|
2160
2228
|
|
|
2161
2229
|
// Footer Component
|
|
2162
2230
|
const footerComponentTs = `import { Component } from '@angular/core';
|
|
2163
|
-
import { CommonModule } from '@angular/common';
|
|
2164
2231
|
import { TranslateModule } from '@ngx-translate/core';
|
|
2165
2232
|
|
|
2166
2233
|
@Component({
|
|
2167
2234
|
selector: 'app-footer',
|
|
2168
2235
|
standalone: true,
|
|
2169
|
-
imports: [
|
|
2236
|
+
imports: [TranslateModule],
|
|
2170
2237
|
templateUrl: './footer.component.html'
|
|
2171
2238
|
})
|
|
2172
2239
|
export class FooterComponent {
|
|
@@ -2181,8 +2248,7 @@ export class FooterComponent {
|
|
|
2181
2248
|
<!-- Company Info -->
|
|
2182
2249
|
<div>
|
|
2183
2250
|
<div class="flex items-center space-x-3 mb-4">
|
|
2184
|
-
<
|
|
2185
|
-
</div>
|
|
2251
|
+
<img src="assets/images/logo.svg" alt="Logo" class="h-10 w-10" />
|
|
2186
2252
|
<span class="text-xl font-semibold text-gray-900">
|
|
2187
2253
|
{{ projectName }}
|
|
2188
2254
|
</span>
|
|
@@ -2199,17 +2265,17 @@ export class FooterComponent {
|
|
|
2199
2265
|
</h3>
|
|
2200
2266
|
<ul class="space-y-2">
|
|
2201
2267
|
<li>
|
|
2202
|
-
<a href="/" class="text-gray-600 hover:text-
|
|
2268
|
+
<a href="/" class="text-gray-600 hover:text-primary-600 transition-colors text-sm">
|
|
2203
2269
|
{{ 'nav.home' | translate }}
|
|
2204
2270
|
</a>
|
|
2205
2271
|
</li>
|
|
2206
2272
|
<li>
|
|
2207
|
-
<a href="/about" class="text-gray-600 hover:text-
|
|
2273
|
+
<a href="/about" class="text-gray-600 hover:text-primary-600 transition-colors text-sm">
|
|
2208
2274
|
{{ 'nav.about' | translate }}
|
|
2209
2275
|
</a>
|
|
2210
2276
|
</li>
|
|
2211
2277
|
<li>
|
|
2212
|
-
<a href="/contact" class="text-gray-600 hover:text-
|
|
2278
|
+
<a href="/contact" class="text-gray-600 hover:text-primary-600 transition-colors text-sm">
|
|
2213
2279
|
{{ 'nav.contact' | translate }}
|
|
2214
2280
|
</a>
|
|
2215
2281
|
</li>
|
|
@@ -2223,17 +2289,17 @@ export class FooterComponent {
|
|
|
2223
2289
|
</h3>
|
|
2224
2290
|
<ul class="space-y-2">
|
|
2225
2291
|
<li>
|
|
2226
|
-
<a href="https://angular.dev" target="_blank" class="text-gray-600 hover:text-
|
|
2292
|
+
<a href="https://angular.dev" target="_blank" class="text-gray-600 hover:text-primary-600 transition-colors text-sm">
|
|
2227
2293
|
Angular
|
|
2228
2294
|
</a>
|
|
2229
2295
|
</li>
|
|
2230
2296
|
<li>
|
|
2231
|
-
<a href="https://tailwindcss.com" target="_blank" class="text-gray-600 hover:text-
|
|
2297
|
+
<a href="https://tailwindcss.com" target="_blank" class="text-gray-600 hover:text-primary-600 transition-colors text-sm">
|
|
2232
2298
|
Tailwind CSS
|
|
2233
2299
|
</a>
|
|
2234
2300
|
</li>
|
|
2235
2301
|
<li>
|
|
2236
|
-
<a href="https://typescript.org" target="_blank" class="text-gray-600 hover:text-
|
|
2302
|
+
<a href="https://typescript.org" target="_blank" class="text-gray-600 hover:text-primary-600 transition-colors text-sm">
|
|
2237
2303
|
TypeScript
|
|
2238
2304
|
</a>
|
|
2239
2305
|
</li>
|
|
@@ -2242,13 +2308,10 @@ export class FooterComponent {
|
|
|
2242
2308
|
</div>
|
|
2243
2309
|
|
|
2244
2310
|
<!-- Bottom Bar -->
|
|
2245
|
-
<div class="border-t border-gray-200 mt-8 pt-8
|
|
2246
|
-
<p class="text-gray-500 text-sm">
|
|
2311
|
+
<div class="border-t border-gray-200 mt-8 pt-8">
|
|
2312
|
+
<p class="text-gray-500 text-sm text-center">
|
|
2247
2313
|
{{ 'footer.copyright' | translate:{ year: currentYear, name: projectName } }}
|
|
2248
2314
|
</p>
|
|
2249
|
-
<p class="text-gray-500 text-sm mt-2 md:mt-0">
|
|
2250
|
-
{{ 'footer.madeWith' | translate }}
|
|
2251
|
-
</p>
|
|
2252
2315
|
</div>
|
|
2253
2316
|
</div>
|
|
2254
2317
|
</footer>
|
|
@@ -2268,16 +2331,15 @@ export class FooterComponent {
|
|
|
2268
2331
|
async createAuthLayout(config) {
|
|
2269
2332
|
// Auth Layout Component
|
|
2270
2333
|
const authLayoutComponent = `import { Component } from '@angular/core';
|
|
2271
|
-
import { CommonModule } from '@angular/common';
|
|
2272
2334
|
import { RouterOutlet } from '@angular/router';
|
|
2273
2335
|
import { TranslateModule } from '@ngx-translate/core';
|
|
2274
2336
|
|
|
2275
2337
|
@Component({
|
|
2276
2338
|
selector: 'app-auth-layout',
|
|
2277
2339
|
standalone: true,
|
|
2278
|
-
imports: [
|
|
2340
|
+
imports: [RouterOutlet, TranslateModule],
|
|
2279
2341
|
template: \`
|
|
2280
|
-
<div class="min-h-screen bg-linear-to-br from-
|
|
2342
|
+
<div class="min-h-screen bg-linear-to-br from-primary-500 via-accent-600 to-secondary-700 flex items-center justify-center p-4">
|
|
2281
2343
|
<div class="w-full max-w-md">
|
|
2282
2344
|
|
|
2283
2345
|
<!-- Logo/Brand -->
|
|
@@ -2286,7 +2348,7 @@ import { TranslateModule } from '@ngx-translate/core';
|
|
|
2286
2348
|
<span class="text-2xl font-bold text-white">{{ firstLetter }}</span>
|
|
2287
2349
|
</div>
|
|
2288
2350
|
<h1 class="text-2xl font-bold text-white">{{ projectName }}</h1>
|
|
2289
|
-
<p class="text-
|
|
2351
|
+
<p class="text-primary-100 mt-2">{{ 'auth.layout.welcome' | translate }}</p>
|
|
2290
2352
|
</div>
|
|
2291
2353
|
|
|
2292
2354
|
<!-- Auth Form Container -->
|
|
@@ -2295,7 +2357,7 @@ import { TranslateModule } from '@ngx-translate/core';
|
|
|
2295
2357
|
</div>
|
|
2296
2358
|
|
|
2297
2359
|
<!-- Footer -->
|
|
2298
|
-
<div class="text-center mt-8 text-
|
|
2360
|
+
<div class="text-center mt-8 text-primary-100 text-sm">
|
|
2299
2361
|
<p>{{ 'auth.layout.copyright' | translate:{ year: currentYear, name: projectName } }}</p>
|
|
2300
2362
|
</div>
|
|
2301
2363
|
</div>
|
|
@@ -2315,7 +2377,6 @@ export class AuthLayoutComponent {
|
|
|
2315
2377
|
|
|
2316
2378
|
// Login Component
|
|
2317
2379
|
const loginComponentTs = `import { Component, inject } from '@angular/core';
|
|
2318
|
-
import { CommonModule } from '@angular/common';
|
|
2319
2380
|
import { ReactiveFormsModule, FormBuilder, Validators } from '@angular/forms';
|
|
2320
2381
|
import { Router, RouterModule } from '@angular/router';
|
|
2321
2382
|
import { TranslateModule } from '@ngx-translate/core';
|
|
@@ -2324,7 +2385,7 @@ import { ButtonComponent } from '@shared/components/button/button.component';
|
|
|
2324
2385
|
@Component({
|
|
2325
2386
|
selector: 'app-login',
|
|
2326
2387
|
standalone: true,
|
|
2327
|
-
imports: [
|
|
2388
|
+
imports: [ReactiveFormsModule, RouterModule, TranslateModule, ButtonComponent],
|
|
2328
2389
|
templateUrl: './login.component.html'
|
|
2329
2390
|
})
|
|
2330
2391
|
export class LoginComponent {
|
|
@@ -2391,11 +2452,12 @@ export class LoginComponent {
|
|
|
2391
2452
|
id="email"
|
|
2392
2453
|
type="email"
|
|
2393
2454
|
formControlName="email"
|
|
2455
|
+
autocomplete="email"
|
|
2394
2456
|
[placeholder]="'auth.login.email' | translate"
|
|
2395
|
-
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-
|
|
2396
|
-
[class.border-
|
|
2457
|
+
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent transition-colors"
|
|
2458
|
+
[class.border-danger-500]="loginForm.get('email')?.invalid && loginForm.get('email')?.touched">
|
|
2397
2459
|
@if (loginForm.get('email')?.invalid && loginForm.get('email')?.touched) {
|
|
2398
|
-
<div class="mt-1 text-sm text-
|
|
2460
|
+
<div class="mt-1 text-sm text-danger-600">
|
|
2399
2461
|
@if (loginForm.get('email')?.errors?.['required']) {
|
|
2400
2462
|
<span>{{ 'auth.validation.emailRequired' | translate }}</span>
|
|
2401
2463
|
}
|
|
@@ -2416,9 +2478,10 @@ export class LoginComponent {
|
|
|
2416
2478
|
id="password"
|
|
2417
2479
|
[type]="showPassword ? 'text' : 'password'"
|
|
2418
2480
|
formControlName="password"
|
|
2481
|
+
autocomplete="current-password"
|
|
2419
2482
|
[placeholder]="'auth.login.password' | translate"
|
|
2420
|
-
class="w-full px-4 py-3 pr-12 border border-gray-300 rounded-lg focus:ring-2 focus:ring-
|
|
2421
|
-
[class.border-
|
|
2483
|
+
class="w-full px-4 py-3 pr-12 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent transition-colors"
|
|
2484
|
+
[class.border-danger-500]="loginForm.get('password')?.invalid && loginForm.get('password')?.touched">
|
|
2422
2485
|
<button
|
|
2423
2486
|
type="button"
|
|
2424
2487
|
(click)="togglePassword()"
|
|
@@ -2438,7 +2501,7 @@ export class LoginComponent {
|
|
|
2438
2501
|
</button>
|
|
2439
2502
|
</div>
|
|
2440
2503
|
@if (loginForm.get('password')?.invalid && loginForm.get('password')?.touched) {
|
|
2441
|
-
<div class="mt-1 text-sm text-
|
|
2504
|
+
<div class="mt-1 text-sm text-danger-600">
|
|
2442
2505
|
@if (loginForm.get('password')?.errors?.['required']) {
|
|
2443
2506
|
<span>{{ 'auth.validation.passwordRequired' | translate }}</span>
|
|
2444
2507
|
}
|
|
@@ -2455,10 +2518,10 @@ export class LoginComponent {
|
|
|
2455
2518
|
<input
|
|
2456
2519
|
type="checkbox"
|
|
2457
2520
|
formControlName="rememberMe"
|
|
2458
|
-
class="h-4 w-4 text-
|
|
2521
|
+
class="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded">
|
|
2459
2522
|
<span class="ml-2 rtl:mr-2 text-sm text-gray-600">{{ 'auth.login.rememberMe' | translate }}</span>
|
|
2460
2523
|
</label>
|
|
2461
|
-
<a routerLink="/auth/forgot-password" class="text-sm text-
|
|
2524
|
+
<a routerLink="/auth/forgot-password" class="text-sm text-primary-600 hover:text-primary-700">
|
|
2462
2525
|
{{ 'auth.login.forgotPassword' | translate }}
|
|
2463
2526
|
</a>
|
|
2464
2527
|
</div>
|
|
@@ -2474,7 +2537,7 @@ export class LoginComponent {
|
|
|
2474
2537
|
|
|
2475
2538
|
<!-- Error Message -->
|
|
2476
2539
|
@if (errorMessage) {
|
|
2477
|
-
<div class="p-3 bg-
|
|
2540
|
+
<div class="p-3 bg-danger-100 border border-danger-300 text-danger-700 rounded-lg text-sm">
|
|
2478
2541
|
{{ errorMessage }}
|
|
2479
2542
|
</div>
|
|
2480
2543
|
}
|
|
@@ -2484,7 +2547,7 @@ export class LoginComponent {
|
|
|
2484
2547
|
<div class="text-center pt-4 border-t border-gray-200">
|
|
2485
2548
|
<p class="text-sm text-gray-600">
|
|
2486
2549
|
{{ 'auth.login.noAccount' | translate }}
|
|
2487
|
-
<a routerLink="/auth/register" class="text-
|
|
2550
|
+
<a routerLink="/auth/register" class="text-primary-600 hover:text-primary-700 font-medium">
|
|
2488
2551
|
{{ 'auth.login.createAccount' | translate }}
|
|
2489
2552
|
</a>
|
|
2490
2553
|
</p>
|
|
@@ -2504,7 +2567,6 @@ export class LoginComponent {
|
|
|
2504
2567
|
|
|
2505
2568
|
// Register Component
|
|
2506
2569
|
const registerComponentTs = `import { Component, inject } from '@angular/core';
|
|
2507
|
-
import { CommonModule } from '@angular/common';
|
|
2508
2570
|
import { ReactiveFormsModule, FormBuilder, Validators } from '@angular/forms';
|
|
2509
2571
|
import { RouterModule } from '@angular/router';
|
|
2510
2572
|
import { TranslateModule } from '@ngx-translate/core';
|
|
@@ -2513,7 +2575,7 @@ import { ButtonComponent } from '@shared/components/button/button.component';
|
|
|
2513
2575
|
@Component({
|
|
2514
2576
|
selector: 'app-register',
|
|
2515
2577
|
standalone: true,
|
|
2516
|
-
imports: [
|
|
2578
|
+
imports: [ReactiveFormsModule, RouterModule, TranslateModule, ButtonComponent],
|
|
2517
2579
|
templateUrl: './register.component.html'
|
|
2518
2580
|
})
|
|
2519
2581
|
export class RegisterComponent {
|
|
@@ -2537,29 +2599,33 @@ export class RegisterComponent {
|
|
|
2537
2599
|
<input
|
|
2538
2600
|
type="text"
|
|
2539
2601
|
formControlName="firstName"
|
|
2602
|
+
autocomplete="given-name"
|
|
2540
2603
|
[placeholder]="'auth.register.firstName' | translate"
|
|
2541
|
-
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-
|
|
2604
|
+
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent">
|
|
2542
2605
|
<input
|
|
2543
2606
|
type="text"
|
|
2544
2607
|
formControlName="lastName"
|
|
2608
|
+
autocomplete="family-name"
|
|
2545
2609
|
[placeholder]="'auth.register.lastName' | translate"
|
|
2546
|
-
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-
|
|
2610
|
+
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent">
|
|
2547
2611
|
</div>
|
|
2548
2612
|
<input
|
|
2549
2613
|
type="email"
|
|
2550
2614
|
formControlName="email"
|
|
2615
|
+
autocomplete="email"
|
|
2551
2616
|
[placeholder]="'auth.register.email' | translate"
|
|
2552
|
-
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-
|
|
2617
|
+
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent">
|
|
2553
2618
|
<input
|
|
2554
2619
|
type="password"
|
|
2555
2620
|
formControlName="password"
|
|
2621
|
+
autocomplete="new-password"
|
|
2556
2622
|
[placeholder]="'auth.register.password' | translate"
|
|
2557
|
-
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-
|
|
2623
|
+
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent">
|
|
2558
2624
|
<app-button type="submit" [fullWidth]="true" [disabled]="registerForm.invalid">
|
|
2559
2625
|
{{ 'auth.register.submit' | translate }}
|
|
2560
2626
|
</app-button>
|
|
2561
2627
|
<div class="text-center">
|
|
2562
|
-
<a routerLink="/auth/login" class="text-
|
|
2628
|
+
<a routerLink="/auth/login" class="text-primary-600 hover:text-primary-700">
|
|
2563
2629
|
{{ 'auth.register.hasAccount' | translate }}
|
|
2564
2630
|
</a>
|
|
2565
2631
|
</div>
|
|
@@ -2579,7 +2645,6 @@ export class RegisterComponent {
|
|
|
2579
2645
|
|
|
2580
2646
|
// Forgot Password Component
|
|
2581
2647
|
const forgotPasswordComponentTs = `import { Component, inject } from '@angular/core';
|
|
2582
|
-
import { CommonModule } from '@angular/common';
|
|
2583
2648
|
import { ReactiveFormsModule, FormBuilder, Validators } from '@angular/forms';
|
|
2584
2649
|
import { RouterModule } from '@angular/router';
|
|
2585
2650
|
import { TranslateModule } from '@ngx-translate/core';
|
|
@@ -2588,7 +2653,7 @@ import { ButtonComponent } from '@shared/components/button/button.component';
|
|
|
2588
2653
|
@Component({
|
|
2589
2654
|
selector: 'app-forgot-password',
|
|
2590
2655
|
standalone: true,
|
|
2591
|
-
imports: [
|
|
2656
|
+
imports: [ReactiveFormsModule, RouterModule, TranslateModule, ButtonComponent],
|
|
2592
2657
|
templateUrl: './forgot-password.component.html'
|
|
2593
2658
|
})
|
|
2594
2659
|
export class ForgotPasswordComponent {
|
|
@@ -2606,13 +2671,14 @@ export class ForgotPasswordComponent {
|
|
|
2606
2671
|
<input
|
|
2607
2672
|
type="email"
|
|
2608
2673
|
formControlName="email"
|
|
2674
|
+
autocomplete="email"
|
|
2609
2675
|
[placeholder]="'auth.forgot.email' | translate"
|
|
2610
|
-
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-
|
|
2676
|
+
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent">
|
|
2611
2677
|
<app-button type="submit" [fullWidth]="true" [disabled]="forgotForm.invalid">
|
|
2612
2678
|
{{ 'auth.forgot.submit' | translate }}
|
|
2613
2679
|
</app-button>
|
|
2614
2680
|
<div class="text-center">
|
|
2615
|
-
<a routerLink="/auth/login" class="text-
|
|
2681
|
+
<a routerLink="/auth/login" class="text-primary-600 hover:text-primary-700">
|
|
2616
2682
|
{{ 'auth.forgot.backToLogin' | translate }}
|
|
2617
2683
|
</a>
|
|
2618
2684
|
</div>
|
|
@@ -2708,6 +2774,8 @@ export class ForgotPasswordComponent {
|
|
|
2708
2774
|
'editor.tabSize': 2,
|
|
2709
2775
|
'prettier.requireConfig': false,
|
|
2710
2776
|
'eslint.validate': ['javascript', 'typescript', 'html'],
|
|
2777
|
+
// Suppress CSS linting warnings for Tailwind CSS directives
|
|
2778
|
+
'css.lint.unknownAtRules': 'ignore',
|
|
2711
2779
|
};
|
|
2712
2780
|
|
|
2713
2781
|
// VSCode extensions recommendations
|
|
@@ -2734,8 +2802,7 @@ export class ForgotPasswordComponent {
|
|
|
2734
2802
|
},
|
|
2735
2803
|
|
|
2736
2804
|
async createHomeComponent(config) {
|
|
2737
|
-
const homeComponentTs = `import { Component, inject } from '@angular/core';
|
|
2738
|
-
import { CommonModule } from '@angular/common';
|
|
2805
|
+
const homeComponentTs = `import { Component, OnInit, inject } from '@angular/core';
|
|
2739
2806
|
import { RouterModule } from '@angular/router';
|
|
2740
2807
|
import { NgIconComponent, provideIcons } from '@ng-icons/core';
|
|
2741
2808
|
import {
|
|
@@ -2757,11 +2824,12 @@ import { ButtonComponent } from '@shared/components/button/button.component';
|
|
|
2757
2824
|
import { CardComponent } from '@shared/components/card/card.component';
|
|
2758
2825
|
import { ToastService } from '@core/services/toast.service';
|
|
2759
2826
|
import { ModalService } from '@core/services/modal.service';
|
|
2827
|
+
import { SeoService } from '@core/services/seo.service';
|
|
2760
2828
|
|
|
2761
2829
|
@Component({
|
|
2762
2830
|
selector: 'app-home',
|
|
2763
2831
|
standalone: true,
|
|
2764
|
-
imports: [
|
|
2832
|
+
imports: [RouterModule, NgIconComponent, TranslateModule, ButtonComponent, CardComponent],
|
|
2765
2833
|
viewProviders: [provideIcons({
|
|
2766
2834
|
heroRocketLaunch,
|
|
2767
2835
|
heroPaintBrush,
|
|
@@ -2778,11 +2846,22 @@ import { ModalService } from '@core/services/modal.service';
|
|
|
2778
2846
|
})],
|
|
2779
2847
|
templateUrl: './home.component.html'
|
|
2780
2848
|
})
|
|
2781
|
-
export class HomeComponent {
|
|
2849
|
+
export class HomeComponent implements OnInit {
|
|
2782
2850
|
protected readonly projectName = '${config.projectName}';
|
|
2783
2851
|
|
|
2784
2852
|
private toast = inject(ToastService);
|
|
2785
2853
|
private modal = inject(ModalService);
|
|
2854
|
+
private seo = inject(SeoService);
|
|
2855
|
+
|
|
2856
|
+
ngOnInit(): void {
|
|
2857
|
+
// Set SEO meta tags for Home page
|
|
2858
|
+
this.seo.updateMeta({
|
|
2859
|
+
title: 'Home',
|
|
2860
|
+
description: 'Welcome to ${config.projectName} - A modern Angular application with Tailwind CSS, SEO optimization, and i18n support.',
|
|
2861
|
+
keywords: 'angular, tailwind, seo, i18n, typescript',
|
|
2862
|
+
ogType: 'website'
|
|
2863
|
+
});
|
|
2864
|
+
}
|
|
2786
2865
|
|
|
2787
2866
|
// Toast Examples
|
|
2788
2867
|
showSuccessToast(): void {
|
|
@@ -2825,12 +2904,12 @@ export class HomeComponent {
|
|
|
2825
2904
|
}`;
|
|
2826
2905
|
|
|
2827
2906
|
const homeComponentHtml = `<!-- Hero Section -->
|
|
2828
|
-
<section class="relative py-20 bg-linear-to-br from-
|
|
2907
|
+
<section class="relative py-20 bg-linear-to-br from-primary-600 via-secondary-600 to-accent-700 overflow-hidden">
|
|
2829
2908
|
<!-- Animated Background Elements -->
|
|
2830
2909
|
<div class="absolute inset-0 opacity-20">
|
|
2831
|
-
<div class="absolute top-0 -left-4 w-72 h-72 bg-
|
|
2832
|
-
<div class="absolute top-0 -right-4 w-72 h-72 bg-
|
|
2833
|
-
<div class="absolute -bottom-8 left-20 w-72 h-72 bg-
|
|
2910
|
+
<div class="absolute top-0 -left-4 w-72 h-72 bg-accent-300 rounded-full mix-blend-multiply filter blur-xl animate-blob"></div>
|
|
2911
|
+
<div class="absolute top-0 -right-4 w-72 h-72 bg-warning-300 rounded-full mix-blend-multiply filter blur-xl animate-blob animation-delay-2000"></div>
|
|
2912
|
+
<div class="absolute -bottom-8 left-20 w-72 h-72 bg-accent-400 rounded-full mix-blend-multiply filter blur-xl animate-blob animation-delay-4000"></div>
|
|
2834
2913
|
</div>
|
|
2835
2914
|
|
|
2836
2915
|
<div class="relative container mx-auto px-4 text-center">
|
|
@@ -2838,7 +2917,7 @@ export class HomeComponent {
|
|
|
2838
2917
|
<h1 class="text-4xl md:text-6xl lg:text-7xl font-bold text-white mb-6 drop-shadow-lg">
|
|
2839
2918
|
{{ 'home.hero.title' | translate:{ projectName: projectName } }}
|
|
2840
2919
|
</h1>
|
|
2841
|
-
<p class="text-lg md:text-xl lg:text-2xl text-
|
|
2920
|
+
<p class="text-lg md:text-xl lg:text-2xl text-primary-50 mb-8 max-w-3xl mx-auto leading-relaxed">
|
|
2842
2921
|
{{ 'home.hero.subtitle' | translate }}
|
|
2843
2922
|
</p>
|
|
2844
2923
|
<div class="flex flex-col sm:flex-row gap-4 justify-center animate-slide-up">
|
|
@@ -2877,8 +2956,8 @@ export class HomeComponent {
|
|
|
2877
2956
|
<app-card [hover]="true" [shadow]="true">
|
|
2878
2957
|
<div class="flex items-start space-x-4">
|
|
2879
2958
|
<div class="shrink-0">
|
|
2880
|
-
<div class="w-12 h-12 rounded-lg bg-
|
|
2881
|
-
<ng-icon name="heroRocketLaunch" size="24" style="color:
|
|
2959
|
+
<div class="w-12 h-12 rounded-lg bg-primary-100 flex items-center justify-center">
|
|
2960
|
+
<ng-icon name="heroRocketLaunch" size="24" style="color: var(--color-primary-600);"></ng-icon>
|
|
2882
2961
|
</div>
|
|
2883
2962
|
</div>
|
|
2884
2963
|
<div>
|
|
@@ -2892,8 +2971,8 @@ export class HomeComponent {
|
|
|
2892
2971
|
<app-card [hover]="true" [shadow]="true">
|
|
2893
2972
|
<div class="flex items-start space-x-4">
|
|
2894
2973
|
<div class="shrink-0">
|
|
2895
|
-
<div class="w-12 h-12 rounded-lg bg-
|
|
2896
|
-
<ng-icon name="heroGlobeAlt" size="24" style="color:
|
|
2974
|
+
<div class="w-12 h-12 rounded-lg bg-accent-100 flex items-center justify-center">
|
|
2975
|
+
<ng-icon name="heroGlobeAlt" size="24" style="color: var(--color-accent-600);"></ng-icon>
|
|
2897
2976
|
</div>
|
|
2898
2977
|
</div>
|
|
2899
2978
|
<div>
|
|
@@ -2907,8 +2986,8 @@ export class HomeComponent {
|
|
|
2907
2986
|
<app-card [hover]="true" [shadow]="true">
|
|
2908
2987
|
<div class="flex items-start space-x-4">
|
|
2909
2988
|
<div class="shrink-0">
|
|
2910
|
-
<div class="w-12 h-12 rounded-lg bg-
|
|
2911
|
-
<ng-icon name="heroServer" size="24" style="color:
|
|
2989
|
+
<div class="w-12 h-12 rounded-lg bg-success-100 flex items-center justify-center">
|
|
2990
|
+
<ng-icon name="heroServer" size="24" style="color: var(--color-success-600);"></ng-icon>
|
|
2912
2991
|
</div>
|
|
2913
2992
|
</div>
|
|
2914
2993
|
<div>
|
|
@@ -2922,8 +3001,8 @@ export class HomeComponent {
|
|
|
2922
3001
|
<app-card [hover]="true" [shadow]="true">
|
|
2923
3002
|
<div class="flex items-start space-x-4">
|
|
2924
3003
|
<div class="shrink-0">
|
|
2925
|
-
<div class="w-12 h-12 rounded-lg bg-
|
|
2926
|
-
<ng-icon name="heroPaintBrush" size="24" style="color:
|
|
3004
|
+
<div class="w-12 h-12 rounded-lg bg-secondary-100 flex items-center justify-center">
|
|
3005
|
+
<ng-icon name="heroPaintBrush" size="24" style="color: var(--color-secondary-600);"></ng-icon>
|
|
2927
3006
|
</div>
|
|
2928
3007
|
</div>
|
|
2929
3008
|
<div>
|
|
@@ -2937,8 +3016,8 @@ export class HomeComponent {
|
|
|
2937
3016
|
<app-card [hover]="true" [shadow]="true">
|
|
2938
3017
|
<div class="flex items-start space-x-4">
|
|
2939
3018
|
<div class="shrink-0">
|
|
2940
|
-
<div class="w-12 h-12 rounded-lg bg-
|
|
2941
|
-
<ng-icon name="heroCube" size="24" style="color:
|
|
3019
|
+
<div class="w-12 h-12 rounded-lg bg-accent-100 flex items-center justify-center">
|
|
3020
|
+
<ng-icon name="heroCube" size="24" style="color: var(--color-accent-600);"></ng-icon>
|
|
2942
3021
|
</div>
|
|
2943
3022
|
</div>
|
|
2944
3023
|
<div>
|
|
@@ -2952,8 +3031,8 @@ export class HomeComponent {
|
|
|
2952
3031
|
<app-card [hover]="true" [shadow]="true">
|
|
2953
3032
|
<div class="flex items-start space-x-4">
|
|
2954
3033
|
<div class="shrink-0">
|
|
2955
|
-
<div class="w-12 h-12 rounded-lg bg-
|
|
2956
|
-
<ng-icon name="heroShieldCheck" size="24" style="color:
|
|
3034
|
+
<div class="w-12 h-12 rounded-lg bg-primary-100 flex items-center justify-center">
|
|
3035
|
+
<ng-icon name="heroShieldCheck" size="24" style="color: var(--color-primary-600);"></ng-icon>
|
|
2957
3036
|
</div>
|
|
2958
3037
|
</div>
|
|
2959
3038
|
<div>
|
|
@@ -2970,35 +3049,35 @@ export class HomeComponent {
|
|
|
2970
3049
|
<app-card [title]="'home.coreServices.title' | translate" [shadow]="true">
|
|
2971
3050
|
<ul class="space-y-2 text-gray-700">
|
|
2972
3051
|
<li class="flex items-start">
|
|
2973
|
-
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color:
|
|
3052
|
+
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color: var(--color-success-500);"></ng-icon>
|
|
2974
3053
|
<span>{{ 'home.coreServices.authService' | translate }}</span>
|
|
2975
3054
|
</li>
|
|
2976
3055
|
<li class="flex items-start">
|
|
2977
|
-
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color:
|
|
3056
|
+
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color: var(--color-success-500);"></ng-icon>
|
|
2978
3057
|
<span>{{ 'home.coreServices.apiService' | translate }}</span>
|
|
2979
3058
|
</li>
|
|
2980
3059
|
<li class="flex items-start">
|
|
2981
|
-
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color:
|
|
3060
|
+
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color: var(--color-success-500);"></ng-icon>
|
|
2982
3061
|
<span>{{ 'home.coreServices.toastService' | translate }}</span>
|
|
2983
3062
|
</li>
|
|
2984
3063
|
<li class="flex items-start">
|
|
2985
|
-
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color:
|
|
3064
|
+
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color: var(--color-success-500);"></ng-icon>
|
|
2986
3065
|
<span>{{ 'home.coreServices.modalService' | translate }}</span>
|
|
2987
3066
|
</li>
|
|
2988
3067
|
<li class="flex items-start">
|
|
2989
|
-
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color:
|
|
3068
|
+
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color: var(--color-success-500);"></ng-icon>
|
|
2990
3069
|
<span>{{ 'home.coreServices.loadingService' | translate }}</span>
|
|
2991
3070
|
</li>
|
|
2992
3071
|
<li class="flex items-start">
|
|
2993
|
-
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color:
|
|
3072
|
+
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color: var(--color-success-500);"></ng-icon>
|
|
2994
3073
|
<span>{{ 'home.coreServices.cacheService' | translate }}</span>
|
|
2995
3074
|
</li>
|
|
2996
3075
|
<li class="flex items-start">
|
|
2997
|
-
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color:
|
|
3076
|
+
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color: var(--color-success-500);"></ng-icon>
|
|
2998
3077
|
<span>{{ 'home.coreServices.storageService' | translate }}</span>
|
|
2999
3078
|
</li>
|
|
3000
3079
|
<li class="flex items-start">
|
|
3001
|
-
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color:
|
|
3080
|
+
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color: var(--color-success-500);"></ng-icon>
|
|
3002
3081
|
<span>{{ 'home.coreServices.i18nService' | translate }}</span>
|
|
3003
3082
|
</li>
|
|
3004
3083
|
</ul>
|
|
@@ -3008,31 +3087,31 @@ export class HomeComponent {
|
|
|
3008
3087
|
<app-card [title]="'home.sharedComponents.title' | translate" [shadow]="true">
|
|
3009
3088
|
<ul class="space-y-2 text-gray-700">
|
|
3010
3089
|
<li class="flex items-start">
|
|
3011
|
-
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color:
|
|
3090
|
+
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color: var(--color-primary-500);"></ng-icon>
|
|
3012
3091
|
<span>{{ 'home.sharedComponents.button' | translate }}</span>
|
|
3013
3092
|
</li>
|
|
3014
3093
|
<li class="flex items-start">
|
|
3015
|
-
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color:
|
|
3094
|
+
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color: var(--color-primary-500);"></ng-icon>
|
|
3016
3095
|
<span>{{ 'home.sharedComponents.card' | translate }}</span>
|
|
3017
3096
|
</li>
|
|
3018
3097
|
<li class="flex items-start">
|
|
3019
|
-
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color:
|
|
3098
|
+
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color: var(--color-primary-500);"></ng-icon>
|
|
3020
3099
|
<span>{{ 'home.sharedComponents.spinner' | translate }}</span>
|
|
3021
3100
|
</li>
|
|
3022
3101
|
<li class="flex items-start">
|
|
3023
|
-
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color:
|
|
3102
|
+
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color: var(--color-primary-500);"></ng-icon>
|
|
3024
3103
|
<span>{{ 'home.sharedComponents.toast' | translate }}</span>
|
|
3025
3104
|
</li>
|
|
3026
3105
|
<li class="flex items-start">
|
|
3027
|
-
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color:
|
|
3106
|
+
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color: var(--color-primary-500);"></ng-icon>
|
|
3028
3107
|
<span>{{ 'home.sharedComponents.modal' | translate }}</span>
|
|
3029
3108
|
</li>
|
|
3030
3109
|
<li class="flex items-start">
|
|
3031
|
-
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color:
|
|
3110
|
+
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color: var(--color-primary-500);"></ng-icon>
|
|
3032
3111
|
<span>{{ 'home.sharedComponents.pipes' | translate }}</span>
|
|
3033
3112
|
</li>
|
|
3034
3113
|
<li class="flex items-start">
|
|
3035
|
-
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color:
|
|
3114
|
+
<ng-icon name="heroCheckCircle" size="20" class="mr-2 mt-0.5" style="color: var(--color-primary-500);"></ng-icon>
|
|
3036
3115
|
<span>{{ 'home.sharedComponents.directives' | translate }}</span>
|
|
3037
3116
|
</li>
|
|
3038
3117
|
</ul>
|
|
@@ -3112,16 +3191,16 @@ export class HomeComponent {
|
|
|
3112
3191
|
<app-card [title]="'home.pathAliases.title' | translate" [shadow]="true">
|
|
3113
3192
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm">
|
|
3114
3193
|
<div class="bg-gray-50 p-3 rounded font-mono">
|
|
3115
|
-
<span
|
|
3194
|
+
<span style="color: var(--color-accent-600);">{{ 'home.pathAliases.core' | translate }}</span>
|
|
3116
3195
|
</div>
|
|
3117
3196
|
<div class="bg-gray-50 p-3 rounded font-mono">
|
|
3118
|
-
<span
|
|
3197
|
+
<span style="color: var(--color-accent-600);">{{ 'home.pathAliases.shared' | translate }}</span>
|
|
3119
3198
|
</div>
|
|
3120
3199
|
<div class="bg-gray-50 p-3 rounded font-mono">
|
|
3121
|
-
<span
|
|
3200
|
+
<span style="color: var(--color-accent-600);">{{ 'home.pathAliases.features' | translate }}</span>
|
|
3122
3201
|
</div>
|
|
3123
3202
|
<div class="bg-gray-50 p-3 rounded font-mono">
|
|
3124
|
-
<span
|
|
3203
|
+
<span style="color: var(--color-accent-600);">{{ 'home.pathAliases.environments' | translate }}</span>
|
|
3125
3204
|
</div>
|
|
3126
3205
|
</div>
|
|
3127
3206
|
</app-card>
|
|
@@ -3146,7 +3225,7 @@ export class HomeComponent {
|
|
|
3146
3225
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 text-sm font-mono">
|
|
3147
3226
|
<div>
|
|
3148
3227
|
<div class="flex items-center mb-2">
|
|
3149
|
-
<ng-icon name="heroFolder" size="18" class="mr-2" style="color:
|
|
3228
|
+
<ng-icon name="heroFolder" size="18" class="mr-2" style="color: var(--color-primary-500);"></ng-icon>
|
|
3150
3229
|
<span class="font-semibold">src/app/core/</span>
|
|
3151
3230
|
</div>
|
|
3152
3231
|
<ul class="ml-6 space-y-1 text-gray-600">
|
|
@@ -3159,7 +3238,7 @@ export class HomeComponent {
|
|
|
3159
3238
|
|
|
3160
3239
|
<div>
|
|
3161
3240
|
<div class="flex items-center mb-2">
|
|
3162
|
-
<ng-icon name="heroFolder" size="18" class="mr-2" style="color:
|
|
3241
|
+
<ng-icon name="heroFolder" size="18" class="mr-2" style="color: var(--color-accent-500);"></ng-icon>
|
|
3163
3242
|
<span class="font-semibold">src/app/shared/</span>
|
|
3164
3243
|
</div>
|
|
3165
3244
|
<ul class="ml-6 space-y-1 text-gray-600">
|
|
@@ -3172,7 +3251,7 @@ export class HomeComponent {
|
|
|
3172
3251
|
|
|
3173
3252
|
<div>
|
|
3174
3253
|
<div class="flex items-center mb-2">
|
|
3175
|
-
<ng-icon name="heroFolder" size="18" class="mr-2" style="color:
|
|
3254
|
+
<ng-icon name="heroFolder" size="18" class="mr-2" style="color: var(--color-success-500);"></ng-icon>
|
|
3176
3255
|
<span class="font-semibold">src/app/features/</span>
|
|
3177
3256
|
</div>
|
|
3178
3257
|
<ul class="ml-6 space-y-1 text-gray-600">
|
|
@@ -3185,7 +3264,7 @@ export class HomeComponent {
|
|
|
3185
3264
|
|
|
3186
3265
|
<div>
|
|
3187
3266
|
<div class="flex items-center mb-2">
|
|
3188
|
-
<ng-icon name="heroFolder" size="18" class="mr-2" style="color:
|
|
3267
|
+
<ng-icon name="heroFolder" size="18" class="mr-2" style="color: var(--color-warning-500);"></ng-icon>
|
|
3189
3268
|
<span class="font-semibold">src/app/layout/</span>
|
|
3190
3269
|
</div>
|
|
3191
3270
|
<ul class="ml-6 space-y-1 text-gray-600">
|
|
@@ -3203,7 +3282,7 @@ export class HomeComponent {
|
|
|
3203
3282
|
<section class="py-20 bg-gray-50">
|
|
3204
3283
|
<div class="container mx-auto px-4">
|
|
3205
3284
|
<div class="text-center mb-12 animate-fade-in">
|
|
3206
|
-
<div class="inline-flex items-center justify-center w-14 h-14 rounded-xl bg-
|
|
3285
|
+
<div class="inline-flex items-center justify-center w-14 h-14 rounded-xl bg-primary-600 mb-4">
|
|
3207
3286
|
<ng-icon name="heroBolt" size="28" style="color: white;"></ng-icon>
|
|
3208
3287
|
</div>
|
|
3209
3288
|
<h2 class="text-3xl md:text-4xl font-bold text-gray-900 mb-3">
|
|
@@ -3221,7 +3300,7 @@ export class HomeComponent {
|
|
|
3221
3300
|
<div class="animate-slide-up">
|
|
3222
3301
|
<div class="flex items-center gap-3 mb-5">
|
|
3223
3302
|
<div class="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center">
|
|
3224
|
-
<ng-icon name="heroCheckCircle" size="20"
|
|
3303
|
+
<ng-icon name="heroCheckCircle" size="20" class="text-gray-700"></ng-icon>
|
|
3225
3304
|
</div>
|
|
3226
3305
|
<div>
|
|
3227
3306
|
<h3 class="text-xl font-semibold text-gray-900">
|
|
@@ -3267,7 +3346,7 @@ export class HomeComponent {
|
|
|
3267
3346
|
<div class="animate-slide-up animation-delay-200">
|
|
3268
3347
|
<div class="flex items-center gap-3 mb-5">
|
|
3269
3348
|
<div class="w-10 h-10 rounded-lg bg-gray-100 flex items-center justify-center">
|
|
3270
|
-
<ng-icon name="heroCube" size="20"
|
|
3349
|
+
<ng-icon name="heroCube" size="20" class="text-gray-700"></ng-icon>
|
|
3271
3350
|
</div>
|
|
3272
3351
|
<div>
|
|
3273
3352
|
<h3 class="text-xl font-semibold text-gray-900">
|
|
@@ -3296,10 +3375,10 @@ export class HomeComponent {
|
|
|
3296
3375
|
</div>
|
|
3297
3376
|
|
|
3298
3377
|
<!-- Pro Tip -->
|
|
3299
|
-
<div class="bg-
|
|
3378
|
+
<div class="bg-info-50 rounded-lg p-5 border border-info-100">
|
|
3300
3379
|
<div class="flex items-start gap-3">
|
|
3301
3380
|
<div class="shrink-0">
|
|
3302
|
-
<div class="w-8 h-8 rounded-lg bg-
|
|
3381
|
+
<div class="w-8 h-8 rounded-lg bg-primary-600 flex items-center justify-center">
|
|
3303
3382
|
<ng-icon name="heroBolt" size="16" style="color: white;"></ng-icon>
|
|
3304
3383
|
</div>
|
|
3305
3384
|
</div>
|
|
@@ -3350,8 +3429,7 @@ export class HomeComponent {
|
|
|
3350
3429
|
},
|
|
3351
3430
|
|
|
3352
3431
|
async createAboutComponent(config) {
|
|
3353
|
-
const aboutComponentTs = `import { Component } from '@angular/core';
|
|
3354
|
-
import { CommonModule } from '@angular/common';
|
|
3432
|
+
const aboutComponentTs = `import { Component, OnInit, inject } from '@angular/core';
|
|
3355
3433
|
import { RouterModule } from '@angular/router';
|
|
3356
3434
|
import { NgIconComponent, provideIcons } from '@ng-icons/core';
|
|
3357
3435
|
import {
|
|
@@ -3364,11 +3442,12 @@ import {
|
|
|
3364
3442
|
} from '@ng-icons/heroicons/outline';
|
|
3365
3443
|
import { TranslateModule } from '@ngx-translate/core';
|
|
3366
3444
|
import { ButtonComponent } from '@shared/components/button/button.component';
|
|
3445
|
+
import { SeoService } from '@core/services/seo.service';
|
|
3367
3446
|
|
|
3368
3447
|
@Component({
|
|
3369
3448
|
selector: 'app-about',
|
|
3370
3449
|
standalone: true,
|
|
3371
|
-
imports: [
|
|
3450
|
+
imports: [RouterModule, NgIconComponent, TranslateModule, ButtonComponent],
|
|
3372
3451
|
viewProviders: [provideIcons({
|
|
3373
3452
|
heroRocketLaunch,
|
|
3374
3453
|
heroPaintBrush,
|
|
@@ -3379,16 +3458,27 @@ import { ButtonComponent } from '@shared/components/button/button.component';
|
|
|
3379
3458
|
})],
|
|
3380
3459
|
templateUrl: './about.component.html'
|
|
3381
3460
|
})
|
|
3382
|
-
export class AboutComponent {
|
|
3461
|
+
export class AboutComponent implements OnInit {
|
|
3383
3462
|
protected readonly projectName = '${config.projectName}';
|
|
3463
|
+
private seo = inject(SeoService);
|
|
3464
|
+
|
|
3465
|
+
ngOnInit(): void {
|
|
3466
|
+
// Set SEO meta tags for About page
|
|
3467
|
+
this.seo.updateMeta({
|
|
3468
|
+
title: 'About Us',
|
|
3469
|
+
description: 'Learn more about ${config.projectName} and our mission to create modern web applications.',
|
|
3470
|
+
keywords: 'about, team, mission, company',
|
|
3471
|
+
ogType: 'website'
|
|
3472
|
+
});
|
|
3473
|
+
}
|
|
3384
3474
|
}`;
|
|
3385
3475
|
|
|
3386
3476
|
const aboutComponentHtml = `<!-- Hero Section -->
|
|
3387
|
-
<section class="relative py-16 bg-linear-to-br from-
|
|
3477
|
+
<section class="relative py-16 bg-linear-to-br from-primary-600 via-accent-600 to-secondary-600 overflow-hidden">
|
|
3388
3478
|
<div class="absolute inset-0 opacity-10">
|
|
3389
3479
|
<div class="absolute top-0 left-0 w-96 h-96 bg-white rounded-full mix-blend-multiply filter blur-3xl animate-blob"></div>
|
|
3390
|
-
<div class="absolute top-0 right-0 w-96 h-96 bg-
|
|
3391
|
-
<div class="absolute bottom-0 left-1/2 w-96 h-96 bg-
|
|
3480
|
+
<div class="absolute top-0 right-0 w-96 h-96 bg-warning-200 rounded-full mix-blend-multiply filter blur-3xl animate-blob animation-delay-2000"></div>
|
|
3481
|
+
<div class="absolute bottom-0 left-1/2 w-96 h-96 bg-accent-200 rounded-full mix-blend-multiply filter blur-3xl animate-blob animation-delay-4000"></div>
|
|
3392
3482
|
</div>
|
|
3393
3483
|
|
|
3394
3484
|
<div class="relative container mx-auto px-4 text-center">
|
|
@@ -3396,7 +3486,7 @@ export class AboutComponent {
|
|
|
3396
3486
|
<h1 class="text-4xl md:text-5xl lg:text-6xl font-bold text-white mb-4 drop-shadow-lg">
|
|
3397
3487
|
{{ 'about.title' | translate:{ projectName: projectName } }}
|
|
3398
3488
|
</h1>
|
|
3399
|
-
<p class="text-lg md:text-xl text-
|
|
3489
|
+
<p class="text-lg md:text-xl text-accent-50 max-w-3xl mx-auto leading-relaxed">
|
|
3400
3490
|
{{ 'about.subtitle' | translate }}
|
|
3401
3491
|
</p>
|
|
3402
3492
|
</div>
|
|
@@ -3409,9 +3499,9 @@ export class AboutComponent {
|
|
|
3409
3499
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12 max-w-6xl mx-auto">
|
|
3410
3500
|
<!-- Project Overview Card -->
|
|
3411
3501
|
<div class="animate-slide-up">
|
|
3412
|
-
<div class="bg-linear-to-br from-
|
|
3502
|
+
<div class="bg-linear-to-br from-primary-50 to-accent-50 rounded-2xl p-8 border border-primary-100 h-full">
|
|
3413
3503
|
<div class="flex items-center gap-3 mb-6">
|
|
3414
|
-
<div class="w-12 h-12 rounded-lg bg-linear-to-br from-
|
|
3504
|
+
<div class="w-12 h-12 rounded-lg bg-linear-to-br from-primary-500 to-accent-600 flex items-center justify-center shadow-lg">
|
|
3415
3505
|
<ng-icon name="heroRocketLaunch" size="24" style="color: white;"></ng-icon>
|
|
3416
3506
|
</div>
|
|
3417
3507
|
<h2 class="text-2xl font-bold text-gray-900">
|
|
@@ -3429,9 +3519,9 @@ export class AboutComponent {
|
|
|
3429
3519
|
|
|
3430
3520
|
<!-- Features Card -->
|
|
3431
3521
|
<div class="animate-slide-up animation-delay-200">
|
|
3432
|
-
<div class="bg-linear-to-br from-
|
|
3522
|
+
<div class="bg-linear-to-br from-accent-50 to-secondary-50 rounded-2xl p-8 border border-accent-100 h-full">
|
|
3433
3523
|
<div class="flex items-center gap-3 mb-6">
|
|
3434
|
-
<div class="w-12 h-12 rounded-lg bg-linear-to-br from-
|
|
3524
|
+
<div class="w-12 h-12 rounded-lg bg-linear-to-br from-accent-500 to-secondary-600 flex items-center justify-center shadow-lg">
|
|
3435
3525
|
<ng-icon name="heroCheckCircle" size="24" style="color: white;"></ng-icon>
|
|
3436
3526
|
</div>
|
|
3437
3527
|
<h2 class="text-2xl font-bold text-gray-900">
|
|
@@ -3440,32 +3530,32 @@ export class AboutComponent {
|
|
|
3440
3530
|
</div>
|
|
3441
3531
|
<ul class="space-y-4">
|
|
3442
3532
|
<li class="flex items-start gap-3">
|
|
3443
|
-
<div class="shrink-0 w-6 h-6 rounded-full bg-
|
|
3444
|
-
<ng-icon name="heroCheckCircle" size="16" style="color:
|
|
3533
|
+
<div class="shrink-0 w-6 h-6 rounded-full bg-success-100 flex items-center justify-center mt-0.5">
|
|
3534
|
+
<ng-icon name="heroCheckCircle" size="16" style="color: var(--color-success-600);"></ng-icon>
|
|
3445
3535
|
</div>
|
|
3446
3536
|
<span class="text-gray-700">{{ 'about.features.standalone' | translate }}</span>
|
|
3447
3537
|
</li>
|
|
3448
3538
|
<li class="flex items-start gap-3">
|
|
3449
|
-
<div class="shrink-0 w-6 h-6 rounded-full bg-
|
|
3450
|
-
<ng-icon name="heroCheckCircle" size="16" style="color:
|
|
3539
|
+
<div class="shrink-0 w-6 h-6 rounded-full bg-success-100 flex items-center justify-center mt-0.5">
|
|
3540
|
+
<ng-icon name="heroCheckCircle" size="16" style="color: var(--color-success-600);"></ng-icon>
|
|
3451
3541
|
</div>
|
|
3452
3542
|
<span class="text-gray-700">{{ 'about.features.tailwind' | translate }}</span>
|
|
3453
3543
|
</li>
|
|
3454
3544
|
<li class="flex items-start gap-3">
|
|
3455
|
-
<div class="shrink-0 w-6 h-6 rounded-full bg-
|
|
3456
|
-
<ng-icon name="heroCheckCircle" size="16" style="color:
|
|
3545
|
+
<div class="shrink-0 w-6 h-6 rounded-full bg-success-100 flex items-center justify-center mt-0.5">
|
|
3546
|
+
<ng-icon name="heroCheckCircle" size="16" style="color: var(--color-success-600);"></ng-icon>
|
|
3457
3547
|
</div>
|
|
3458
3548
|
<span class="text-gray-700">{{ 'about.features.responsive' | translate }}</span>
|
|
3459
3549
|
</li>
|
|
3460
3550
|
<li class="flex items-start gap-3">
|
|
3461
|
-
<div class="shrink-0 w-6 h-6 rounded-full bg-
|
|
3462
|
-
<ng-icon name="heroCheckCircle" size="16" style="color:
|
|
3551
|
+
<div class="shrink-0 w-6 h-6 rounded-full bg-success-100 flex items-center justify-center mt-0.5">
|
|
3552
|
+
<ng-icon name="heroCheckCircle" size="16" style="color: var(--color-success-600);"></ng-icon>
|
|
3463
3553
|
</div>
|
|
3464
3554
|
<span class="text-gray-700">{{ 'about.features.typescript' | translate }}</span>
|
|
3465
3555
|
</li>
|
|
3466
3556
|
<li class="flex items-start gap-3">
|
|
3467
|
-
<div class="shrink-0 w-6 h-6 rounded-full bg-
|
|
3468
|
-
<ng-icon name="heroCheckCircle" size="16" style="color:
|
|
3557
|
+
<div class="shrink-0 w-6 h-6 rounded-full bg-success-100 flex items-center justify-center mt-0.5">
|
|
3558
|
+
<ng-icon name="heroCheckCircle" size="16" style="color: var(--color-success-600);"></ng-icon>
|
|
3469
3559
|
</div>
|
|
3470
3560
|
<span class="text-gray-700">{{ 'about.features.production' | translate }}</span>
|
|
3471
3561
|
</li>
|
|
@@ -3492,8 +3582,8 @@ export class AboutComponent {
|
|
|
3492
3582
|
<!-- Angular -->
|
|
3493
3583
|
<div class="bg-white rounded-xl p-6 shadow-lg hover:shadow-xl border border-gray-100 transform hover:-translate-y-1 transition-all">
|
|
3494
3584
|
<div class="flex items-center gap-4 mb-4">
|
|
3495
|
-
<div class="w-12 h-12 rounded-lg bg-
|
|
3496
|
-
<ng-icon name="heroCodeBracket" size="24" style="color:
|
|
3585
|
+
<div class="w-12 h-12 rounded-lg bg-danger-100 flex items-center justify-center">
|
|
3586
|
+
<ng-icon name="heroCodeBracket" size="24" style="color: var(--color-danger-600);"></ng-icon>
|
|
3497
3587
|
</div>
|
|
3498
3588
|
<h3 class="text-xl font-bold text-gray-900">{{ 'home.techStack.angular.title' | translate }}</h3>
|
|
3499
3589
|
</div>
|
|
@@ -3505,8 +3595,8 @@ export class AboutComponent {
|
|
|
3505
3595
|
<!-- Tailwind CSS -->
|
|
3506
3596
|
<div class="bg-white rounded-xl p-6 shadow-lg hover:shadow-xl border border-gray-100 transform hover:-translate-y-1 transition-all">
|
|
3507
3597
|
<div class="flex items-center gap-4 mb-4">
|
|
3508
|
-
<div class="w-12 h-12 rounded-lg bg-
|
|
3509
|
-
<ng-icon name="heroPaintBrush" size="24" style="color:
|
|
3598
|
+
<div class="w-12 h-12 rounded-lg bg-secondary-100 flex items-center justify-center">
|
|
3599
|
+
<ng-icon name="heroPaintBrush" size="24" style="color: var(--color-secondary-600);"></ng-icon>
|
|
3510
3600
|
</div>
|
|
3511
3601
|
<h3 class="text-xl font-bold text-gray-900">{{ 'home.techStack.tailwind.title' | translate }}</h3>
|
|
3512
3602
|
</div>
|
|
@@ -3518,8 +3608,8 @@ export class AboutComponent {
|
|
|
3518
3608
|
<!-- TypeScript -->
|
|
3519
3609
|
<div class="bg-white rounded-xl p-6 shadow-lg hover:shadow-xl border border-gray-100 transform hover:-translate-y-1 transition-all">
|
|
3520
3610
|
<div class="flex items-center gap-4 mb-4">
|
|
3521
|
-
<div class="w-12 h-12 rounded-lg bg-
|
|
3522
|
-
<ng-icon name="heroShieldCheck" size="24" style="color:
|
|
3611
|
+
<div class="w-12 h-12 rounded-lg bg-primary-100 flex items-center justify-center">
|
|
3612
|
+
<ng-icon name="heroShieldCheck" size="24" style="color: var(--color-primary-600);"></ng-icon>
|
|
3523
3613
|
</div>
|
|
3524
3614
|
<h3 class="text-xl font-bold text-gray-900">{{ 'home.techStack.typescript.title' | translate }}</h3>
|
|
3525
3615
|
</div>
|
|
@@ -3806,6 +3896,8 @@ module.exports = tseslint.config(
|
|
|
3806
3896
|
'files.eol': '\\n',
|
|
3807
3897
|
'files.trimTrailingWhitespace': true,
|
|
3808
3898
|
'files.insertFinalNewline': true,
|
|
3899
|
+
// Suppress CSS linting warnings for Tailwind CSS directives
|
|
3900
|
+
'css.lint.unknownAtRules': 'ignore',
|
|
3809
3901
|
};
|
|
3810
3902
|
|
|
3811
3903
|
await fs.writeFile(
|
|
@@ -3939,49 +4031,13 @@ npx lint-staged
|
|
|
3939
4031
|
description: `${config.projectName} - Built with Angular and Tailwind CSS`,
|
|
3940
4032
|
icons: [
|
|
3941
4033
|
{
|
|
3942
|
-
src: 'assets/icons/
|
|
3943
|
-
sizes: '72x72',
|
|
3944
|
-
type: 'image/svg+xml',
|
|
3945
|
-
purpose: 'maskable any',
|
|
3946
|
-
},
|
|
3947
|
-
{
|
|
3948
|
-
src: 'assets/icons/icon-96x96.svg',
|
|
3949
|
-
sizes: '96x96',
|
|
3950
|
-
type: 'image/svg+xml',
|
|
3951
|
-
purpose: 'maskable any',
|
|
3952
|
-
},
|
|
3953
|
-
{
|
|
3954
|
-
src: 'assets/icons/icon-128x128.svg',
|
|
3955
|
-
sizes: '128x128',
|
|
3956
|
-
type: 'image/svg+xml',
|
|
3957
|
-
purpose: 'maskable any',
|
|
3958
|
-
},
|
|
3959
|
-
{
|
|
3960
|
-
src: 'assets/icons/icon-144x144.svg',
|
|
3961
|
-
sizes: '144x144',
|
|
3962
|
-
type: 'image/svg+xml',
|
|
3963
|
-
purpose: 'maskable any',
|
|
3964
|
-
},
|
|
3965
|
-
{
|
|
3966
|
-
src: 'assets/icons/icon-152x152.svg',
|
|
3967
|
-
sizes: '152x152',
|
|
3968
|
-
type: 'image/svg+xml',
|
|
3969
|
-
purpose: 'maskable any',
|
|
3970
|
-
},
|
|
3971
|
-
{
|
|
3972
|
-
src: 'assets/icons/icon-192x192.svg',
|
|
4034
|
+
src: 'assets/icons/android-chrome-192x192.svg',
|
|
3973
4035
|
sizes: '192x192',
|
|
3974
4036
|
type: 'image/svg+xml',
|
|
3975
4037
|
purpose: 'maskable any',
|
|
3976
4038
|
},
|
|
3977
4039
|
{
|
|
3978
|
-
src: 'assets/icons/
|
|
3979
|
-
sizes: '384x384',
|
|
3980
|
-
type: 'image/svg+xml',
|
|
3981
|
-
purpose: 'maskable any',
|
|
3982
|
-
},
|
|
3983
|
-
{
|
|
3984
|
-
src: 'assets/icons/icon-512x512.svg',
|
|
4040
|
+
src: 'assets/icons/android-chrome-512x512.svg',
|
|
3985
4041
|
sizes: '512x512',
|
|
3986
4042
|
type: 'image/svg+xml',
|
|
3987
4043
|
purpose: 'maskable any',
|