diff --git a/angular.json b/angular.json index 2452635..a5bd5e6 100644 --- a/angular.json +++ b/angular.json @@ -18,7 +18,10 @@ "main": "src/main.ts", "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.app.json", - "allowedCommonJsDependencies": ["ts-interface-checker"], + "allowedCommonJsDependencies": [ + "ts-interface-checker", + "apexcharts" + ], "assets": [ "src/assets", "src/manifest.webmanifest", diff --git a/package-lock.json b/package-lock.json index b343608..2026e78 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "@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", @@ -3291,6 +3292,12 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@jest/schemas/node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", @@ -4670,10 +4677,9 @@ } }, "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true + "version": "0.32.4", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.32.4.tgz", + "integrity": "sha512-Q2Pex6H+HGuyxIGuFadkpqwtjZFXiVZlvy1rVX9XgAzUrDmUDEM69M2c4CkWUgMJ1NaFPvUf+cMBljY96GJVNQ==" }, "node_modules/@socket.io/component-emitter": { "version": "3.1.0", diff --git a/package.json b/package.json index 2646c9c..0dabfdb 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "@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", diff --git a/src/app/header/header-dropdown/header-dropdown.component.ts b/src/app/header/header-dropdown/header-dropdown.component.ts index 55aac63..f42a64e 100644 --- a/src/app/header/header-dropdown/header-dropdown.component.ts +++ b/src/app/header/header-dropdown/header-dropdown.component.ts @@ -7,7 +7,6 @@ import { } from '@angular/animations'; import { Component, - ComponentRef, EventEmitter, Input, OnDestroy, @@ -16,7 +15,6 @@ import { ViewContainerRef, } from '@angular/core'; import { - faEdit, faQuestionCircle, faSignIn, faSignOutAlt, faUser, @@ -24,10 +22,8 @@ import { import { Subscription } from 'rxjs'; import { AuthService } from 'src/app/shared/service/auth.service'; import { User } from '../../shared/model/user/user.model'; -import UserChecker from '../../shared/model/user/user.checker'; -import { HelpComponent } from '../header-popup/help/help.component'; -import { MyProfileComponent } from '../header-popup/my-profile/my-profile.component'; import {IconDefinition} from "@fortawesome/free-regular-svg-icons"; +import {Value} from "@sinclair/typebox/value"; @Component({ selector: 'app-header-dropdown', @@ -124,7 +120,7 @@ export class HeaderDropdownComponent implements OnInit, OnDestroy { ngOnInit(): void { this.userSubscription = this.authService.authSubject.subscribe( (res) => { - if (res && UserChecker.test(res)) { + if (res && Value.Check(User, res)) { this.user = res; } else { this.user = null; diff --git a/src/app/header/header-popup/login/login.component.ts b/src/app/header/header-popup/login/login.component.ts index a6a1baa..d073fe9 100644 --- a/src/app/header/header-popup/login/login.component.ts +++ b/src/app/header/header-popup/login/login.component.ts @@ -14,9 +14,7 @@ import { DomSanitizer } from '@angular/platform-browser'; import { faLock, faUser } from '@fortawesome/free-solid-svg-icons'; import { Subscription } from 'rxjs'; import { AuthService } from 'src/app/shared/service/auth.service'; -import { HttpError } from 'src/app/shared/model/httpError/httpError.model'; -import HttpErrorChecker from 'src/app/shared/model/httpError/httpErrorChecker'; -import UserChecker from 'src/app/shared/model/user/user.checker'; +import { Value } from '@sinclair/typebox/value' import { User } from 'src/app/shared/model/user/user.model'; import { animate, @@ -35,6 +33,7 @@ import { NgcStatusChangeEvent, } from 'ngx-cookieconsent'; import { CookieConsertService } from '../../../shared/cookie-consent/cookie-consert.service'; +import {HttpError} from "../../../shared/model/httpError/httpError.model"; const GOOGLE_LOGO_SVG = 'assets/img/providers/google.svg'; const GOOGLE_DISABLED_LOGO_SVG = 'assets/img/providers/google-disabled.svg'; @@ -206,10 +205,10 @@ export class LoginComponent implements OnInit, AfterViewInit, OnDestroy { } private validateLogin(res: User | HttpError | null) { - if (res && UserChecker.test(res)) { + if (res && Value.Check(User, res)) { this.closePopup(); } - if (HttpErrorChecker.test(res)) { + if (Value.Check(HttpError, res)) { this.errorMessage = (res).details; } } diff --git a/src/app/header/header-popup/my-profile/my-profile.component.ts b/src/app/header/header-popup/my-profile/my-profile.component.ts index fe3bdf0..5087f40 100644 --- a/src/app/header/header-popup/my-profile/my-profile.component.ts +++ b/src/app/header/header-popup/my-profile/my-profile.component.ts @@ -1,5 +1,4 @@ import { - ChangeDetectorRef, Component, EventEmitter, Input, @@ -18,15 +17,9 @@ import { transition, trigger, } from '@angular/animations'; -import { MatIconRegistry } from '@angular/material/icon'; -import { DomSanitizer } from '@angular/platform-browser'; import { FormControl, FormGroup, Validators } from '@angular/forms'; import { ValidateNotEmptyValidator } from '../../../shared/validators/validate-not-empty.validator'; import { ValidatePasswordValidator } from '../../../shared/validators/validate-password.validator'; -import { first, take } from 'rxjs'; -import UserChecker from '../../../shared/model/user/user.checker'; -import HttpErrorChecker from '../../../shared/model/httpError/httpErrorChecker'; -import { HttpError } from '../../../shared/model/httpError/httpError.model'; import { faFileUpload } from '@fortawesome/free-solid-svg-icons'; @Component({ diff --git a/src/app/header/header-popup/signup/signup.component.ts b/src/app/header/header-popup/signup/signup.component.ts index 3430aca..872525c 100644 --- a/src/app/header/header-popup/signup/signup.component.ts +++ b/src/app/header/header-popup/signup/signup.component.ts @@ -11,8 +11,6 @@ import { import { Subscription } from 'rxjs'; import { AuthService } from 'src/app/shared/service/auth.service'; import { HttpError } from 'src/app/shared/model/httpError/httpError.model'; -import HttpErrorChecker from 'src/app/shared/model/httpError/httpErrorChecker'; -import UserChecker from 'src/app/shared/model/user/user.checker'; import { User } from 'src/app/shared/model/user/user.model'; import { animate, @@ -27,6 +25,7 @@ import { import { ValidateEmailValidator } from '../../../shared/validators/validate-email.validator'; import { ValidatePasswordValidator } from '../../../shared/validators/validate-password.validator'; import { ValidateNotEmptyValidator } from '../../../shared/validators/validate-not-empty.validator'; +import {Value} from "@sinclair/typebox/value"; const GOOGLE_LOGO_SVG = 'assets/img/providers/google.svg'; const GITHUB_LOGO_SVG = 'assets/img/providers/github.svg'; @@ -179,10 +178,10 @@ export class SignupComponent implements OnInit { } private validateSignup(res: User | HttpError | null) { - if (res && UserChecker.test(res)) { + if (res && Value.Check(User, res)) { this.closePopup(); } - if (HttpErrorChecker.test(res)) { + if (Value.Check(HttpError, res)) { this.errorMessage = (res).details; } } diff --git a/src/app/header/header-slider/nav-slider/nav-slider.component.ts b/src/app/header/header-slider/nav-slider/nav-slider.component.ts index 3154012..c240456 100644 --- a/src/app/header/header-slider/nav-slider/nav-slider.component.ts +++ b/src/app/header/header-slider/nav-slider/nav-slider.component.ts @@ -8,10 +8,10 @@ import { } from '@angular/core'; import { faUser } from '@fortawesome/free-solid-svg-icons'; import { SliderItemComponent } from 'src/app/shared/components/slider-item/slider-item.component'; -import UserChecker from '../../../shared/model/user/user.checker'; import { User } from '../../../shared/model/user/user.model'; import { AuthService } from '../../../shared/service/auth.service'; import { Subscription } from 'rxjs'; +import {Value} from "@sinclair/typebox/value"; @Component({ selector: 'app-nav-slider', @@ -41,7 +41,7 @@ export class NavSliderComponent ngOnInit(): void { this.userSubscription = this.authService.authSubject.subscribe( (res) => { - if (res && UserChecker.test(res)) { + if (res && Value.Check(User, res)) { this.loggedUser = res; } else { this.loggedUser = null; diff --git a/src/app/header/header-slider/user-slider/user-slider.component.ts b/src/app/header/header-slider/user-slider/user-slider.component.ts index 9da3b36..8320c0b 100644 --- a/src/app/header/header-slider/user-slider/user-slider.component.ts +++ b/src/app/header/header-slider/user-slider/user-slider.component.ts @@ -2,8 +2,8 @@ import { Component, EventEmitter, OnInit, Output } from '@angular/core'; import { Subscription } from 'rxjs'; import { AuthService } from 'src/app/shared/service/auth.service'; import { SliderItemComponent } from 'src/app/shared/components/slider-item/slider-item.component'; -import UserChecker from 'src/app/shared/model/user/user.checker'; import { User } from 'src/app/shared/model/user/user.model'; +import {Value} from "@sinclair/typebox/value"; @Component({ selector: 'app-user-slider', @@ -70,7 +70,7 @@ export class UserSliderComponent extends SliderItemComponent implements OnInit { ngOnInit() { this.authSubscription = this.authService.authSubject.subscribe( (res) => { - if (UserChecker.test(res)) { + if (Value.Check(User, res)) { this.user = res; } else { this.user = null; diff --git a/src/app/header/header.component.ts b/src/app/header/header.component.ts index e9f0b64..fdbac56 100644 --- a/src/app/header/header.component.ts +++ b/src/app/header/header.component.ts @@ -5,17 +5,17 @@ import { OnDestroy, OnInit, ViewChild, - ViewContainerRef, ViewEncapsulation, + ViewContainerRef, } from '@angular/core'; import { faUser } from '@fortawesome/free-solid-svg-icons'; import { LoginComponent } from './header-popup/login/login.component'; import { SignupComponent } from './header-popup/signup/signup.component'; import { AuthService } from '../shared/service/auth.service'; -import UserChecker from '../shared/model/user/user.checker'; import { User } from '../shared/model/user/user.model'; import { Subscription } from 'rxjs'; import { HelpComponent } from './header-popup/help/help.component'; import { MyProfileComponent } from './header-popup/my-profile/my-profile.component'; +import {Value} from "@sinclair/typebox/value"; @Component({ selector: 'app-header', @@ -66,7 +66,7 @@ export class HeaderComponent implements OnInit, OnDestroy { ngOnInit(): void { this.userSubscription = this.authService.authSubject.subscribe( (res) => { - if (res && UserChecker.test(res)) { + if (res && Value.Check(User, res)) { this.loggedUser = res; } else { this.loggedUser = null; diff --git a/src/app/projects/project-card/project-card.component.css b/src/app/projects/project-card/project-card.component.css index 23fc3c6..cd5ac00 100644 --- a/src/app/projects/project-card/project-card.component.css +++ b/src/app/projects/project-card/project-card.component.css @@ -48,8 +48,8 @@ margin: 5px 0px 5px 0px; display: flex; flex-direction: row; - align-items: start; - align-content: start; + align-items: flex-start; + align-content: flex-start; color: #919294; } diff --git a/src/app/shared/model/httpError/httpError.model-ti.ts b/src/app/shared/model/httpError/httpError.model-ti.ts deleted file mode 100644 index 7396270..0000000 --- a/src/app/shared/model/httpError/httpError.model-ti.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * This module was automatically generated by `ts-interface-builder` - */ -import * as t from 'ts-interface-checker'; -// tslint:disable:object-literal-key-quotes - -export const HttpError = t.iface([], { - title: 'string', - status: 'number', - details: 'string', - developerMessage: 'string', - timestamp: 'string', -}); - -const exportedTypeSuite: t.ITypeSuite = { - HttpError, -}; -export default exportedTypeSuite; diff --git a/src/app/shared/model/httpError/httpError.model.ts b/src/app/shared/model/httpError/httpError.model.ts index 909a52f..7fb659b 100644 --- a/src/app/shared/model/httpError/httpError.model.ts +++ b/src/app/shared/model/httpError/httpError.model.ts @@ -1,7 +1,12 @@ -export interface HttpError { - title: string; - status: number; - details: string; - developerMessage: string; - timestamp: string; -} +import { Type, type Static } from '@sinclair/typebox' + + +export const HttpError = Type.Object({ + title: Type.String(), + status: Type.Number(), + details: Type.String(), + developerMessage: Type.String(), + timestamp: Type.String() +}); + +export type HttpError = Static; diff --git a/src/app/shared/model/httpError/httpErrorChecker.ts b/src/app/shared/model/httpError/httpErrorChecker.ts deleted file mode 100644 index 322bc27..0000000 --- a/src/app/shared/model/httpError/httpErrorChecker.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { createCheckers } from 'ts-interface-checker'; -import HttpError from './httpError.model-ti'; - -const HttpErrorChecker = createCheckers(HttpError)['HttpError']; -export default HttpErrorChecker; diff --git a/src/app/shared/model/project/project.model.ts b/src/app/shared/model/project/project.model.ts index af47098..1fb8944 100644 --- a/src/app/shared/model/project/project.model.ts +++ b/src/app/shared/model/project/project.model.ts @@ -1,18 +1,25 @@ -export type Language = { - name: string; - color: string; - percentage: number; -} +import { Type, type Static } from '@sinclair/typebox' -export type Project = { - name: string; - description: string; - link: string; - license?: string; - languages?: Language[]; +export const Language = Type.Object({ + name: Type.String(), + color: Type.String(), + percentage: Type.Number(), +}) - stars: number; - forks: number; - watchers: number; -} +export const Project = Type.Object({ + name: Type.String(), + description: Type.String(), + link: Type.String(), + + license: Type.Optional(Type.String()), + languages: Type.Optional(Type.Array(Language)), + + stars: Type.Number(), + forks: Type.Number(), + watchers: Type.Number(), +}); + + +export type Language = Static; +export type Project = Static; diff --git a/src/app/shared/model/stack/stack.model.ts b/src/app/shared/model/stack/stack.model.ts index fa6b311..d9b230d 100644 --- a/src/app/shared/model/stack/stack.model.ts +++ b/src/app/shared/model/stack/stack.model.ts @@ -1,5 +1,11 @@ -export interface Stack { - name: string; - image: string; - description: string; -} +import { Type, type Static } from '@sinclair/typebox' + + +export const Stack = Type.Object({ + name: Type.String(), + image: Type.String(), + description: Type.String() +}); + + +export type Stack = Static; diff --git a/src/app/shared/model/token/token.model-ti.ts b/src/app/shared/model/token/token.model-ti.ts deleted file mode 100644 index e9eb5a4..0000000 --- a/src/app/shared/model/token/token.model-ti.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * This module was automatically generated by `ts-interface-builder` - */ -import * as t from 'ts-interface-checker'; -// tslint:disable:object-literal-key-quotes - -export const Token = t.iface([], { - token: 'string', - expirationDate: t.union('string', 'number'), -}); - -const exportedTypeSuite: t.ITypeSuite = { - Token, -}; -export default exportedTypeSuite; diff --git a/src/app/shared/model/token/token.model.ts b/src/app/shared/model/token/token.model.ts index cbfaa69..d088952 100644 --- a/src/app/shared/model/token/token.model.ts +++ b/src/app/shared/model/token/token.model.ts @@ -1,4 +1,9 @@ -export interface Token { - token: string; - expirationDate: string | number; -} +import { Type, type Static } from '@sinclair/typebox' + + +export const Token = Type.Object({ + token: Type.String(), + expirationDate: Type.Union([Type.String(), Type.Number()]) +}); + +export type Token = Static; diff --git a/src/app/shared/model/user/user.checker.ts b/src/app/shared/model/user/user.checker.ts deleted file mode 100644 index 00ad4c6..0000000 --- a/src/app/shared/model/user/user.checker.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { createCheckers } from 'ts-interface-checker'; -import User from './user.model-ti'; -import Token from '../token/token.model-ti'; - -const UserChecker = createCheckers(User, Token)['User']; -export default UserChecker; diff --git a/src/app/shared/model/user/user.model-ti.ts b/src/app/shared/model/user/user.model-ti.ts deleted file mode 100644 index 9bdb1ff..0000000 --- a/src/app/shared/model/user/user.model-ti.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * This module was automatically generated by `ts-interface-builder` - */ -import * as t from 'ts-interface-checker'; -// tslint:disable:object-literal-key-quotes - -export const User = t.iface([], { - id: t.opt('number'), - name: t.opt('string'), - email: t.opt('string'), - username: 'string', - password: t.opt('string'), - profilePictureUrl: t.opt('string'), - accessToken: t.opt('Token'), - refreshToken: t.opt('Token'), - roles: t.opt(t.array('string')), -}); - -const exportedTypeSuite: t.ITypeSuite = { - User, -}; -export default exportedTypeSuite; diff --git a/src/app/shared/model/user/user.model.ts b/src/app/shared/model/user/user.model.ts index 6979709..b0bfbd8 100644 --- a/src/app/shared/model/user/user.model.ts +++ b/src/app/shared/model/user/user.model.ts @@ -1,14 +1,20 @@ +import { Type, type Static } from '@sinclair/typebox' + import { Token } from '../token/token.model'; -export interface User { - id?: number; - name?: string; - email?: string; - username: string; - password?: string; - profilePictureUrl?: string; - accessToken?: Token; - refreshToken?: Token; - roles?: Array; - validateAccessToken?: () => Token | undefined; -} + +export const User = Type.Object({ + id: Type.Optional(Type.Number()), + name: Type.Optional(Type.String()), + email: Type.Optional(Type.String()), + username: Type.String(), + password: Type.Optional(Type.String()), + profilePictureUrl: Type.Optional(Type.String()), + accessToken: Type.Optional(Token), + refreshToken: Type.Optional(Token), + roles: Type.Optional(Type.Array(Type.String())), + validateAccessToken: Type.Optional(Type.Function([],Type.Optional(Token))) +}); + + +export type User = Static;