Merge pull request #82 from HideyoshiSolutions/feature/updates-angular

feature: updates angular to v20
This commit is contained in:
2025-10-31 18:55:56 -03:00
committed by GitHub
39 changed files with 15170 additions and 17626 deletions

View File

@@ -11,12 +11,15 @@
"prefix": "app", "prefix": "app",
"architect": { "architect": {
"build": { "build": {
"builder": "@angular-devkit/build-angular:browser", "builder": "@angular/build:application",
"options": { "options": {
"outputPath": "dist/frontend-hideyoshi.com", "outputPath": {
"base": "dist/frontend-hideyoshi.com"
},
"index": "src/index.html", "index": "src/index.html",
"main": "src/main.ts", "polyfills": [
"polyfills": "src/polyfills.ts", "src/polyfills.ts"
],
"tsConfig": "tsconfig.app.json", "tsConfig": "tsconfig.app.json",
"allowedCommonJsDependencies": [ "allowedCommonJsDependencies": [
"ts-interface-checker", "ts-interface-checker",
@@ -39,8 +42,8 @@
"node_modules/bootstrap/dist/js/bootstrap.bundle.js", "node_modules/bootstrap/dist/js/bootstrap.bundle.js",
"node_modules/cookieconsent/build/cookieconsent.min.js" "node_modules/cookieconsent/build/cookieconsent.min.js"
], ],
"serviceWorker": true, "serviceWorker": "ngsw-config.json",
"ngswConfigPath": "ngsw-config.json" "browser": "src/main.ts"
}, },
"configurations": { "configurations": {
"production": { "production": {
@@ -66,9 +69,7 @@
"serviceWorker": true "serviceWorker": true
}, },
"development": { "development": {
"buildOptimizer": false, "optimization": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false, "extractLicenses": false,
"sourceMap": true, "sourceMap": true,
"namedChunks": true "namedChunks": true
@@ -77,25 +78,25 @@
"defaultConfiguration": "production" "defaultConfiguration": "production"
}, },
"serve": { "serve": {
"builder": "@angular-devkit/build-angular:dev-server", "builder": "@angular/build:dev-server",
"configurations": { "configurations": {
"production": { "production": {
"browserTarget": "frontend-hideyoshi.com:build:production" "buildTarget": "frontend-hideyoshi.com:build:production"
}, },
"development": { "development": {
"browserTarget": "frontend-hideyoshi.com:build:development" "buildTarget": "frontend-hideyoshi.com:build:development"
} }
}, },
"defaultConfiguration": "development" "defaultConfiguration": "development"
}, },
"extract-i18n": { "extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n", "builder": "@angular/build:extract-i18n",
"options": { "options": {
"browserTarget": "frontend-hideyoshi.com:build" "buildTarget": "frontend-hideyoshi.com:build"
} }
}, },
"test": { "test": {
"builder": "@angular-devkit/build-angular:karma", "builder": "@angular/build:karma",
"options": { "options": {
"main": "src/test.ts", "main": "src/test.ts",
"polyfills": "src/polyfills.ts", "polyfills": "src/polyfills.ts",
@@ -129,6 +130,30 @@
}, },
"@angular-eslint/schematics:library": { "@angular-eslint/schematics:library": {
"setParserOptionsProject": true "setParserOptionsProject": true
},
"@schematics/angular:component": {
"type": "component"
},
"@schematics/angular:directive": {
"type": "directive"
},
"@schematics/angular:service": {
"type": "service"
},
"@schematics/angular:guard": {
"typeSeparator": "."
},
"@schematics/angular:interceptor": {
"typeSeparator": "."
},
"@schematics/angular:module": {
"typeSeparator": "."
},
"@schematics/angular:pipe": {
"typeSeparator": "."
},
"@schematics/angular:resolver": {
"typeSeparator": "."
} }
} }
} }

31535
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,81 +1,81 @@
{ {
"name": "frontend-hideyoshi.com", "name": "frontend-hideyoshi.com",
"version": "0.0.0", "private": true,
"scripts": { "scripts": {
"start": "node ./set_env.js && node ./server.js", "build": "ng build",
"serve": "node ./set_env.js && ng serve", "build:prod": "ng build --configuration=production",
"build": "ng build", "serve": "node ./set_env.js && ng serve",
"start:prod": "node ./set_env.js --prod && node ./server.js", "serve:prod": "node ./set_env.js --prod && ng serve --configuration=production",
"serve:prod": "node ./set_env.js --prod && ng serve --configuration=production", "start": "node ./set_env.js && node ./server.js",
"build:prod": "ng build --configuration=production" "start:prod": "node ./set_env.js --prod && node ./server.js"
}, },
"proxy": { "dependencies": {
"/callback": { "@angular/animations": "^20.3.9",
"target": "http://localhost:8070" "@angular/cdk": "^19.2.19",
} "@angular/common": "^20.3.9",
}, "@angular/compiler": "^20.3.9",
"private": true, "@angular/core": "^20.3.9",
"dependencies": { "@angular/forms": "^20.3.9",
"@angular/animations": "^16.2.2", "@angular/material": "^19.2.19",
"@angular/cdk": "^16.2.1", "@angular/platform-browser": "^20.3.9",
"@angular/common": "^16.2.2", "@angular/platform-browser-dynamic": "^20.3.9",
"@angular/compiler": "^16.2.2", "@angular/router": "^20.3.9",
"@angular/core": "^16.2.2", "@angular/service-worker": "^20.3.9",
"@angular/forms": "^16.2.2", "@fortawesome/angular-fontawesome": "^3.0.0",
"@angular/material": "^16.2.1", "@fortawesome/fontawesome-svg-core": "^6.1.1",
"@angular/platform-browser": "^16.2.2", "@fortawesome/free-brands-svg-icons": "^6.1.1",
"@angular/platform-browser-dynamic": "^16.2.2", "@fortawesome/free-regular-svg-icons": "^6.1.1",
"@angular/router": "^16.2.2", "@fortawesome/free-solid-svg-icons": "^6.1.1",
"@angular/service-worker": "^16.2.2", "@glidejs/glide": "^3.6.0",
"@fortawesome/angular-fontawesome": "^0.13.0", "@sinclair/typebox": "^0.32.4",
"@fortawesome/fontawesome-svg-core": "^6.1.1", "apexcharts": "^3.45.1",
"@fortawesome/free-brands-svg-icons": "^6.1.1", "bootstrap": "^4.6.2",
"@fortawesome/free-regular-svg-icons": "^6.1.1", "cookieconsent": "^3.1.1",
"@fortawesome/free-solid-svg-icons": "^6.1.1", "cors": "^2.8.5",
"@glidejs/glide": "^3.6.0", "dotenv": "^17.2.3",
"@sinclair/typebox": "^0.32.4", "envsub": "^4.1.0",
"apexcharts": "^3.45.1", "express": "^4.18.1",
"bootstrap": "^4.6.2", "jquery": "^3.6.0",
"cookieconsent": "^3.1.1", "ng-apexcharts": "^1.8.0",
"cors": "^2.8.5", "ngx-cookie-service": "^20.1.1",
"envsub": "^4.1.0", "ngx-cookieconsent": "^4.0.2",
"express": "^4.18.1", "ngx-glide": "^16.0.0",
"jquery": "^3.6.0", "normalize.css": "^8.0.1",
"ng-apexcharts": "^1.8.0", "rxjs": "~7.5.0",
"ngx-cookie-service": "^16.0.1", "ts-interface-checker": "^1.0.2",
"ngx-cookieconsent": "^4.0.2", "tslib": "^2.3.0",
"ngx-glide": "^16.0.0", "zone.js": "~0.15.1"
"normalize.css": "^8.0.1", },
"rxjs": "~7.5.0", "devDependencies": {
"ts-interface-checker": "^1.0.2", "@angular-eslint/builder": "^19.8.1",
"tslib": "^2.3.0", "@angular-eslint/eslint-plugin": "^20.5.0",
"zone.js": "~0.13.1" "@angular-eslint/eslint-plugin-template": "^20.5.0",
}, "@angular-eslint/schematics": "^20.5.0",
"devDependencies": { "@angular-eslint/template-parser": "^20.5.0",
"@angular-devkit/build-angular": "^16.2.0", "@angular/build": "^20.3.8",
"@angular-eslint/builder": "^16.1.1", "@angular/cli": "^20.3.8",
"@angular-eslint/eslint-plugin": "16.1.1", "@angular/compiler-cli": "^20.3.9",
"@angular-eslint/eslint-plugin-template": "16.1.1", "@types/jasmine": "~4.0.0",
"@angular-eslint/schematics": "16.1.1", "@types/node": "^22.18.13",
"@angular-eslint/template-parser": "16.1.1", "@typescript-eslint/eslint-plugin": "^8.46.2",
"@angular/cli": "^16.2.0", "@typescript-eslint/parser": "^8.46.2",
"@angular/compiler-cli": "^16.2.2", "eslint": "^8.39.0",
"@types/jasmine": "~4.0.0", "eslint-config-prettier": "^9.0.0",
"@types/node": "^18.11.19", "eslint-plugin-prettier": "^5.0.1",
"@typescript-eslint/eslint-plugin": "^5.59.2", "jasmine-core": "~4.1.0",
"@typescript-eslint/parser": "^5.59.2", "karma": "~6.4.4",
"eslint": "^8.39.0", "karma-chrome-launcher": "~3.1.0",
"eslint-config-prettier": "^9.0.0", "karma-coverage": "~2.2.0",
"eslint-plugin-prettier": "^5.0.1", "karma-jasmine": "~5.0.0",
"jasmine-core": "~4.1.0", "karma-jasmine-html-reporter": "~1.7.0",
"karma": "~6.3.0", "prettier": "^3.0.3",
"karma-chrome-launcher": "~3.1.0", "prettier-eslint": "^16.1.1",
"karma-coverage": "~2.2.0", "ts-interface-builder": "^0.3.3",
"karma-jasmine": "~5.0.0", "typescript": "~5.8.3"
"karma-jasmine-html-reporter": "~1.7.0", },
"prettier": "^3.0.3", "proxy": {
"prettier-eslint": "^16.1.1", "/callback": {
"ts-interface-builder": "^0.3.3", "target": "http://localhost:8070"
"typescript": "~4.9.5"
} }
}
} }

View File

@@ -9,6 +9,7 @@ import {CookieConsertService} from './shared/cookie-consent/cookie-consert.servi
selector: 'app-root', selector: 'app-root',
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrls: ['./app.component.css'], styleUrls: ['./app.component.css'],
standalone: false
}) })
export class AppComponent implements OnInit { export class AppComponent implements OnInit {
title = 'frontend-hideyoshi.com'; title = 'frontend-hideyoshi.com';

View File

@@ -5,6 +5,7 @@ import {faGithub, faLinkedinIn, faTwitter,} from '@fortawesome/free-brands-svg-i
selector: 'app-footer', selector: 'app-footer',
templateUrl: './footer.component.html', templateUrl: './footer.component.html',
styleUrls: ['./footer.component.css'], styleUrls: ['./footer.component.css'],
standalone: false
}) })
export class FooterComponent { export class FooterComponent {
_githubIcon = faGithub; _githubIcon = faGithub;

View File

@@ -1,36 +1,44 @@
<div <div
class="dropdown" class="dropdown"
appClickedOutside appClickedOutside
(clickOutside)="onClickedOutside()" (clickOutside)="onClickedOutside()"
[includeClickedOutside]="[management]" [includeClickedOutside]="[management]"
[ignoreElementList]="ignoreClickOutside" [ignoreElementList]="ignoreClickOutside"
[@dropdownState]="dropDownState" [@dropdownState]="dropDownState"
(@dropdownState.start)="$event.element.style.display = 'block'" (@dropdownState.start)="$event.element.style.display = 'block'"
(@dropdownState.done)=" (@dropdownState.done)="
$event.element.style.display = state ? 'block' : 'none' $event.element.style.display = state ? 'block' : 'none'
" "
> >
<div class="info"> <div class="info">
<h3>{{ this.user ? this.user.username : "User Account" }}</h3> <h3>{{ this.user ? this.user.username : "User Account" }}</h3>
</div> </div>
<div #management> <div #management>
<ul class="user-management" *ngIf="!this.user"> @if (!this.user) {
<li *ngFor="let option of mainOptions" <ul class="user-management">
class="dropdown-item" (click)=option.callback()> @for (option of mainOptions; track option) {
<div class="icon-box"> <li
<fa-icon [icon]="option.icon"></fa-icon> class="dropdown-item" (click)=option.callback()>
</div> <div class="icon-box">
<p>{{option.text}}</p> <fa-icon [icon]="option.icon"></fa-icon>
</li> </div>
</ul> <p>{{option.text}}</p>
<ul class="user-management" *ngIf="this.user"> </li>
<li *ngFor="let option of userOptions" }
class="dropdown-item" (click)=option.callback()> </ul>
<div class="icon-box"> }
<fa-icon [icon]="option.icon"></fa-icon> @if (this.user) {
</div> <ul class="user-management">
<p>{{option.text}}</p> @for (option of userOptions; track option) {
</li> <li
</ul> class="dropdown-item" (click)=option.callback()>
</div> <div class="icon-box">
<fa-icon [icon]="option.icon"></fa-icon>
</div>
<p>{{option.text}}</p>
</li>
}
</ul>
}
</div>
</div> </div>

View File

@@ -13,22 +13,17 @@ import {Value} from "@sinclair/typebox/value";
styleUrls: ['./header-dropdown.component.css'], styleUrls: ['./header-dropdown.component.css'],
animations: [ animations: [
trigger('dropdownState', [ trigger('dropdownState', [
state( state('hide', style({
'hide', opacity: '0',
style({ })),
opacity: '0', state('show', style({
}), opacity: '1',
), })),
state(
'show',
style({
opacity: '1',
}),
),
transition('hide => show', animate('20ms ease-in')), transition('hide => show', animate('20ms ease-in')),
transition('show => hide', animate('5ms ease-out')), transition('show => hide', animate('5ms ease-out')),
]), ]),
], ],
standalone: false
}) })
export class HeaderDropdownComponent implements OnInit, OnDestroy { export class HeaderDropdownComponent implements OnInit, OnDestroy {
mainOptions: { text: string, icon: IconDefinition, callback: () => void }[] = [ mainOptions: { text: string, icon: IconDefinition, callback: () => void }[] = [

View File

@@ -6,6 +6,7 @@ import {AuthService} from 'src/app/shared/service/auth.service';
selector: 'app-callback', selector: 'app-callback',
templateUrl: './callback.component.html', templateUrl: './callback.component.html',
styleUrls: ['./callback.component.css'], styleUrls: ['./callback.component.css'],
standalone: false
}) })
export class CallbackComponent implements OnInit { export class CallbackComponent implements OnInit {
constructor( constructor(

View File

@@ -1,3 +1,5 @@
<div class="error-box" *ngIf="errorMessage"> @if (errorMessage) {
<div class="error-box">
{{ errorMessage }} {{ errorMessage }}
</div> </div>
}

View File

@@ -4,6 +4,7 @@ import {Component, Input} from '@angular/core';
selector: 'app-error-box', selector: 'app-error-box',
templateUrl: './error-box.component.html', templateUrl: './error-box.component.html',
styleUrls: ['./error-box.component.css'], styleUrls: ['./error-box.component.css'],
standalone: false
}) })
export class ErrorBoxComponent { export class ErrorBoxComponent {
@Input() @Input()

View File

@@ -4,6 +4,7 @@ import {Component, EventEmitter, Input, Output} from '@angular/core';
selector: 'app-help', selector: 'app-help',
templateUrl: './help.component.html', templateUrl: './help.component.html',
styleUrls: ['./help.component.css'], styleUrls: ['./help.component.css'],
standalone: false
}) })
export class HelpComponent { export class HelpComponent {
@Input() @Input()

View File

@@ -1,96 +1,98 @@
<app-popup <app-popup
[state]="state" [state]="state"
(stateChange)="onStateChange($event)" (stateChange)="onStateChange($event)"
[ignoreClickOutside]="ignoreClickOutside" [ignoreClickOutside]="ignoreClickOutside"
> >
<div <div
class="container m-0 overflow-hidden" class="container m-0 overflow-hidden"
[@resizeContainerForErrorMessage]="hideErrorMessage()" [@resizeContainerForErrorMessage]="hideErrorMessage()"
> >
<app-error-box <app-error-box
[errorMessage]="errorMessage" [errorMessage]="errorMessage"
[@showErrorMessage]="showErrorMessage()" [@showErrorMessage]="showErrorMessage()"
> >
</app-error-box> </app-error-box>
<div <div
class="container authentication-container" class="container authentication-container"
[@hideAuthContainer]="hideErrorMessage()" [@hideAuthContainer]="hideErrorMessage()"
(@hideAuthContainer.done)="hideAuthContainer($event)" (@hideAuthContainer.done)="hideAuthContainer($event)"
> >
<div class="row"> <div class="row">
<div class="col-lg-6 authentication-body"> <div class="col-lg-6 authentication-body">
<form [formGroup]="loginForm" (ngSubmit)="onLogin()"> <form [formGroup]="loginForm" (ngSubmit)="onLogin()">
<div class="input-div"> <div class="input-div">
<fa-icon class="input-div-icon" [icon]="_userIcon"> <fa-icon class="input-div-icon" [icon]="_userIcon">
</fa-icon> </fa-icon>
<input <input
type="text" type="text"
id="username" id="username"
formControlName="username" formControlName="username"
class="form-control" class="form-control"
placeholder="Username" placeholder="Username"
/> />
</div>
<div class="input-div">
<fa-icon
class="input-div-icon"
[icon]="_passwordIcon"
>
</fa-icon>
<input
type="password"
id="password"
formControlName="password"
class="form-control"
placeholder="Password"
/>
</div>
<button
class="btn"
[disabled]="loginForm.invalid"
type="submit"
>
Login
</button>
</form>
</div>
<div class="separator-line">
<div class="line"></div>
</div>
<div class="col-lg-6 authentication-body">
<button
mat-button
class="oauth-button d-flex justify-content-center align-items-center"
[disabled]="isCookieBlocked"
(click)="onGoogleLogin()"
>
<mat-icon
*ngIf="!isCookieBlocked"
style="width: 50px; height: 30px"
svgIcon="google-logo"
></mat-icon>
<mat-icon
*ngIf="isCookieBlocked"
style="width: 50px; height: 30px"
svgIcon="google-disabled-logo"
></mat-icon>
Login With Google
</button>
<button
mat-button
class="oauth-button d-flex justify-content-center align-items-center"
[disabled]="isCookieBlocked"
(click)="onGithubLogin()"
>
<mat-icon
style="width: 50px; height: 30px"
svgIcon="github-logo"
></mat-icon>
Login With Github
</button>
</div>
</div> </div>
<div class="input-div">
<fa-icon
class="input-div-icon"
[icon]="_passwordIcon"
>
</fa-icon>
<input
type="password"
id="password"
formControlName="password"
class="form-control"
placeholder="Password"
/>
</div>
<button
class="btn"
[disabled]="loginForm.invalid"
type="submit"
>
Login
</button>
</form>
</div> </div>
<div class="separator-line">
<div class="line"></div>
</div>
<div class="col-lg-6 authentication-body">
<button
mat-button
class="oauth-button d-flex justify-content-center align-items-center"
[disabled]="isCookieBlocked"
(click)="onGoogleLogin()"
>
@if (!isCookieBlocked) {
<mat-icon
style="width: 50px; height: 30px"
svgIcon="google-logo"
></mat-icon>
}
@if (isCookieBlocked) {
<mat-icon
style="width: 50px; height: 30px"
svgIcon="google-disabled-logo"
></mat-icon>
}
Login With Google
</button>
<button
mat-button
class="oauth-button d-flex justify-content-center align-items-center"
[disabled]="isCookieBlocked"
(click)="onGithubLogin()"
>
<mat-icon
style="width: 50px; height: 30px"
svgIcon="github-logo"
></mat-icon>
Login With Github
</button>
</div>
</div>
</div> </div>
</div>
</app-popup> </app-popup>

View File

@@ -33,56 +33,39 @@ const GITHUB_LOGO_SVG = 'assets/img/providers/github.svg';
styleUrls: ['./login.component.css'], styleUrls: ['./login.component.css'],
animations: [ animations: [
trigger('resizeContainerForErrorMessage', [ trigger('resizeContainerForErrorMessage', [
state( state('hide', style({
'hide', height: '100px',
style({ width: '320px',
height: '100px', })),
width: '320px', transition('show => hide', group([
}), query('@*', animateChild(), { optional: true }),
), animate('1s ease'),
transition( ])),
'show => hide',
group([
query('@*', animateChild(), { optional: true }),
animate('1s ease'),
]),
),
]), ]),
trigger('showErrorMessage', [ trigger('showErrorMessage', [
state( state('show', style({
'show', opacity: 1,
style({ height: '100px',
opacity: 1, width: '320px',
height: '100px', })),
width: '320px', state('hide', style({
}), opacity: 0,
), height: '0px',
state( width: '0px',
'hide', })),
style({
opacity: 0,
height: '0px',
width: '0px',
}),
),
transition('* => show', animate('500ms ease-in')), transition('* => show', animate('500ms ease-in')),
]), ]),
trigger('hideAuthContainer', [ trigger('hideAuthContainer', [
state( state('hide', style({
'hide', opacity: 0,
style({ })),
opacity: 0, transition('show => hide', group([
}), query('@*', animateChild(), { optional: true }),
), animate('250ms ease-out'),
transition( ])),
'show => hide',
group([
query('@*', animateChild(), { optional: true }),
animate('250ms ease-out'),
]),
),
]), ]),
], ],
standalone: false
}) })
export class LoginComponent implements OnInit, AfterViewInit, OnDestroy { export class LoginComponent implements OnInit, AfterViewInit, OnDestroy {
@Input() @Input()

View File

@@ -13,56 +13,39 @@ import {faFileUpload} from '@fortawesome/free-solid-svg-icons';
styleUrls: ['./my-profile.component.css'], styleUrls: ['./my-profile.component.css'],
animations: [ animations: [
trigger('resizeContainerForErrorMessage', [ trigger('resizeContainerForErrorMessage', [
state( state('hide', style({
'hide', height: '100px',
style({ width: '320px',
height: '100px', })),
width: '320px', transition('show => hide', group([
}), query('@*', animateChild(), { optional: true }),
), animate('1s ease'),
transition( ])),
'show => hide',
group([
query('@*', animateChild(), { optional: true }),
animate('1s ease'),
]),
),
]), ]),
trigger('showErrorMessage', [ trigger('showErrorMessage', [
state( state('show', style({
'show', opacity: 1,
style({ height: '100px',
opacity: 1, width: '320px',
height: '100px', })),
width: '320px', state('hide', style({
}), opacity: 0,
), height: '0px',
state( width: '0px',
'hide', })),
style({
opacity: 0,
height: '0px',
width: '0px',
}),
),
transition('* => show', animate('500ms ease-in')), transition('* => show', animate('500ms ease-in')),
]), ]),
trigger('hideAuthContainer', [ trigger('hideAuthContainer', [
state( state('hide', style({
'hide', opacity: 0,
style({ })),
opacity: 0, transition('show => hide', group([
}), query('@*', animateChild(), { optional: true }),
), animate('250ms ease-out'),
transition( ])),
'show => hide',
group([
query('@*', animateChild(), { optional: true }),
animate('250ms ease-out'),
]),
),
]), ]),
], ],
standalone: false
}) })
export class MyProfileComponent implements OnInit { export class MyProfileComponent implements OnInit {
@Input() @Input()

View File

@@ -5,6 +5,7 @@ import {AuthService} from '../../../../shared/service/auth.service';
selector: 'app-profile-picture-picker', selector: 'app-profile-picture-picker',
templateUrl: './profile-picture-picker.component.html', templateUrl: './profile-picture-picker.component.html',
styleUrls: ['./profile-picture-picker.component.css'], styleUrls: ['./profile-picture-picker.component.css'],
standalone: false
}) })
export class ProfilePicturePickerComponent { export class ProfilePicturePickerComponent {
@Output() @Output()

View File

@@ -22,56 +22,39 @@ const GITHUB_LOGO_SVG = 'assets/img/providers/github.svg';
styleUrls: ['./signup.component.css'], styleUrls: ['./signup.component.css'],
animations: [ animations: [
trigger('resizeContainerForErrorMessage', [ trigger('resizeContainerForErrorMessage', [
state( state('hide', style({
'hide', height: '100px',
style({ width: '320px',
height: '100px', })),
width: '320px', transition('show => hide', group([
}), query('@*', animateChild(), { optional: true }),
), animate('1s ease'),
transition( ])),
'show => hide',
group([
query('@*', animateChild(), { optional: true }),
animate('1s ease'),
]),
),
]), ]),
trigger('showErrorMessage', [ trigger('showErrorMessage', [
state( state('show', style({
'show', opacity: 1,
style({ height: '100px',
opacity: 1, width: '320px',
height: '100px', })),
width: '320px', state('hide', style({
}), opacity: 0,
), height: '0px',
state( width: '0px',
'hide', })),
style({
opacity: 0,
height: '0px',
width: '0px',
}),
),
transition('* => show', animate('500ms ease-in')), transition('* => show', animate('500ms ease-in')),
]), ]),
trigger('hideAuthContainer', [ trigger('hideAuthContainer', [
state( state('hide', style({
'hide', opacity: 0,
style({ })),
opacity: 0, transition('show => hide', group([
}), query('@*', animateChild(), { optional: true }),
), animate('250ms ease-out'),
transition( ])),
'show => hide',
group([
query('@*', animateChild(), { optional: true }),
animate('250ms ease-out'),
]),
),
]), ]),
], ],
standalone: false
}) })
export class SignupComponent implements OnInit { export class SignupComponent implements OnInit {
@Input() @Input()

View File

@@ -7,18 +7,12 @@ import {Component, EventEmitter, Input, Output} from '@angular/core';
styleUrls: ['./header-slider.component.css'], styleUrls: ['./header-slider.component.css'],
animations: [ animations: [
trigger('slideState', [ trigger('slideState', [
state( state('hide', style({
'hide', transform: 'translateX(100%)',
style({ })),
transform: 'translateX(100%)', state('show', style({
}), transform: 'translateX(0%)',
), })),
state(
'show',
style({
transform: 'translateX(0%)',
}),
),
transition('hide => show', [ transition('hide => show', [
group([ group([
query('@*', animateChild(), { optional: true }), query('@*', animateChild(), { optional: true }),
@@ -33,6 +27,7 @@ import {Component, EventEmitter, Input, Output} from '@angular/core';
]), ]),
]), ]),
], ],
standalone: false
}) })
export class HeaderSliderComponent { export class HeaderSliderComponent {
@Input() @Input()

View File

@@ -1,8 +1,8 @@
<div class="links-container"> <div class="links-container">
<div class="nav-links"> <div class="nav-links">
<ul> <ul>
<li @for (page of pages; track page; let i = $index) {
*ngFor="let page of pages; let i = index" <li
[@animateSliderItem]="{ [@animateSliderItem]="{
value: itemStatus, value: itemStatus,
params: { params: {
@@ -10,18 +10,19 @@
fadeOutTime: 0.6 - i / 10 fadeOutTime: 0.6 - i / 10
} }
}" }"
> >
<a [routerLink]="page.route"> <a [routerLink]="page.route">
{{ page.name }} {{ page.name }}
</a> </a>
</li> </li>
</ul> }
</div> </ul>
</div>
</div> </div>
<div class="profile-container"> <div class="profile-container">
<div <div
class="profile" class="profile"
[@animateSliderItem]="{ [@animateSliderItem]="{
value: itemStatus, value: itemStatus,
params: { params: {
@@ -29,23 +30,25 @@
fadeOutTime: 0.6 - (pages.length + 1) / 10 fadeOutTime: 0.6 - (pages.length + 1) / 10
} }
}" }"
#profile #profile
> >
<div class="profile-btn" (click)="onProfileButtonClicked()"> <div class="profile-btn" (click)="onProfileButtonClicked()">
<fa-icon @if (!loggedUser || !loggedUser.profilePictureUrl) {
*ngIf="!loggedUser || !loggedUser.profilePictureUrl" <fa-icon
class="fas fa-user" class="fas fa-user"
[icon]="userIcon" [icon]="userIcon"
></fa-icon> ></fa-icon>
<img }
*ngIf="!!loggedUser && !!loggedUser.profilePictureUrl" @if (!!loggedUser && !!loggedUser.profilePictureUrl) {
class="profile-picture" <img
[ngSrc]="loggedUser.profilePictureUrl" class="profile-picture"
width="50" [ngSrc]="loggedUser.profilePictureUrl"
height="50" width="50"
alt="Profile Picture" height="50"
priority alt="Profile Picture"
/> priority
</div> />
</div> }
</div>
</div>
</div> </div>

View File

@@ -10,6 +10,7 @@ import {Value} from "@sinclair/typebox/value";
selector: 'app-nav-slider', selector: 'app-nav-slider',
templateUrl: './nav-slider.component.html', templateUrl: './nav-slider.component.html',
styleUrls: ['./nav-slider.component.css'], styleUrls: ['./nav-slider.component.css'],
standalone: false
}) })
export class NavSliderComponent export class NavSliderComponent
extends SliderItemComponent extends SliderItemComponent

View File

@@ -1,11 +1,10 @@
<div class="user-container"> <div class="user-container">
<div class="user-options"> <div class="user-options">
<ul> <ul>
<li @for (
*ngFor=" options of user ? userOptions : userlessOptions; track
let options of user ? userOptions : userlessOptions; options; let i = $index) {
let i = index <li
"
[@animateSliderItem]="{ [@animateSliderItem]="{
value: itemStatus, value: itemStatus,
params: { params: {
@@ -13,11 +12,12 @@
fadeOutTime: 0.6 - i / 10 fadeOutTime: 0.6 - i / 10
} }
}" }"
> >
<a (click)="options.onClick()"> <a (click)="options.onClick()">
{{ options.name }} {{ options.name }}
</a> </a>
</li> </li>
</ul> }
</div> </ul>
</div>
</div> </div>

View File

@@ -9,6 +9,7 @@ import {Value} from "@sinclair/typebox/value";
selector: 'app-user-slider', selector: 'app-user-slider',
templateUrl: './user-slider.component.html', templateUrl: './user-slider.component.html',
styleUrls: ['./user-slider.component.css'], styleUrls: ['./user-slider.component.css'],
standalone: false
}) })
export class UserSliderComponent extends SliderItemComponent implements OnInit { export class UserSliderComponent extends SliderItemComponent implements OnInit {
userlessOptions = [ userlessOptions = [

View File

@@ -1,90 +1,94 @@
<div class="header"> <div class="header">
<div class="main" #header> <div class="main" #header>
<div class="logo"> <div class="logo">
<a routerLink=""> <a routerLink="">
<img src="assets/img/logohideyoshi-white.png" alt="" /> <img src="assets/img/logohideyoshi-white.png" alt="" />
</a> </a>
</div>
<div class="nav-links">
<ul class="link-container">
<li *ngFor="let page of pages">
<a [routerLink]="page.route">{{ page.name }}</a>
</li>
</ul>
</div>
<div class="profile" #profileDropdown>
<div
class="profile-btn"
(click)="toogleProfileDropdown()"
#profileBtn
>
<fa-icon
*ngIf="!loggedUser || !loggedUser.profilePictureUrl"
class="fas fa-user"
[icon]="userIcon"
></fa-icon>
<img
*ngIf="!!loggedUser && !!loggedUser.profilePictureUrl"
class="profile-picture"
[ngSrc]="loggedUser.profilePictureUrl"
width="50"
height="50"
alt="Profile Picture"
priority
/>
</div>
<app-header-dropdown
class="dropdown"
(clickOutside)="closeDropdown()"
[ignoreClickOutside]="[profileBtn]"
[state]="profileDropdownState"
(loginPopupState)="loginPopupStateChange($event)"
(signupPopupState)="signupPopupStateChange($event)"
(myProfilePopupState)="myProfilePopupStateChange($event)"
(helpPopupState)="helpPopupStateChange($event)"
>
</app-header-dropdown>
</div>
<div class="burger-container" (click)="toogleNavSlider()">
<div
class="burger-menu"
[ngClass]="{ open: navSliderStatus }"
></div>
</div>
</div> </div>
<div class="nav-links">
<ul class="link-container">
@for (page of pages; track page) {
<li>
<a [routerLink]="page.route">{{ page.name }}</a>
</li>
}
</ul>
</div>
<div class="profile" #profileDropdown>
<div
class="profile-btn"
(click)="toogleProfileDropdown()"
#profileBtn
>
@if (!loggedUser || !loggedUser.profilePictureUrl) {
<fa-icon
class="fas fa-user"
[icon]="userIcon"
></fa-icon>
}
@if (!!loggedUser && !!loggedUser.profilePictureUrl) {
<img
class="profile-picture"
[ngSrc]="loggedUser.profilePictureUrl"
width="50"
height="50"
alt="Profile Picture"
priority
/>
}
</div>
<app-header-dropdown
class="dropdown"
(clickOutside)="closeDropdown()"
[ignoreClickOutside]="[profileBtn]"
[state]="profileDropdownState"
(loginPopupState)="loginPopupStateChange($event)"
(signupPopupState)="signupPopupStateChange($event)"
(myProfilePopupState)="myProfilePopupStateChange($event)"
(helpPopupState)="helpPopupStateChange($event)"
>
</app-header-dropdown>
</div>
<div class="burger-container" (click)="toogleNavSlider()">
<div
class="burger-menu"
[ngClass]="{ open: navSliderStatus }"
></div>
</div>
</div>
</div> </div>
<div class="slider-container" #nav> <div class="slider-container" #nav>
<app-header-slider <app-header-slider
[(state)]="navSliderStatus" [(state)]="navSliderStatus"
[clickOutsideStopWatching]="userSliderStatus" [clickOutsideStopWatching]="userSliderStatus"
[ignoreClickOutside]="[header, user]" [ignoreClickOutside]="[header, user]"
> >
<app-nav-slider <app-nav-slider
[state]="navSliderStatus" [state]="navSliderStatus"
(profileButtonClicked)="profileButtonClicked()" (profileButtonClicked)="profileButtonClicked()"
[pages]="pages" [pages]="pages"
> >
</app-nav-slider> </app-nav-slider>
</app-header-slider> </app-header-slider>
</div> </div>
<div class="slider-container" #user> <div class="slider-container" #user>
<app-header-slider <app-header-slider
[(state)]="userSliderStatus" [(state)]="userSliderStatus"
[ignoreClickOutside]="[header, nav]" [ignoreClickOutside]="[header, nav]"
> >
<app-user-slider <app-user-slider
[state]="userSliderStatus" [state]="userSliderStatus"
(loginPopupState)="loginPopupStateChange($event)" (loginPopupState)="loginPopupStateChange($event)"
(signupPopupState)="signupPopupStateChange($event)" (signupPopupState)="signupPopupStateChange($event)"
(myProfilePopupState)="myProfilePopupStateChange($event)" (myProfilePopupState)="myProfilePopupStateChange($event)"
(helpPopupState)="helpPopupStateChange($event)" (helpPopupState)="helpPopupStateChange($event)"
> >
</app-user-slider> </app-user-slider>
</app-header-slider> </app-header-slider>
</div> </div>
<div class="header-spacer"></div> <div class="header-spacer"></div>

View File

@@ -13,6 +13,7 @@ import {Value} from "@sinclair/typebox/value";
selector: 'app-header', selector: 'app-header',
templateUrl: './header.component.html', templateUrl: './header.component.html',
styleUrls: ['./header.component.css'], styleUrls: ['./header.component.css'],
standalone: false
}) })
export class HeaderComponent implements OnInit, OnDestroy { export class HeaderComponent implements OnInit, OnDestroy {
pages: { name: string; route: string }[] = [ pages: { name: string; route: string }[] = [

View File

@@ -4,6 +4,7 @@ import {Component} from '@angular/core';
selector: 'app-home', selector: 'app-home',
templateUrl: './home.component.html', templateUrl: './home.component.html',
styleUrls: ['./home.component.css'], styleUrls: ['./home.component.css'],
standalone: false
}) })
export class HomeComponent { export class HomeComponent {
constructor() {} constructor() {}

View File

@@ -23,6 +23,7 @@ import {animate, state, style, transition, trigger} from "@angular/animations";
]), ]),
]) ])
], ],
standalone: false
}) })
export class StackCardComponent { export class StackCardComponent {
@Input() @Input()

View File

@@ -1,5 +1,9 @@
<ngx-glide #ngxGlide class="container stack-slider" *ngIf="stacks && stacks.length > 0"> @if (stacks && stacks.length > 0) {
<app-stack-card *ngFor="let stack of stacks" class="slider-card" [stack]="stack" <ngx-glide #ngxGlide class="container stack-slider">
@for (stack of stacks; track stack) {
<app-stack-card class="slider-card" [stack]="stack"
[inFocus]="isInFocus(stack)"> [inFocus]="isInFocus(stack)">
</app-stack-card> </app-stack-card>
</ngx-glide> }
</ngx-glide>
}

View File

@@ -6,7 +6,8 @@ import {Stack} from "../../shared/model/stack/stack.model";
@Component({ @Component({
selector: 'app-stack-slider', selector: 'app-stack-slider',
templateUrl: './stack-slider.component.html', templateUrl: './stack-slider.component.html',
styleUrls: ['./stack-slider.component.css'] styleUrls: ['./stack-slider.component.css'],
standalone: false
}) })
export class StackSliderComponent implements AfterViewInit { export class StackSliderComponent implements AfterViewInit {
@ViewChild('ngxGlide') @ViewChild('ngxGlide')

View File

@@ -1,49 +1,55 @@
<div class="card container"> <div class="card container">
<div class="card-content"> <div class="card-content">
<div class="card-content-h"> <div class="card-content-h">
<h2 class="card-title"> <h2 class="card-title">
<a [href]="project.link">{{project.name}}</a> <a [href]="project.link">{{project.name}}</a>
</h2> </h2>
<p class="card-text">{{project.description}}</p> <p class="card-text">{{project.description}}</p>
</div> </div>
<div class="card-content-f row"> <div class="card-content-f row">
<div class="card-languages col-md-9" *ngIf="hasLanguage" id="language-chart"> @if (hasLanguage) {
<apx-chart *ngIf="chartOptions !== undefined" <div class="card-languages col-md-9" id="language-chart">
[series]="chartOptions.series" @if (chartOptions !== undefined) {
[colors]="chartOptions.colors" <apx-chart
[chart]="chartOptions.chart" [series]="chartOptions.series"
[labels]="chartOptions.labels" [colors]="chartOptions.colors"
[responsive]="chartOptions.responsive" [chart]="chartOptions.chart"
[plotOptions]="chartOptions.plotOptions" [labels]="chartOptions.labels"
[dataLabels]="chartOptions.dataLabels"> [responsive]="chartOptions.responsive"
</apx-chart> [plotOptions]="chartOptions.plotOptions"
</div> [dataLabels]="chartOptions.dataLabels">
<div class="card-stats" [ngClass]="hasLanguage ? 'col-md-3' : 'stats-inline'"> </apx-chart>
<div class="stat-item" *ngIf="hasLicense"> }
<div class="stat-icon">
<fa-icon [icon]="faLicense"></fa-icon>
</div>
<span>{{project.license}}</span>
</div>
<div class="stat-item">
<div class="stat-icon">
<fa-icon [icon]="faStars"></fa-icon>
</div>
<span>{{project.stars}}</span>
</div>
<div class="stat-item">
<div class="stat-icon">
<fa-icon [icon]="faCodeFork"></fa-icon>
</div>
<span>{{project.forks}}</span>
</div>
<div class="stat-item">
<div class="stat-icon">
<fa-icon [icon]="faEye"></fa-icon>
</div>
<span>{{project.watchers}}</span>
</div>
</div>
</div>
</div> </div>
}
<div class="card-stats" [ngClass]="hasLanguage ? 'col-md-3' : 'stats-inline'">
@if (hasLicense) {
<div class="stat-item">
<div class="stat-icon">
<fa-icon [icon]="faLicense"></fa-icon>
</div>
<span>{{project.license}}</span>
</div>
}
<div class="stat-item">
<div class="stat-icon">
<fa-icon [icon]="faStars"></fa-icon>
</div>
<span>{{project.stars}}</span>
</div>
<div class="stat-item">
<div class="stat-icon">
<fa-icon [icon]="faCodeFork"></fa-icon>
</div>
<span>{{project.forks}}</span>
</div>
<div class="stat-item">
<div class="stat-icon">
<fa-icon [icon]="faEye"></fa-icon>
</div>
<span>{{project.watchers}}</span>
</div>
</div>
</div>
</div> </div>
</div>

View File

@@ -24,7 +24,8 @@ export type ChartOptions = {
@Component({ @Component({
selector: 'app-project-card', selector: 'app-project-card',
templateUrl: './project-card.component.html', templateUrl: './project-card.component.html',
styleUrls: ['./project-card.component.css'] styleUrls: ['./project-card.component.css'],
standalone: false
}) })
export class ProjectCardComponent implements OnInit { export class ProjectCardComponent implements OnInit {
@Input() inverted: boolean = false; @Input() inverted: boolean = false;

View File

@@ -1,6 +1,8 @@
<div class="container"> <div class="container">
<div *ngFor="let p of projects; index as i;trackBy: identifyProject"> @for (p of projects; track identifyProject(i, p); let i = $index) {
<app-project-card [project]="p" [inverted]="i % 2 !== 0"> <div>
</app-project-card> <app-project-card [project]="p" [inverted]="i % 2 !== 0">
</app-project-card>
</div> </div>
}
</div> </div>

View File

@@ -5,7 +5,8 @@ import {Project} from "../shared/model/project/project.model";
@Component({ @Component({
selector: 'app-projects', selector: 'app-projects',
templateUrl: './projects.component.html', templateUrl: './projects.component.html',
styleUrls: ['./projects.component.css'] styleUrls: ['./projects.component.css'],
standalone: false
}) })
export class ProjectsComponent implements OnInit { export class ProjectsComponent implements OnInit {
projects!: Project[]; projects!: Project[];

View File

@@ -7,36 +7,25 @@ import {Component, EventEmitter, Input, Output,} from '@angular/core';
styleUrls: ['./popup.component.css'], styleUrls: ['./popup.component.css'],
animations: [ animations: [
trigger('popupState', [ trigger('popupState', [
state( state('hide', style({
'hide', opacity: '0',
style({ zIndex: 2
opacity: '0', })),
zIndex: 2 state('show', style({
}), opacity: '1',
), zIndex: 2
state( })),
'show', transition('* => show', group([
style({ query('@*', animateChild(), { optional: true }),
opacity: '1', animate('250ms ease-in'),
zIndex: 2 ])),
}), transition('show => hide', group([
), query('@*', animateChild(), { optional: true }),
transition( animate('250ms ease-out'),
'* => show', ])),
group([
query('@*', animateChild(), { optional: true }),
animate('250ms ease-in'),
]),
),
transition(
'show => hide',
group([
query('@*', animateChild(), { optional: true }),
animate('250ms ease-out'),
]),
),
]), ]),
], ],
standalone: false
}) })
export class PopupComponent { export class PopupComponent {
@Input() @Input()

View File

@@ -7,36 +7,29 @@ import {Component, Input} from '@angular/core';
styleUrls: ['./slider-item.component.css'], styleUrls: ['./slider-item.component.css'],
animations: [ animations: [
trigger('animateSliderItem', [ trigger('animateSliderItem', [
state( state('hide', style({
'hide', opacity: '0',
style({ transform: 'translateX(150px)',
opacity: '0', }), {
transform: 'translateX(150px)', params: {
}), fadeInTime: 600,
{ fadeOutTime: 600,
params: {
fadeInTime: 600,
fadeOutTime: 600,
},
}, },
), }),
state( state('show', style({
'show', opacity: '1',
style({ transform: 'translateX(0px)',
opacity: '1', }), {
transform: 'translateX(0px)', params: {
}), fadeOutTime: 600,
{ fadeInTime: 600,
params: {
fadeOutTime: 600,
fadeInTime: 600,
},
}, },
), }),
transition('hide => show', animate(`{{ fadeInTime }}s ease-in`)), transition('hide => show', animate(`{{ fadeInTime }}s ease-in`)),
transition('show => hide', animate(`{{ fadeOutTime }}s ease-out`)), transition('show => hide', animate(`{{ fadeOutTime }}s ease-out`)),
]), ]),
], ],
standalone: false
}) })
export class SliderItemComponent { export class SliderItemComponent {
@Input() @Input()

View File

@@ -1,9 +1,10 @@
import {DOCUMENT} from '@angular/common';
import {AfterViewInit, Directive, ElementRef, EventEmitter, Inject, Input, OnDestroy, Output,} from '@angular/core'; import {AfterViewInit, Directive, ElementRef, EventEmitter, Inject, Input, OnDestroy, Output, DOCUMENT} from '@angular/core';
import {filter, fromEvent, Subscription,} from 'rxjs'; import {filter, fromEvent, Subscription,} from 'rxjs';
@Directive({ @Directive({
selector: '[appClickedOutside]', selector: '[appClickedOutside]',
standalone: false
}) })
export class ClickedOutsideDirective implements AfterViewInit, OnDestroy { export class ClickedOutsideDirective implements AfterViewInit, OnDestroy {
@Input() @Input()

View File

@@ -17,7 +17,11 @@ export class UpdateService {
} }
public checkForUpdates(): void { public checkForUpdates(): void {
this.swUpdate.available.subscribe((event) => this.promptUser()); this.swUpdate.versionUpdates.subscribe(versionUpdate => {
if (versionUpdate.type === 'VERSION_READY') {
this.promptUser();
}
})
} }
private promptUser(): void { private promptUser(): void {

View File

@@ -1,4 +1,4 @@
import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http'; import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {first, map, Observable, of, Subject,} from 'rxjs'; import {first, map, Observable, of, Subject,} from 'rxjs';
import {catchError} from 'rxjs/operators'; import {catchError} from 'rxjs/operators';

View File

@@ -1,6 +1,6 @@
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {Language, Project} from "../model/project/project.model"; import {Language, Project} from "../model/project/project.model";
import {HttpClient} from "@angular/common/http"; import { HttpClient } from "@angular/common/http";
import {map, Observable, switchMap, tap} from 'rxjs'; import {map, Observable, switchMap, tap} from 'rxjs';
import {environment} from 'src/environments/environment'; import {environment} from 'src/environments/environment';

View File

@@ -3,24 +3,18 @@ import {CommonModule} from '@angular/common';
import {ClickedOutsideDirective} from './directive/clicked-outside/clicked-outside.directive'; import {ClickedOutsideDirective} from './directive/clicked-outside/clicked-outside.directive';
import {SliderItemComponent} from './components/slider-item/slider-item.component'; import {SliderItemComponent} from './components/slider-item/slider-item.component';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {HttpClientModule} from '@angular/common/http'; import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import {PopupComponent} from './components/popup/popup.component'; import {PopupComponent} from './components/popup/popup.component';
import {FontAwesomeModule} from '@fortawesome/angular-fontawesome'; import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
import {CookieConsentModule} from './cookie-consent/cookie-consent.module'; import {CookieConsentModule} from './cookie-consent/cookie-consent.module';
@NgModule({ @NgModule({ declarations: [
declarations: [
ClickedOutsideDirective, ClickedOutsideDirective,
SliderItemComponent, SliderItemComponent,
PopupComponent, PopupComponent,
], ],
imports: [ exports: [ClickedOutsideDirective, SliderItemComponent, PopupComponent], imports: [CommonModule,
CommonModule,
HttpClientModule,
BrowserAnimationsModule, BrowserAnimationsModule,
FontAwesomeModule, FontAwesomeModule,
CookieConsentModule, CookieConsentModule], providers: [provideHttpClient(withInterceptorsFromDi())] })
],
exports: [ClickedOutsideDirective, SliderItemComponent, PopupComponent],
})
export class SharedModule {} export class SharedModule {}

View File

@@ -2,24 +2,27 @@
{ {
"compileOnSave": false, "compileOnSave": false,
"compilerOptions": { "compilerOptions": {
"baseUrl": "./", "baseUrl": "./",
"outDir": "./dist/out-tsc", "outDir": "./dist/out-tsc",
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"strict": true, "esModuleInterop": true,
"noImplicitOverride": true, "strict": true,
"noPropertyAccessFromIndexSignature": true, "noImplicitOverride": true,
"noImplicitReturns": true, "noPropertyAccessFromIndexSignature": true,
"noFallthroughCasesInSwitch": true, "noImplicitReturns": true,
"sourceMap": true, "noFallthroughCasesInSwitch": true,
"declaration": false, "sourceMap": true,
"downlevelIteration": true, "declaration": false,
"experimentalDecorators": true, "experimentalDecorators": true,
"moduleResolution": "node", "moduleResolution": "bundler",
"importHelpers": true, "importHelpers": true,
"target": "ES2022", "target": "ES2022",
"module": "es2020", "module": "es2020",
"lib": ["es2020", "dom"], "lib": [
"useDefineForClassFields": false "es2020",
"dom"
],
"useDefineForClassFields": false
}, },
"angularCompilerOptions": { "angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false, "enableI18nLegacyMessageIdFormat": false,