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",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"builder": "@angular/build:application",
"options": {
"outputPath": "dist/frontend-hideyoshi.com",
"outputPath": {
"base": "dist/frontend-hideyoshi.com"
},
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"polyfills": [
"src/polyfills.ts"
],
"tsConfig": "tsconfig.app.json",
"allowedCommonJsDependencies": [
"ts-interface-checker",
@@ -39,8 +42,8 @@
"node_modules/bootstrap/dist/js/bootstrap.bundle.js",
"node_modules/cookieconsent/build/cookieconsent.min.js"
],
"serviceWorker": true,
"ngswConfigPath": "ngsw-config.json"
"serviceWorker": "ngsw-config.json",
"browser": "src/main.ts"
},
"configurations": {
"production": {
@@ -66,9 +69,7 @@
"serviceWorker": true
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"optimization": false,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
@@ -77,25 +78,25 @@
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"builder": "@angular/build:dev-server",
"configurations": {
"production": {
"browserTarget": "frontend-hideyoshi.com:build:production"
"buildTarget": "frontend-hideyoshi.com:build:production"
},
"development": {
"browserTarget": "frontend-hideyoshi.com:build:development"
"buildTarget": "frontend-hideyoshi.com:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"builder": "@angular/build:extract-i18n",
"options": {
"browserTarget": "frontend-hideyoshi.com:build"
"buildTarget": "frontend-hideyoshi.com:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"builder": "@angular/build:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
@@ -129,6 +130,30 @@
},
"@angular-eslint/schematics:library": {
"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",
"version": "0.0.0",
"scripts": {
"start": "node ./set_env.js && node ./server.js",
"serve": "node ./set_env.js && ng serve",
"build": "ng build",
"start:prod": "node ./set_env.js --prod && node ./server.js",
"serve:prod": "node ./set_env.js --prod && ng serve --configuration=production",
"build:prod": "ng build --configuration=production"
},
"proxy": {
"/callback": {
"target": "http://localhost:8070"
}
},
"private": true,
"dependencies": {
"@angular/animations": "^16.2.2",
"@angular/cdk": "^16.2.1",
"@angular/common": "^16.2.2",
"@angular/compiler": "^16.2.2",
"@angular/core": "^16.2.2",
"@angular/forms": "^16.2.2",
"@angular/material": "^16.2.1",
"@angular/platform-browser": "^16.2.2",
"@angular/platform-browser-dynamic": "^16.2.2",
"@angular/router": "^16.2.2",
"@angular/service-worker": "^16.2.2",
"@fortawesome/angular-fontawesome": "^0.13.0",
"@fortawesome/fontawesome-svg-core": "^6.1.1",
"@fortawesome/free-brands-svg-icons": "^6.1.1",
"@fortawesome/free-regular-svg-icons": "^6.1.1",
"@fortawesome/free-solid-svg-icons": "^6.1.1",
"@glidejs/glide": "^3.6.0",
"@sinclair/typebox": "^0.32.4",
"apexcharts": "^3.45.1",
"bootstrap": "^4.6.2",
"cookieconsent": "^3.1.1",
"cors": "^2.8.5",
"envsub": "^4.1.0",
"express": "^4.18.1",
"jquery": "^3.6.0",
"ng-apexcharts": "^1.8.0",
"ngx-cookie-service": "^16.0.1",
"ngx-cookieconsent": "^4.0.2",
"ngx-glide": "^16.0.0",
"normalize.css": "^8.0.1",
"rxjs": "~7.5.0",
"ts-interface-checker": "^1.0.2",
"tslib": "^2.3.0",
"zone.js": "~0.13.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "^16.2.0",
"@angular-eslint/builder": "^16.1.1",
"@angular-eslint/eslint-plugin": "16.1.1",
"@angular-eslint/eslint-plugin-template": "16.1.1",
"@angular-eslint/schematics": "16.1.1",
"@angular-eslint/template-parser": "16.1.1",
"@angular/cli": "^16.2.0",
"@angular/compiler-cli": "^16.2.2",
"@types/jasmine": "~4.0.0",
"@types/node": "^18.11.19",
"@typescript-eslint/eslint-plugin": "^5.59.2",
"@typescript-eslint/parser": "^5.59.2",
"eslint": "^8.39.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.1",
"jasmine-core": "~4.1.0",
"karma": "~6.3.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.0.0",
"karma-jasmine-html-reporter": "~1.7.0",
"prettier": "^3.0.3",
"prettier-eslint": "^16.1.1",
"ts-interface-builder": "^0.3.3",
"typescript": "~4.9.5"
"name": "frontend-hideyoshi.com",
"private": true,
"scripts": {
"build": "ng build",
"build:prod": "ng build --configuration=production",
"serve": "node ./set_env.js && ng serve",
"serve:prod": "node ./set_env.js --prod && ng serve --configuration=production",
"start": "node ./set_env.js && node ./server.js",
"start:prod": "node ./set_env.js --prod && node ./server.js"
},
"dependencies": {
"@angular/animations": "^20.3.9",
"@angular/cdk": "^19.2.19",
"@angular/common": "^20.3.9",
"@angular/compiler": "^20.3.9",
"@angular/core": "^20.3.9",
"@angular/forms": "^20.3.9",
"@angular/material": "^19.2.19",
"@angular/platform-browser": "^20.3.9",
"@angular/platform-browser-dynamic": "^20.3.9",
"@angular/router": "^20.3.9",
"@angular/service-worker": "^20.3.9",
"@fortawesome/angular-fontawesome": "^3.0.0",
"@fortawesome/fontawesome-svg-core": "^6.1.1",
"@fortawesome/free-brands-svg-icons": "^6.1.1",
"@fortawesome/free-regular-svg-icons": "^6.1.1",
"@fortawesome/free-solid-svg-icons": "^6.1.1",
"@glidejs/glide": "^3.6.0",
"@sinclair/typebox": "^0.32.4",
"apexcharts": "^3.45.1",
"bootstrap": "^4.6.2",
"cookieconsent": "^3.1.1",
"cors": "^2.8.5",
"dotenv": "^17.2.3",
"envsub": "^4.1.0",
"express": "^4.18.1",
"jquery": "^3.6.0",
"ng-apexcharts": "^1.8.0",
"ngx-cookie-service": "^20.1.1",
"ngx-cookieconsent": "^4.0.2",
"ngx-glide": "^16.0.0",
"normalize.css": "^8.0.1",
"rxjs": "~7.5.0",
"ts-interface-checker": "^1.0.2",
"tslib": "^2.3.0",
"zone.js": "~0.15.1"
},
"devDependencies": {
"@angular-eslint/builder": "^19.8.1",
"@angular-eslint/eslint-plugin": "^20.5.0",
"@angular-eslint/eslint-plugin-template": "^20.5.0",
"@angular-eslint/schematics": "^20.5.0",
"@angular-eslint/template-parser": "^20.5.0",
"@angular/build": "^20.3.8",
"@angular/cli": "^20.3.8",
"@angular/compiler-cli": "^20.3.9",
"@types/jasmine": "~4.0.0",
"@types/node": "^22.18.13",
"@typescript-eslint/eslint-plugin": "^8.46.2",
"@typescript-eslint/parser": "^8.46.2",
"eslint": "^8.39.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.1",
"jasmine-core": "~4.1.0",
"karma": "~6.4.4",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.0.0",
"karma-jasmine-html-reporter": "~1.7.0",
"prettier": "^3.0.3",
"prettier-eslint": "^16.1.1",
"ts-interface-builder": "^0.3.3",
"typescript": "~5.8.3"
},
"proxy": {
"/callback": {
"target": "http://localhost:8070"
}
}
}

View File

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

View File

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

View File

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

View File

@@ -13,22 +13,17 @@ import {Value} from "@sinclair/typebox/value";
styleUrls: ['./header-dropdown.component.css'],
animations: [
trigger('dropdownState', [
state(
'hide',
style({
opacity: '0',
}),
),
state(
'show',
style({
opacity: '1',
}),
),
state('hide', style({
opacity: '0',
})),
state('show', style({
opacity: '1',
})),
transition('hide => show', animate('20ms ease-in')),
transition('show => hide', animate('5ms ease-out')),
]),
],
standalone: false
})
export class HeaderDropdownComponent implements OnInit, OnDestroy {
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',
templateUrl: './callback.component.html',
styleUrls: ['./callback.component.css'],
standalone: false
})
export class CallbackComponent implements OnInit {
constructor(

View File

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

View File

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

View File

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

View File

@@ -1,96 +1,98 @@
<app-popup
[state]="state"
(stateChange)="onStateChange($event)"
[ignoreClickOutside]="ignoreClickOutside"
>
<div
class="container m-0 overflow-hidden"
[@resizeContainerForErrorMessage]="hideErrorMessage()"
[state]="state"
(stateChange)="onStateChange($event)"
[ignoreClickOutside]="ignoreClickOutside"
>
<div
class="container m-0 overflow-hidden"
[@resizeContainerForErrorMessage]="hideErrorMessage()"
>
<app-error-box
[errorMessage]="errorMessage"
[@showErrorMessage]="showErrorMessage()"
>
</app-error-box>
<app-error-box
[errorMessage]="errorMessage"
[@showErrorMessage]="showErrorMessage()"
>
</app-error-box>
<div
class="container authentication-container"
[@hideAuthContainer]="hideErrorMessage()"
(@hideAuthContainer.done)="hideAuthContainer($event)"
>
<div class="row">
<div class="col-lg-6 authentication-body">
<form [formGroup]="loginForm" (ngSubmit)="onLogin()">
<div class="input-div">
<fa-icon class="input-div-icon" [icon]="_userIcon">
</fa-icon>
<input
type="text"
id="username"
formControlName="username"
class="form-control"
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
class="container authentication-container"
[@hideAuthContainer]="hideErrorMessage()"
(@hideAuthContainer.done)="hideAuthContainer($event)"
>
<div class="row">
<div class="col-lg-6 authentication-body">
<form [formGroup]="loginForm" (ngSubmit)="onLogin()">
<div class="input-div">
<fa-icon class="input-div-icon" [icon]="_userIcon">
</fa-icon>
<input
type="text"
id="username"
formControlName="username"
class="form-control"
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()"
>
@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>
</app-popup>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,8 +1,8 @@
<div class="links-container">
<div class="nav-links">
<ul>
<li
*ngFor="let page of pages; let i = index"
<div class="nav-links">
<ul>
@for (page of pages; track page; let i = $index) {
<li
[@animateSliderItem]="{
value: itemStatus,
params: {
@@ -10,18 +10,19 @@
fadeOutTime: 0.6 - i / 10
}
}"
>
<a [routerLink]="page.route">
{{ page.name }}
</a>
</li>
</ul>
</div>
>
<a [routerLink]="page.route">
{{ page.name }}
</a>
</li>
}
</ul>
</div>
</div>
<div class="profile-container">
<div
class="profile"
<div
class="profile"
[@animateSliderItem]="{
value: itemStatus,
params: {
@@ -29,23 +30,25 @@
fadeOutTime: 0.6 - (pages.length + 1) / 10
}
}"
#profile
>
<div class="profile-btn" (click)="onProfileButtonClicked()">
<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>
</div>
#profile
>
<div class="profile-btn" (click)="onProfileButtonClicked()">
@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>
</div>
</div>

View File

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

View File

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

View File

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

View File

@@ -1,90 +1,94 @@
<div class="header">
<div class="main" #header>
<div class="logo">
<a routerLink="">
<img src="assets/img/logohideyoshi-white.png" alt="" />
</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 class="main" #header>
<div class="logo">
<a routerLink="">
<img src="assets/img/logohideyoshi-white.png" alt="" />
</a>
</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 class="slider-container" #nav>
<app-header-slider
[(state)]="navSliderStatus"
[clickOutsideStopWatching]="userSliderStatus"
[ignoreClickOutside]="[header, user]"
<app-header-slider
[(state)]="navSliderStatus"
[clickOutsideStopWatching]="userSliderStatus"
[ignoreClickOutside]="[header, user]"
>
<app-nav-slider
[state]="navSliderStatus"
(profileButtonClicked)="profileButtonClicked()"
[pages]="pages"
>
</app-nav-slider>
</app-header-slider>
<app-nav-slider
[state]="navSliderStatus"
(profileButtonClicked)="profileButtonClicked()"
[pages]="pages"
>
</app-nav-slider>
</app-header-slider>
</div>
<div class="slider-container" #user>
<app-header-slider
[(state)]="userSliderStatus"
[ignoreClickOutside]="[header, nav]"
<app-header-slider
[(state)]="userSliderStatus"
[ignoreClickOutside]="[header, nav]"
>
<app-user-slider
[state]="userSliderStatus"
(loginPopupState)="loginPopupStateChange($event)"
(signupPopupState)="signupPopupStateChange($event)"
(myProfilePopupState)="myProfilePopupStateChange($event)"
(helpPopupState)="helpPopupStateChange($event)"
>
</app-user-slider>
</app-header-slider>
<app-user-slider
[state]="userSliderStatus"
(loginPopupState)="loginPopupStateChange($event)"
(signupPopupState)="signupPopupStateChange($event)"
(myProfilePopupState)="myProfilePopupStateChange($event)"
(helpPopupState)="helpPopupStateChange($event)"
>
</app-user-slider>
</app-header-slider>
</div>
<div class="header-spacer"></div>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,49 +1,55 @@
<div class="card container">
<div class="card-content">
<div class="card-content-h">
<h2 class="card-title">
<a [href]="project.link">{{project.name}}</a>
</h2>
<p class="card-text">{{project.description}}</p>
</div>
<div class="card-content-f row">
<div class="card-languages col-md-9" *ngIf="hasLanguage" id="language-chart">
<apx-chart *ngIf="chartOptions !== undefined"
[series]="chartOptions.series"
[colors]="chartOptions.colors"
[chart]="chartOptions.chart"
[labels]="chartOptions.labels"
[responsive]="chartOptions.responsive"
[plotOptions]="chartOptions.plotOptions"
[dataLabels]="chartOptions.dataLabels">
</apx-chart>
</div>
<div class="card-stats" [ngClass]="hasLanguage ? 'col-md-3' : 'stats-inline'">
<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 class="card container">
<div class="card-content">
<div class="card-content-h">
<h2 class="card-title">
<a [href]="project.link">{{project.name}}</a>
</h2>
<p class="card-text">{{project.description}}</p>
</div>
<div class="card-content-f row">
@if (hasLanguage) {
<div class="card-languages col-md-9" id="language-chart">
@if (chartOptions !== undefined) {
<apx-chart
[series]="chartOptions.series"
[colors]="chartOptions.colors"
[chart]="chartOptions.chart"
[labels]="chartOptions.labels"
[responsive]="chartOptions.responsive"
[plotOptions]="chartOptions.plotOptions"
[dataLabels]="chartOptions.dataLabels">
</apx-chart>
}
</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>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -17,7 +17,11 @@ export class UpdateService {
}
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 {

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 {first, map, Observable, of, Subject,} from 'rxjs';
import {catchError} from 'rxjs/operators';

View File

@@ -1,6 +1,6 @@
import {Injectable} from '@angular/core';
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 {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 {SliderItemComponent} from './components/slider-item/slider-item.component';
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 {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
import {CookieConsentModule} from './cookie-consent/cookie-consent.module';
@NgModule({
declarations: [
@NgModule({ declarations: [
ClickedOutsideDirective,
SliderItemComponent,
PopupComponent,
],
imports: [
CommonModule,
HttpClientModule,
exports: [ClickedOutsideDirective, SliderItemComponent, PopupComponent], imports: [CommonModule,
BrowserAnimationsModule,
FontAwesomeModule,
CookieConsentModule,
],
exports: [ClickedOutsideDirective, SliderItemComponent, PopupComponent],
})
CookieConsentModule], providers: [provideHttpClient(withInterceptorsFromDi())] })
export class SharedModule {}

View File

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