Merge pull request #36 from HideyoshiNakazone/formatting-project

Running Prettier in Project
This commit is contained in:
2023-10-14 19:19:10 -03:00
committed by GitHub
100 changed files with 18174 additions and 17212 deletions

View File

@@ -1,17 +1,11 @@
{
"root": true,
"ignorePatterns": [
"projects/**/*"
],
"ignorePatterns": ["projects/**/*"],
"overrides": [
{
"files": [
"*.ts"
],
"files": ["*.ts"],
"parserOptions": {
"project": [
"tsconfig.json"
],
"project": ["tsconfig.json"],
"createDefaultProgram": true
},
"extends": [
@@ -19,10 +13,7 @@
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
"indent": [
"error",
4
],
"indent": ["error", 4],
"@angular-eslint/directive-selector": [
"error",
{
@@ -42,12 +33,8 @@
}
},
{
"files": [
"*.html"
],
"extends": [
"plugin:@angular-eslint/template/recommended"
],
"files": ["*.html"],
"extends": ["plugin:@angular-eslint/template/recommended"],
"rules": {}
}
]

View File

@@ -3,12 +3,10 @@ name: ci
on:
push:
branches:
- 'main'
- "main"
jobs:
build:
runs-on: ubuntu-latest
strategy:
@@ -22,17 +20,15 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
cache: "npm"
- run: npm install
- run: npm run build --if-present
docker:
needs: [build]
runs-on: ubuntu-latest
steps:
- name: Login to DockerHub
uses: docker/login-action@v1
with:

View File

@@ -18,9 +18,7 @@
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"allowedCommonJsDependencies": [
"ts-interface-checker"
],
"allowedCommonJsDependencies": ["ts-interface-checker"],
"assets": [
"src/assets",
"src/manifest.webmanifest",
@@ -104,28 +102,21 @@
"src/manifest.webmanifest",
"src/manifest.webmanifest"
],
"styles": [
"src/styles.css"
],
"styles": ["src/styles.css"],
"scripts": []
}
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"lintFilePatterns": [
"src/**/*.ts",
"src/**/*.html"
]
"lintFilePatterns": ["src/**/*.ts", "src/**/*.html"]
}
}
}
}
},
"cli": {
"schematicCollections": [
"@angular-eslint/schematics"
],
"schematicCollections": ["@angular-eslint/schematics"],
"analytics": false
},
"schematics": {

View File

@@ -3,14 +3,14 @@
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
basePath: "",
frameworks: ["jasmine", "@angular-devkit/build-angular"],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage'),
require('@angular-devkit/build-angular/plugins/karma')
require("karma-jasmine"),
require("karma-chrome-launcher"),
require("karma-jasmine-html-reporter"),
require("karma-coverage"),
require("@angular-devkit/build-angular/plugins/karma"),
],
client: {
jasmine: {
@@ -19,26 +19,26 @@ module.exports = function (config) {
// for example, you can disable the random execution with `random: false`
// or set a specific seed with `seed: 4321`
},
clearContext: false // leave Jasmine Spec Runner output visible in browser
clearContext: false, // leave Jasmine Spec Runner output visible in browser
},
jasmineHtmlReporter: {
suppressAll: true // removes the duplicated traces
suppressAll: true, // removes the duplicated traces
},
coverageReporter: {
dir: require('path').join(__dirname, './coverage/frontend-hideyoshi.com'),
subdir: '.',
reporters: [
{ type: 'html' },
{ type: 'text-summary' }
]
dir: require("path").join(
__dirname,
"./coverage/frontend-hideyoshi.com",
),
subdir: ".",
reporters: [{ type: "html" }, { type: "text-summary" }],
},
reporters: ['progress', 'kjhtml'],
reporters: ["progress", "kjhtml"],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
browsers: ["Chrome"],
singleRun: false,
restartOnFileChange: true
restartOnFileChange: true,
});
};

737
package-lock.json generated
View File

@@ -51,12 +51,16 @@
"@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"
}
@@ -3251,6 +3255,18 @@
"node": ">=8"
}
},
"node_modules/@jest/schemas": {
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz",
"integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==",
"dev": true,
"dependencies": {
"@sinclair/typebox": "^0.27.8"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
@@ -4515,6 +4531,56 @@
"node": ">=14"
}
},
"node_modules/@pkgr/utils": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz",
"integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==",
"dev": true,
"dependencies": {
"cross-spawn": "^7.0.3",
"fast-glob": "^3.3.0",
"is-glob": "^4.0.3",
"open": "^9.1.0",
"picocolors": "^1.0.0",
"tslib": "^2.6.0"
},
"engines": {
"node": "^12.20.0 || ^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/unts"
}
},
"node_modules/@pkgr/utils/node_modules/define-lazy-prop": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
"integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@pkgr/utils/node_modules/open": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz",
"integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==",
"dev": true,
"dependencies": {
"default-browser": "^4.0.0",
"define-lazy-prop": "^3.0.0",
"is-inside-container": "^1.0.0",
"is-wsl": "^2.2.0"
},
"engines": {
"node": ">=14.16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@schematics/angular": {
"version": "16.2.0",
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-16.2.0.tgz",
@@ -4579,6 +4645,12 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
"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/@socket.io/component-emitter": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
@@ -5905,6 +5977,15 @@
"integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==",
"dev": true
},
"node_modules/big-integer": {
"version": "1.6.51",
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz",
"integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==",
"dev": true,
"engines": {
"node": ">=0.6"
}
},
"node_modules/big.js": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
@@ -6013,6 +6094,18 @@
"popper.js": "^1.16.1"
}
},
"node_modules/bplist-parser": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz",
"integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==",
"dev": true,
"dependencies": {
"big-integer": "^1.6.44"
},
"engines": {
"node": ">= 5.10.0"
}
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -6112,6 +6205,21 @@
"semver": "^7.0.0"
}
},
"node_modules/bundle-name": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz",
"integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==",
"dev": true,
"dependencies": {
"run-applescript": "^5.0.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@@ -6452,6 +6560,15 @@
"integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==",
"dev": true
},
"node_modules/common-tags": {
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz",
"integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==",
"dev": true,
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/compressible": {
"version": "2.0.18",
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
@@ -7067,6 +7184,150 @@
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
"dev": true
},
"node_modules/default-browser": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz",
"integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==",
"dev": true,
"dependencies": {
"bundle-name": "^3.0.0",
"default-browser-id": "^3.0.0",
"execa": "^7.1.1",
"titleize": "^3.0.0"
},
"engines": {
"node": ">=14.16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/default-browser-id": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz",
"integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==",
"dev": true,
"dependencies": {
"bplist-parser": "^0.2.0",
"untildify": "^4.0.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/default-browser/node_modules/execa": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz",
"integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==",
"dev": true,
"dependencies": {
"cross-spawn": "^7.0.3",
"get-stream": "^6.0.1",
"human-signals": "^4.3.0",
"is-stream": "^3.0.0",
"merge-stream": "^2.0.0",
"npm-run-path": "^5.1.0",
"onetime": "^6.0.0",
"signal-exit": "^3.0.7",
"strip-final-newline": "^3.0.0"
},
"engines": {
"node": "^14.18.0 || ^16.14.0 || >=18.0.0"
},
"funding": {
"url": "https://github.com/sindresorhus/execa?sponsor=1"
}
},
"node_modules/default-browser/node_modules/human-signals": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz",
"integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==",
"dev": true,
"engines": {
"node": ">=14.18.0"
}
},
"node_modules/default-browser/node_modules/is-stream": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
"integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
"dev": true,
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/default-browser/node_modules/mimic-fn": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
"integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/default-browser/node_modules/npm-run-path": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz",
"integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==",
"dev": true,
"dependencies": {
"path-key": "^4.0.0"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/default-browser/node_modules/onetime": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
"integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
"dev": true,
"dependencies": {
"mimic-fn": "^4.0.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/default-browser/node_modules/path-key": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
"integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/default-browser/node_modules/strip-final-newline": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
"integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/default-gateway": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz",
@@ -7181,6 +7442,12 @@
"node": ">=8"
}
},
"node_modules/dlv": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
"dev": true
},
"node_modules/dns-equal": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
@@ -7724,6 +7991,47 @@
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint-config-prettier": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz",
"integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==",
"dev": true,
"bin": {
"eslint-config-prettier": "bin/cli.js"
},
"peerDependencies": {
"eslint": ">=7.0.0"
}
},
"node_modules/eslint-plugin-prettier": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz",
"integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==",
"dev": true,
"dependencies": {
"prettier-linter-helpers": "^1.0.0",
"synckit": "^0.8.5"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/prettier"
},
"peerDependencies": {
"@types/eslint": ">=8.0.0",
"eslint": ">=8.0.0",
"prettier": ">=3.0.0"
},
"peerDependenciesMeta": {
"@types/eslint": {
"optional": true
},
"eslint-config-prettier": {
"optional": true
}
}
},
"node_modules/eslint-scope": {
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
@@ -8196,6 +8504,12 @@
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
},
"node_modules/fast-diff": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
"integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
"dev": true
},
"node_modules/fast-glob": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
@@ -8775,6 +9089,27 @@
"node": ">= 0.4.0"
}
},
"node_modules/has-ansi": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
"integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
"dev": true,
"dependencies": {
"ansi-regex": "^2.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/has-ansi/node_modules/ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/has-bigints": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
@@ -9561,6 +9896,39 @@
"node": ">=0.10.0"
}
},
"node_modules/is-inside-container": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
"integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
"dev": true,
"dependencies": {
"is-docker": "^3.0.0"
},
"bin": {
"is-inside-container": "cli.js"
},
"engines": {
"node": ">=14.16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-inside-container/node_modules/is-docker": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
"integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
"dev": true,
"bin": {
"is-docker": "cli.js"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-interactive": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
@@ -10708,6 +11076,84 @@
"node": ">=8.0"
}
},
"node_modules/loglevel": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.8.1.tgz",
"integrity": "sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg==",
"dev": true,
"engines": {
"node": ">= 0.6.0"
},
"funding": {
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/loglevel"
}
},
"node_modules/loglevel-colored-level-prefix": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz",
"integrity": "sha512-u45Wcxxc+SdAlh4yeF/uKlC1SPUPCy0gullSNKXod5I4bmifzk+Q4lSLExNEVn19tGaJipbZ4V4jbFn79/6mVA==",
"dev": true,
"dependencies": {
"chalk": "^1.1.3",
"loglevel": "^1.4.1"
}
},
"node_modules/loglevel-colored-level-prefix/node_modules/ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/loglevel-colored-level-prefix/node_modules/ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/loglevel-colored-level-prefix/node_modules/chalk": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
"dev": true,
"dependencies": {
"ansi-styles": "^2.2.1",
"escape-string-regexp": "^1.0.2",
"has-ansi": "^2.0.0",
"strip-ansi": "^3.0.0",
"supports-color": "^2.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/loglevel-colored-level-prefix/node_modules/strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
"dev": true,
"dependencies": {
"ansi-regex": "^2.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/loglevel-colored-level-prefix/node_modules/supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
"integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
"dev": true,
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -12634,6 +13080,171 @@
"node": ">= 0.8.0"
}
},
"node_modules/prettier": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz",
"integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==",
"dev": true,
"bin": {
"prettier": "bin/prettier.cjs"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/prettier-eslint": {
"version": "16.1.1",
"resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-16.1.1.tgz",
"integrity": "sha512-SbtugbH80njB9QOPqb8C+W40Rvhr6iD0wrJTxk1Zx10rkY7KdjtSwHpf/WfiI3REboaXbvIOJXGiua3maIt0Sw==",
"dev": true,
"dependencies": {
"@typescript-eslint/parser": "^6.7.5",
"common-tags": "^1.4.0",
"dlv": "^1.1.0",
"eslint": "^8.7.0",
"indent-string": "^4.0.0",
"lodash.merge": "^4.6.0",
"loglevel-colored-level-prefix": "^1.0.0",
"prettier": "^3.0.1",
"pretty-format": "^29.7.0",
"require-relative": "^0.8.7",
"typescript": "^5.2.2",
"vue-eslint-parser": "^9.1.0"
},
"engines": {
"node": ">=16.10.0"
}
},
"node_modules/prettier-eslint/node_modules/@typescript-eslint/parser": {
"version": "6.7.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.5.tgz",
"integrity": "sha512-bIZVSGx2UME/lmhLcjdVc7ePBwn7CLqKarUBL4me1C5feOd663liTGjMBGVcGr+BhnSLeP4SgwdvNnnkbIdkCw==",
"dev": true,
"dependencies": {
"@typescript-eslint/scope-manager": "6.7.5",
"@typescript-eslint/types": "6.7.5",
"@typescript-eslint/typescript-estree": "6.7.5",
"@typescript-eslint/visitor-keys": "6.7.5",
"debug": "^4.3.4"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^7.0.0 || ^8.0.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/prettier-eslint/node_modules/@typescript-eslint/scope-manager": {
"version": "6.7.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.5.tgz",
"integrity": "sha512-GAlk3eQIwWOJeb9F7MKQ6Jbah/vx1zETSDw8likab/eFcqkjSD7BI75SDAeC5N2L0MmConMoPvTsmkrg71+B1A==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.7.5",
"@typescript-eslint/visitor-keys": "6.7.5"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/prettier-eslint/node_modules/@typescript-eslint/types": {
"version": "6.7.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.5.tgz",
"integrity": "sha512-WboQBlOXtdj1tDFPyIthpKrUb+kZf2VroLZhxKa/VlwLlLyqv/PwUNgL30BlTVZV1Wu4Asu2mMYPqarSO4L5ZQ==",
"dev": true,
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/prettier-eslint/node_modules/@typescript-eslint/typescript-estree": {
"version": "6.7.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.5.tgz",
"integrity": "sha512-NhJiJ4KdtwBIxrKl0BqG1Ur+uw7FiOnOThcYx9DpOGJ/Abc9z2xNzLeirCG02Ig3vkvrc2qFLmYSSsaITbKjlg==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.7.5",
"@typescript-eslint/visitor-keys": "6.7.5",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
"semver": "^7.5.4",
"ts-api-utils": "^1.0.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/prettier-eslint/node_modules/@typescript-eslint/visitor-keys": {
"version": "6.7.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.5.tgz",
"integrity": "sha512-3MaWdDZtLlsexZzDSdQWsFQ9l9nL8B80Z4fImSpyllFC/KLqWQRdEcB+gGGO+N3Q2uL40EsG66wZLsohPxNXvg==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.7.5",
"eslint-visitor-keys": "^3.4.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/prettier-eslint/node_modules/typescript": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/prettier-linter-helpers": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
"integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
"dev": true,
"dependencies": {
"fast-diff": "^1.1.2"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/pretty-bytes": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
@@ -12646,6 +13257,32 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/pretty-format": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
"dev": true,
"dependencies": {
"@jest/schemas": "^29.6.3",
"ansi-styles": "^5.0.0",
"react-is": "^18.0.0"
},
"engines": {
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
}
},
"node_modules/pretty-format/node_modules/ansi-styles": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
"dev": true,
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/proc-log": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz",
@@ -12800,6 +13437,12 @@
"node": ">= 0.8"
}
},
"node_modules/react-is": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
"dev": true
},
"node_modules/read-package-json": {
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-6.0.4.tgz",
@@ -13036,6 +13679,12 @@
"node": ">=0.10.0"
}
},
"node_modules/require-relative": {
"version": "0.8.7",
"resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz",
"integrity": "sha512-AKGr4qvHiryxRb19m3PsLRGuKVAbJLUD7E6eOaHkfKhwc+vSgVOCY5xNvm9EkolBKTOf0GrQAZKLimOCz81Khg==",
"dev": true
},
"node_modules/requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
@@ -13176,6 +13825,21 @@
"fsevents": "~2.3.2"
}
},
"node_modules/run-applescript": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz",
"integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==",
"dev": true,
"dependencies": {
"execa": "^5.0.0"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/run-async": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
@@ -14125,6 +14789,22 @@
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
"dev": true
},
"node_modules/synckit": {
"version": "0.8.5",
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz",
"integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==",
"dev": true,
"dependencies": {
"@pkgr/utils": "^2.3.1",
"tslib": "^2.5.0"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"funding": {
"url": "https://opencollective.com/unts"
}
},
"node_modules/tapable": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
@@ -14342,6 +15022,18 @@
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
"dev": true
},
"node_modules/titleize": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz",
"integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==",
"dev": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/tmp": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
@@ -14428,6 +15120,18 @@
"tree-kill": "cli.js"
}
},
"node_modules/ts-api-utils": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz",
"integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==",
"dev": true,
"engines": {
"node": ">=16.13.0"
},
"peerDependencies": {
"typescript": ">=4.2.0"
}
},
"node_modules/ts-interface-builder": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/ts-interface-builder/-/ts-interface-builder-0.3.3.tgz",
@@ -14699,6 +15403,15 @@
"node": ">= 0.8"
}
},
"node_modules/untildify": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz",
"integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==",
"dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/update-browserslist-db": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz",
@@ -14871,6 +15584,30 @@
"node": ">=0.10.0"
}
},
"node_modules/vue-eslint-parser": {
"version": "9.3.2",
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.3.2.tgz",
"integrity": "sha512-q7tWyCVaV9f8iQyIA5Mkj/S6AoJ9KBN8IeUSf3XEmBrOtxOZnfTg5s4KClbZBCK3GtnT/+RyCLZyDHuZwTuBjg==",
"dev": true,
"dependencies": {
"debug": "^4.3.4",
"eslint-scope": "^7.1.1",
"eslint-visitor-keys": "^3.3.0",
"espree": "^9.3.1",
"esquery": "^1.4.0",
"lodash": "^4.17.21",
"semver": "^7.3.6"
},
"engines": {
"node": "^14.17.0 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/mysticatea"
},
"peerDependencies": {
"eslint": ">=6.0.0"
}
},
"node_modules/w3c-hr-time": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",

View File

@@ -58,12 +58,16 @@
"@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"
}

View File

@@ -1,6 +1,6 @@
const express = require('express');
const cors = require('cors');
const path = require('path');
const express = require("express");
const cors = require("cors");
const path = require("path");
const PKG_NAME = "frontend-hideyoshi.com";
@@ -9,7 +9,7 @@ app.use(cors());
app.use(express.static(`${__dirname}/dist/${PKG_NAME}`));
app.get('/*', (req, res) => {
app.get("/*", (req, res) => {
res.sendFile(path.join(`${__dirname}/dist/${PKG_NAME}/index.html`));
});

View File

@@ -8,26 +8,21 @@ const routes: Routes = [
{
path: '',
redirectTo: '/home',
pathMatch: 'full'
pathMatch: 'full',
},
{
path: 'home',
component: HomeComponent
component: HomeComponent,
},
{
path: 'callback',
component: CallbackComponent
}
]
component: CallbackComponent,
},
];
@NgModule({
declarations: [],
imports: [
CommonModule,
RouterModule.forRoot(routes)
],
exports: [
RouterModule
]
imports: [CommonModule, RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRouterModule { }
export class AppRouterModule {}

View File

@@ -3,19 +3,15 @@ import { CommonModule } from '@angular/common';
import { environment } from 'src/environments/environment';
import { ServiceWorkerModule } from '@angular/service-worker';
@NgModule({
declarations: [],
imports: [
CommonModule,
ServiceWorkerModule.register('ngsw-worker.js', {
enabled: environment.production,
registrationStrategy: 'registerWhenStable:30000'
})
registrationStrategy: 'registerWhenStable:30000',
}),
],
exports: [
ServiceWorkerModule
]
exports: [ServiceWorkerModule],
})
export class AppServiceWorkerModule { }
export class AppServiceWorkerModule {}

View File

@@ -4,9 +4,7 @@ import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [
AppComponent
],
declarations: [AppComponent],
}).compileComponents();
});
@@ -26,6 +24,8 @@ describe('AppComponent', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('.content span')?.textContent).toContain('frontend-hideyoshi.com app is running!');
expect(compiled.querySelector('.content span')?.textContent).toContain(
'frontend-hideyoshi.com app is running!',
);
});
});

View File

@@ -1,22 +1,22 @@
import {Component, OnDestroy, OnInit} from '@angular/core';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { AuthService } from './shared/auth/auth.service';
import {UpdateService} from "./shared/service-worker/update.service";
import { UpdateService } from './shared/service-worker/update.service';
import {
NgcCookieConsentService,
NgcInitializationErrorEvent,
NgcInitializingEvent,
NgcNoCookieLawEvent, NgcStatusChangeEvent
} from "ngx-cookieconsent";
import {Subscription} from "rxjs";
import {CookieConsertService} from "./shared/cookie-consent/cookie-consert.service";
NgcNoCookieLawEvent,
NgcStatusChangeEvent,
} from 'ngx-cookieconsent';
import { Subscription } from 'rxjs';
import { CookieConsertService } from './shared/cookie-consent/cookie-consert.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
title = 'frontend-hideyoshi.com';
cookieStatusChangeSubscription!: Subscription;
@@ -25,28 +25,30 @@ export class AppComponent implements OnInit {
private authService: AuthService,
private ccService: NgcCookieConsentService,
private cookieConsentService: CookieConsertService,
private serviceWorker: UpdateService) {
private serviceWorker: UpdateService,
) {
this.serviceWorker.checkForUpdates();
}
ngOnInit(): void {
this.authService.autoLogin();
let cookieConsentStatus = this.cookieConsentService.getCookieConsentStatusFromLocalStorage();
let cookieConsentStatus =
this.cookieConsentService.getCookieConsentStatusFromLocalStorage();
if (cookieConsentStatus) {
this.ccService.destroy();
}
this.cookieStatusChangeSubscription = this.ccService.statusChange$.subscribe(
this.cookieStatusChangeSubscription =
this.ccService.statusChange$.subscribe(
(event: NgcStatusChangeEvent) => {
if (event.status === 'allow') {
this.cookieConsentService.consent();
} else if (event.status === 'deny') {
this.cookieConsentService.decline();
}
}
},
);
}
}

View File

@@ -10,14 +10,10 @@ import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { AppServiceWorkerModule } from './app-service-worker.module';
import { ServiceWorkerModule } from '@angular/service-worker';
import { environment } from '../environments/environment';
import {FooterComponent} from "./footer/footer.component";
import { FooterComponent } from './footer/footer.component';
@NgModule({
declarations: [
AppComponent,
HomeComponent,
FooterComponent
],
declarations: [AppComponent, HomeComponent, FooterComponent],
imports: [
BrowserModule,
HeaderModule,
@@ -27,10 +23,10 @@ import {FooterComponent} from "./footer/footer.component";
FontAwesomeModule,
ServiceWorkerModule.register('ngsw-worker.js', {
enabled: environment.production,
registrationStrategy: 'registerWhenStable:30000'
})
registrationStrategy: 'registerWhenStable:30000',
}),
],
providers: [],
bootstrap: [AppComponent]
bootstrap: [AppComponent],
})
export class AppModule { }
export class AppModule {}

View File

@@ -5,23 +5,27 @@
<section class="mb-2">
<!-- Twitter -->
<a class="btn footer-btn" href="https://twitter.com/NakazoneVitor">
<fa-icon class="input-div-icon"
[icon]="_twitterIcon">
<fa-icon class="input-div-icon" [icon]="_twitterIcon">
</fa-icon>
</a>
<!-- Linkedin -->
<a class="btn footer-btn" href="https://www.linkedin.com/in/vitor-hideyoshi/" role="button">
<fa-icon class="input-div-icon"
[icon]="_linkedinIcon">
<a
class="btn footer-btn"
href="https://www.linkedin.com/in/vitor-hideyoshi/"
role="button"
>
<fa-icon class="input-div-icon" [icon]="_linkedinIcon">
</fa-icon>
</a>
<!-- Github -->
<a class="btn footer-btn" href="https://github.com/HideyoshiNakazone" role="button">
<fa-icon class="input-div-icon"
[icon]="_githubIcon">
</fa-icon>
<a
class="btn footer-btn"
href="https://github.com/HideyoshiNakazone"
role="button"
>
<fa-icon class="input-div-icon" [icon]="_githubIcon"> </fa-icon>
</a>
</section>
<!-- Section: Social media -->
@@ -29,9 +33,11 @@
<!-- Grid container -->
<!-- Copyright -->
<div class="text-center p-3" style="background-color: #2E2E2E;">
<div class="text-center p-3" style="background-color: #2e2e2e">
© 2023 Copyright:
<a class="text-white" href="https://hideyoshi.com.br/">Hideyoshi Solutions</a>
<a class="text-white" href="https://hideyoshi.com.br/"
>Hideyoshi Solutions</a
>
</div>
<!-- Copyright -->
</footer>

View File

@@ -8,7 +8,7 @@ describe('FooterComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [FooterComponent]
declarations: [FooterComponent],
});
fixture = TestBed.createComponent(FooterComponent);
component = fixture.componentInstance;

View File

@@ -1,11 +1,14 @@
import {Component} from '@angular/core';
import {faGithub, faLinkedinIn, faTwitter} from "@fortawesome/free-brands-svg-icons";
import { Component } from '@angular/core';
import {
faGithub,
faLinkedinIn,
faTwitter,
} from '@fortawesome/free-brands-svg-icons';
@Component({
selector: 'app-footer',
templateUrl: './footer.component.html',
styleUrls: ['./footer.component.css']
styleUrls: ['./footer.component.css'],
})
export class FooterComponent {
_githubIcon = faGithub;

View File

@@ -2,19 +2,19 @@
width: fit-content;
border-radius: 8px;
background-color: #ffffff;
border: 1px solid rgba(46, 46, 46, .3);
border: 1px solid rgba(46, 46, 46, 0.3);
box-sizing: 0 5px 25px rgba(0, 0, 0, 0.1);
box-shadow: 1px 1px 4px 1px rgba(0, 0, 0, 0.2);
}
.dropdown:before {
content: '';
content: "";
width: 15px;
height: 15px;
position: absolute;
background-color: #ffffff;
border-top: 1px solid rgba(46, 46, 46, .3);
border-left: 1px solid rgba(46, 46, 46, .3);
border-top: 1px solid rgba(46, 46, 46, 0.3);
border-left: 1px solid rgba(46, 46, 46, 0.3);
transform: translateX(120px) translateY(-50%) rotate(45deg);
}
.info {
@@ -59,12 +59,12 @@
.dropdown-item:hover .icon-box fa-icon {
opacity: 1;
color: #f44336;
transition: .5s;
transition: 0.5s;
}
.dropdown-item p {
color: #555555;
font-family: 'Montserrat', sans-serif;
font-family: "Montserrat", sans-serif;
font-weight: 400;
text-decoration: none;
padding-left: 10px;

View File

@@ -6,9 +6,12 @@
[ignoreElementList]="ignoreClickOutside"
[@dropdownState]="dropDownState"
(@dropdownState.start)="$event.element.style.display = 'block'"
(@dropdownState.done)="$event.element.style.display = (state ? 'block' : 'none')">
(@dropdownState.done)="
$event.element.style.display = state ? 'block' : 'none'
"
>
<div class="info">
<h3>{{ this.user ? this.user.username : 'User Account' }}</h3>
<h3>{{ this.user ? this.user.username : "User Account" }}</h3>
</div>
<div #management>
<ul class="user-management" *ngIf="!this.user">
@@ -34,13 +37,19 @@
</li>
<li class="dropdown-item" (click)="onHelpClicked()">
<div class="icon-box">
<fa-icon class="fas fa-question-circle" [icon]="questionCircleIcon"></fa-icon>
<fa-icon
class="fas fa-question-circle"
[icon]="questionCircleIcon"
></fa-icon>
</div>
<p>Help</p>
</li>
<li class="dropdown-item" (click)="onLogout()">
<div class="icon-box">
<fa-icon class="fas fa-sign-out-alt" [icon]="signOutAltIcon"></fa-icon>
<fa-icon
class="fas fa-sign-out-alt"
[icon]="signOutAltIcon"
></fa-icon>
</div>
<p>Logout</p>
</li>

View File

@@ -8,9 +8,8 @@ describe('HeaderDropdownComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ HeaderDropdownComponent ]
})
.compileComponents();
declarations: [HeaderDropdownComponent],
}).compileComponents();
fixture = TestBed.createComponent(HeaderDropdownComponent);
component = fixture.componentInstance;

View File

@@ -1,12 +1,32 @@
import { animate, state, style, transition, trigger } from '@angular/animations';
import {Component, ComponentRef, EventEmitter, Input, OnDestroy, OnInit, Output, ViewContainerRef} from '@angular/core';
import { faEdit, faQuestionCircle, faSignOutAlt, faUser } from '@fortawesome/free-solid-svg-icons';
import {
animate,
state,
style,
transition,
trigger,
} from '@angular/animations';
import {
Component,
ComponentRef,
EventEmitter,
Input,
OnDestroy,
OnInit,
Output,
ViewContainerRef,
} from '@angular/core';
import {
faEdit,
faQuestionCircle,
faSignOutAlt,
faUser,
} from '@fortawesome/free-solid-svg-icons';
import { Subscription } from 'rxjs';
import { AuthService } from 'src/app/shared/auth/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 { 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';
@Component({
selector: 'app-header-dropdown',
@@ -14,19 +34,24 @@ import {MyProfileComponent} from "../header-popup/my-profile/my-profile.componen
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'))
])
]
transition('show => hide', animate('5ms ease-out')),
]),
],
})
export class HeaderDropdownComponent implements OnInit, OnDestroy {
userIcon = faUser;
editIcon = faEdit;
@@ -60,18 +85,21 @@ export class HeaderDropdownComponent implements OnInit, OnDestroy {
@Output()
myProfilePopupState: EventEmitter<boolean> = new EventEmitter();
constructor(private viewContainerRef: ViewContainerRef, private authService: AuthService) { }
constructor(
private viewContainerRef: ViewContainerRef,
private authService: AuthService,
) {}
ngOnInit(): void {
this.userSubscription = this.authService.authSubject.subscribe(
res => {
(res) => {
if (res && UserChecker.test(res)) {
this.user = <User>res;
} else {
this.user = null;
}
}
)
},
);
}
ngOnDestroy(): void {

View File

@@ -8,9 +8,8 @@ describe('CallbackComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ CallbackComponent ]
})
.compileComponents();
declarations: [CallbackComponent],
}).compileComponents();
fixture = TestBed.createComponent(CallbackComponent);
component = fixture.componentInstance;

View File

@@ -5,35 +5,32 @@ import { AuthService } from 'src/app/shared/auth/auth.service';
@Component({
selector: 'app-callback',
templateUrl: './callback.component.html',
styleUrls: ['./callback.component.css']
styleUrls: ['./callback.component.css'],
})
export class CallbackComponent implements OnInit {
constructor(private route: ActivatedRoute,
constructor(
private route: ActivatedRoute,
private router: Router,
private authService: AuthService) { }
private authService: AuthService,
) {}
ngOnInit(): void {
this.route.queryParams.subscribe(p => {
this.route.queryParams.subscribe((p) => {
let auth: 'google' | 'github' = p['auth'];
switch (auth) {
case "github":
this.authService.loginGithubUser(p)
case 'github':
this.authService.loginGithubUser(p);
break;
case "google":
this.authService.loginGoogleUser(p)
case 'google':
this.authService.loginGoogleUser(p);
break;
default:
console.log(`Unimplemented auth: ${auth}`)
console.log(`Unimplemented auth: ${auth}`);
break;
}
this.router.navigate(['/home'])
})
this.router.navigate(['/home']);
});
}
}

View File

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

View File

@@ -8,9 +8,8 @@ describe('ErrorBoxComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ErrorBoxComponent ]
})
.compileComponents();
declarations: [ErrorBoxComponent],
}).compileComponents();
fixture = TestBed.createComponent(ErrorBoxComponent);
component = fixture.componentInstance;

View File

@@ -1,14 +1,13 @@
import {Component, Input} from '@angular/core';
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-error-box',
templateUrl: './error-box.component.html',
styleUrls: ['./error-box.component.css']
styleUrls: ['./error-box.component.css'],
})
export class ErrorBoxComponent {
@Input()
errorMessage: string|null = "Error, please try again later."
constructor() { }
errorMessage: string | null = 'Error, please try again later.';
constructor() {}
}

View File

@@ -1,28 +1,32 @@
<app-popup [state]="state"
<app-popup
[state]="state"
(stateChange)="onStateChange($event)"
[ignoreClickOutside]="ignoreClickOutside">
[ignoreClickOutside]="ignoreClickOutside"
>
<div class="help-container container m-0 overflow-hidden">
<p>
This is a simple example project to demonstrate
User Authentication and Authorization using
<a href="https://spring.io/projects/spring-security" target="_blank">Spring Security</a>
This is a simple example project to demonstrate User Authentication
and Authorization using
<a href="https://spring.io/projects/spring-security" target="_blank"
>Spring Security</a
>
and
<a href="https://docs.spring.io/spring-security/reference/servlet/oauth2/" target="_blank">OAuth2</a>.
<a
href="https://docs.spring.io/spring-security/reference/servlet/oauth2/"
target="_blank"
>OAuth2</a
>.
<br/><br/>
<br /><br />
The only data stored is your email address, username and name.
This data is stored in a database and is used to authenticate you
and will not be used for any other purpose.
The only data stored is your email address, username and name. This
data is stored in a database and is used to authenticate you and
will not be used for any other purpose.
<br/><br/>
<br /><br />
All data can be deleted by clicking the "Delete Account" button
on the "My Profile" option.
All data can be deleted by clicking the "Delete Account" button on
the "My Profile" option.
</p>
</div>
</app-popup>

View File

@@ -8,9 +8,8 @@ describe('HelpComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ HelpComponent ]
})
.compileComponents();
declarations: [HelpComponent],
}).compileComponents();
fixture = TestBed.createComponent(HelpComponent);
component = fixture.componentInstance;

View File

@@ -1,12 +1,11 @@
import {Component, EventEmitter, Input, Output} from '@angular/core';
import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
selector: 'app-help',
templateUrl: './help.component.html',
styleUrls: ['./help.component.css']
styleUrls: ['./help.component.css'],
})
export class HelpComponent {
@Input()
state: boolean = false;
@@ -16,7 +15,7 @@ export class HelpComponent {
@Output()
stateChange = new EventEmitter<boolean>();
constructor() { }
constructor() {}
onStateChange(state: boolean) {
this.stateChange.emit(state);

View File

@@ -17,7 +17,7 @@
}
.authentication-body .btn {
background-color: #D8291C !important;
background-color: #d8291c !important;
text-decoration: none;
border-radius: 8px;
color: #ffffff;
@@ -67,7 +67,7 @@
border-bottom: 2px solid #7676769b;
}
.input-div:after {
content: '';
content: "";
left: 0;
right: 0;
width: 0;
@@ -81,11 +81,12 @@
.input-div:hover:after,
.input-div:has(input.form-control:focus):after {
width: 100%;
transition: .4s;
transition: 0.4s;
overflow: hidden;
}
.input-div > .form-control, .input-div > .form-control:hover{
.input-div > .form-control,
.input-div > .form-control:hover {
border: none;
border-color: inherit;
-webkit-box-shadow: none;
@@ -98,7 +99,7 @@
font-size: 17px;
color: #767676;
}
.input-div >.form-control:-webkit-autofill {
.input-div > .form-control:-webkit-autofill {
-webkit-text-fill-color: #767676;
box-shadow: 0 0 0px 1000px #ffffff inset;
-webkit-box-shadow: 0 0 0px 1000px #ffffff inset;
@@ -108,19 +109,16 @@
.input-div:hover > .form-control::placeholder,
.input-div:hover > .input-div-icon,
.input-div:has(input.form-control:focus) > .input-div-icon {
color: #D8291C;
transition: .3s;
color: #d8291c;
transition: 0.3s;
}
.input-div:hover > .form-control::placeholder,
.input-div:has(input.form-control:focus) > .form-control::placeholder {
font-weight: 500;
transition: .3s;
transition: 0.3s;
}
@media (min-width:767px) {
@media (min-width: 767px) {
.authentication-container {
min-width: 630px;
}
@@ -150,5 +148,4 @@
border-right: 2px solid #80808076;
height: 100%;
}
}

View File

@@ -1,41 +1,56 @@
<app-popup [state]="state"
<app-popup
[state]="state"
(stateChange)="onStateChange($event)"
[ignoreClickOutside]="ignoreClickOutside">
<div class="container m-0 overflow-hidden"
[@resizeContainerForErrorMessage]="hideErrorMessage()">
<app-error-box [errorMessage]="errorMessage"
[@showErrorMessage]="showErrorMessage()">
[ignoreClickOutside]="ignoreClickOutside"
>
<div
class="container m-0 overflow-hidden"
[@resizeContainerForErrorMessage]="hideErrorMessage()"
>
<app-error-box
[errorMessage]="errorMessage"
[@showErrorMessage]="showErrorMessage()"
>
</app-error-box>
<div class="container authentication-container"
<div
class="container authentication-container"
[@hideAuthContainer]="hideErrorMessage()"
(@hideAuthContainer.done)="hideAuthContainer($event)">
(@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 class="input-div-icon" [icon]="_userIcon">
</fa-icon>
<input type="text" id="username"
<input
type="text"
id="username"
formControlName="username"
class="form-control"
placeholder="Username">
placeholder="Username"
/>
</div>
<div class="input-div">
<fa-icon class="input-div-icon"
[icon]="_passwordIcon">
<fa-icon
class="input-div-icon"
[icon]="_passwordIcon"
>
</fa-icon>
<input type="password" id="password"
<input
type="password"
id="password"
formControlName="password"
class="form-control"
placeholder="Password">
placeholder="Password"
/>
</div>
<button class="btn"
<button
class="btn"
[disabled]="loginForm.invalid"
type="submit">
type="submit"
>
Login
</button>
</form>
@@ -44,30 +59,38 @@
<div class="line"></div>
</div>
<div class="col-lg-6 authentication-body">
<button mat-button
<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>
(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
<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>
(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

@@ -8,9 +8,8 @@ describe('LoginComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ LoginComponent ]
})
.compileComponents();
declarations: [LoginComponent],
}).compileComponents();
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;

View File

@@ -1,24 +1,44 @@
import { AfterViewInit, ChangeDetectorRef, Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
import {
AfterViewInit,
ChangeDetectorRef,
Component,
EventEmitter,
Input,
OnDestroy,
OnInit,
Output,
} from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { MatIconRegistry } from '@angular/material/icon';
import { DomSanitizer } from '@angular/platform-browser';
import { faLock, faUser } from '@fortawesome/free-solid-svg-icons';
import {Subscription} from 'rxjs';
import { Subscription } from 'rxjs';
import { AuthService } from 'src/app/shared/auth/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, animateChild, group, query, state, style, transition, trigger} from "@angular/animations";
import {ValidatePasswordValidator} from "../../../shared/validators/validate-password.validator";
import {ValidateNotEmptyValidator} from "../../../shared/validators/validate-not-empty.validator";
import {NgcCookieConsentService, NgcStatusChangeEvent} from "ngx-cookieconsent";
import {CookieConsertService} from "../../../shared/cookie-consent/cookie-consert.service";
import {
animate,
animateChild,
group,
query,
state,
style,
transition,
trigger,
} from '@angular/animations';
import { ValidatePasswordValidator } from '../../../shared/validators/validate-password.validator';
import { ValidateNotEmptyValidator } from '../../../shared/validators/validate-not-empty.validator';
import {
NgcCookieConsentService,
NgcStatusChangeEvent,
} from 'ngx-cookieconsent';
import { CookieConsertService } from '../../../shared/cookie-consent/cookie-consert.service';
const GOOGLE_LOGO_SVG = "assets/img/providers/google.svg";
const GOOGLE_DISABLED_LOGO_SVG = "assets/img/providers/google-disabled.svg";
const GITHUB_LOGO_SVG = "assets/img/providers/github.svg";
const GOOGLE_LOGO_SVG = 'assets/img/providers/google.svg';
const GOOGLE_DISABLED_LOGO_SVG = 'assets/img/providers/google-disabled.svg';
const GITHUB_LOGO_SVG = 'assets/img/providers/github.svg';
@Component({
selector: 'app-login',
@@ -26,70 +46,58 @@ const GITHUB_LOGO_SVG = "assets/img/providers/github.svg";
styleUrls: ['./login.component.css'],
animations: [
trigger('resizeContainerForErrorMessage', [
state('hide',
state(
'hide',
style({
height: '100px',
width: '320px',
})
}),
),
transition(
'show => hide',
group([
query(
"@*",
animateChild(),
{ optional: true }
query('@*', animateChild(), { optional: true }),
animate('1s ease'),
]),
),
animate('1s ease')
])
)
]),
trigger('showErrorMessage', [
state('show',
state(
'show',
style({
opacity: 1,
height: '100px',
width: '320px',
})
}),
),
state('hide',
state(
'hide',
style({
opacity: 0,
height: '0px',
width: '0px',
})
),
transition(
'* => show',
animate(
'500ms ease-in'
)
}),
),
transition('* => show', animate('500ms ease-in')),
]),
trigger('hideAuthContainer', [
state('hide',
state(
'hide',
style({
opacity: 0,
})
}),
),
transition(
'show => hide',
group([
query(
"@*",
animateChild(),
{ optional: true }
),
animate(
'250ms ease-out'
)
])
)
query('@*', animateChild(), { optional: true }),
animate('250ms ease-out'),
]),
]
),
]),
],
})
export class LoginComponent implements OnInit, AfterViewInit, OnDestroy {
@Input()
state: boolean = false;
@@ -121,38 +129,46 @@ export class LoginComponent implements OnInit, AfterViewInit, OnDestroy {
private cookieConsentService: CookieConsertService,
private changeDetectorRef: ChangeDetectorRef,
private matIconRegistry: MatIconRegistry,
private domSanitizer: DomSanitizer) {
private domSanitizer: DomSanitizer,
) {
this.matIconRegistry.addSvgIcon(
"google-logo",
this.domSanitizer.bypassSecurityTrustResourceUrl(GOOGLE_LOGO_SVG)
'google-logo',
this.domSanitizer.bypassSecurityTrustResourceUrl(GOOGLE_LOGO_SVG),
);
this.matIconRegistry.addSvgIcon(
"google-disabled-logo",
this.domSanitizer.bypassSecurityTrustResourceUrl(GOOGLE_DISABLED_LOGO_SVG)
)
'google-disabled-logo',
this.domSanitizer.bypassSecurityTrustResourceUrl(
GOOGLE_DISABLED_LOGO_SVG,
),
);
this.matIconRegistry.addSvgIcon(
"github-logo",
this.domSanitizer.bypassSecurityTrustResourceUrl(GITHUB_LOGO_SVG)
'github-logo',
this.domSanitizer.bypassSecurityTrustResourceUrl(GITHUB_LOGO_SVG),
);
}
ngOnInit(): void {
this.loginForm = new FormGroup({
'username': new FormControl(null, [Validators.required, ValidateNotEmptyValidator]),
'password': new FormControl(null, [Validators.required, ValidatePasswordValidator])
username: new FormControl(null, [
Validators.required,
ValidateNotEmptyValidator,
]),
password: new FormControl(null, [
Validators.required,
ValidatePasswordValidator,
]),
});
this.errorMessage = null;
this.authSubject = this.authService.authSubject.subscribe(
res => {
this.authSubject = this.authService.authSubject.subscribe((res) => {
this.validateLogin(res);
}
);
});
this.cookieStatusChangeSubscription = this.cookieConsentService.cookieStatusChangeSubscription.subscribe(
this.cookieStatusChangeSubscription =
this.cookieConsentService.cookieStatusChangeSubscription.subscribe(
(status: boolean) => {
this.isCookieBlocked = !status;
console.log("Cookie status: " + status);
}
console.log('Cookie status: ' + status);
},
);
if (this.isCookieBlocked) {
@@ -176,8 +192,8 @@ export class LoginComponent implements OnInit, AfterViewInit, OnDestroy {
onLogin() {
let user: User = {
username: this.loginForm.controls['username'].value,
password: this.loginForm.controls['password'].value
}
password: this.loginForm.controls['password'].value,
};
this.authService.login(user);
}
@@ -191,11 +207,11 @@ export class LoginComponent implements OnInit, AfterViewInit, OnDestroy {
private validateLogin(res: User | HttpError | null) {
if (res && UserChecker.test(res)) {
this.closePopup()
} if (HttpErrorChecker.test(res)) {
this.closePopup();
}
if (HttpErrorChecker.test(res)) {
this.errorMessage = (<HttpError>res).details;
}
}
private closePopup() {
@@ -205,23 +221,22 @@ export class LoginComponent implements OnInit, AfterViewInit, OnDestroy {
public showErrorMessage(): string {
if (this.isShowErrorMessage) {
return "show";
return 'show';
}
return "hide";
return 'hide';
}
public hideErrorMessage(): string {
if (!!this.errorMessage) {
return "hide";
return 'hide';
}
return "show";
return 'show';
}
hideAuthContainer(event: any) {
if (event.toState === "hide") {
event.element.style.display = "none";
if (event.toState === 'hide') {
event.element.style.display = 'none';
this.isShowErrorMessage = true;
}
}
}

View File

@@ -36,7 +36,6 @@
}
.profile-options-container button {
text-decoration: none;
border-radius: 8px;
color: #ffffff;
@@ -55,9 +54,7 @@
background-color: rgba(216, 41, 28, 0.7) !important;
}
@media (min-width:767px) {
@media (min-width: 767px) {
.profile-options-container {
all: unset;
justify-content: space-around;
@@ -96,5 +93,4 @@
border-radius: 50px;
height: 100%;
}
}

View File

@@ -1,23 +1,28 @@
<app-popup [state]="state"
<app-popup
[state]="state"
(stateChange)="onStateChange($event)"
[ignoreClickOutside]="ignoreClickOutside">
<div class="container m-0 overflow-hidden"
[@resizeContainerForErrorMessage]="hideErrorMessage()">
<app-error-box [errorMessage]="errorMessage"
[@showErrorMessage]="showErrorMessage()">
[ignoreClickOutside]="ignoreClickOutside"
>
<div
class="container m-0 overflow-hidden"
[@resizeContainerForErrorMessage]="hideErrorMessage()"
>
<app-error-box
[errorMessage]="errorMessage"
[@showErrorMessage]="showErrorMessage()"
>
</app-error-box>
<div class="container profile-options-container"
<div
class="container profile-options-container"
[@hideAuthContainer]="hideErrorMessage()"
(@hideAuthContainer.done)="hideAuthContainer($event)">
(@hideAuthContainer.done)="hideAuthContainer($event)"
>
<div class="row profile-options-row">
<div class="btn-container">
<app-profile-picture-picker
(imageSent)="onProfilePictureSent($event)">
(imageSent)="onProfilePictureSent($event)"
>
</app-profile-picture-picker>
</div>
@@ -26,16 +31,11 @@
</div>
<div class="btn-container">
<button class="delete-btn"
(click)="onDeleteAccount()">
<button class="delete-btn" (click)="onDeleteAccount()">
Delete Account
</button>
</div>
</div>
</div>
</div>
</app-popup>

View File

@@ -8,9 +8,8 @@ describe('MyProfileComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ MyProfileComponent ]
})
.compileComponents();
declarations: [MyProfileComponent],
}).compileComponents();
fixture = TestBed.createComponent(MyProfileComponent);
component = fixture.componentInstance;

View File

@@ -1,18 +1,33 @@
import {ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {AuthService} from "../../../shared/auth/auth.service";
import {User} from "../../../shared/model/user/user.model";
import {animate, animateChild, group, query, state, style, 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";
import {
ChangeDetectorRef,
Component,
EventEmitter,
Input,
OnInit,
Output,
} from '@angular/core';
import { AuthService } from '../../../shared/auth/auth.service';
import { User } from '../../../shared/model/user/user.model';
import {
animate,
animateChild,
group,
query,
state,
style,
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({
selector: 'app-my-profile',
@@ -20,70 +35,58 @@ import {faFileUpload} from "@fortawesome/free-solid-svg-icons";
styleUrls: ['./my-profile.component.css'],
animations: [
trigger('resizeContainerForErrorMessage', [
state('hide',
state(
'hide',
style({
height: '100px',
width: '320px',
})
}),
),
transition(
'show => hide',
group([
query(
"@*",
animateChild(),
{ optional: true }
query('@*', animateChild(), { optional: true }),
animate('1s ease'),
]),
),
animate('1s ease')
])
)
]),
trigger('showErrorMessage', [
state('show',
state(
'show',
style({
opacity: 1,
height: '100px',
width: '320px',
})
}),
),
state('hide',
state(
'hide',
style({
opacity: 0,
height: '0px',
width: '0px',
})
),
transition(
'* => show',
animate(
'500ms ease-in'
)
}),
),
transition('* => show', animate('500ms ease-in')),
]),
trigger('hideAuthContainer', [
state('hide',
state(
'hide',
style({
opacity: 0,
})
}),
),
transition(
'show => hide',
group([
query(
"@*",
animateChild(),
{ optional: true }
),
animate(
'250ms ease-out'
)
])
)
query('@*', animateChild(), { optional: true }),
animate('250ms ease-out'),
]),
]
),
]),
],
})
export class MyProfileComponent implements OnInit {
@Input()
state: boolean = false;
@@ -102,15 +105,20 @@ export class MyProfileComponent implements OnInit {
isShowErrorMessage = false;
_fileIcon = faFileUpload
_fileIcon = faFileUpload;
constructor(private authService: AuthService) {
}
constructor(private authService: AuthService) {}
ngOnInit(): void {
this.alterForm = new FormGroup({
'username': new FormControl(null, [Validators.required, ValidateNotEmptyValidator]),
'password': new FormControl(null, [Validators.required, ValidatePasswordValidator])
username: new FormControl(null, [
Validators.required,
ValidateNotEmptyValidator,
]),
password: new FormControl(null, [
Validators.required,
ValidatePasswordValidator,
]),
});
this.errorMessage = null;
}
@@ -121,30 +129,30 @@ export class MyProfileComponent implements OnInit {
showErrorMessage(): string {
if (this.isShowErrorMessage) {
return "show";
return 'show';
}
return "hide";
return 'hide';
}
hideErrorMessage(): string {
if (!!this.errorMessage) {
return "hide";
return 'hide';
}
return "show";
return 'show';
}
onDeleteAccount() {
this.authService.deleteAccount().subscribe({
next: (response: any) => {
this.authService.logout();
}
})
},
});
this.closePopup();
}
hideAuthContainer(event: any) {
if (event.toState === "hide") {
event.element.style.display = "none";
if (event.toState === 'hide') {
event.element.style.display = 'none';
this.isShowErrorMessage = true;
}
}
@@ -158,5 +166,4 @@ export class MyProfileComponent implements OnInit {
private closePopup() {
this.onStateChange(false);
}
}

View File

@@ -1,20 +1,27 @@
<div class="btn-container">
<div class="input-group mb-3">
<div class="custom-file">
<input type="file"
<input
type="file"
class="custom-file-input"
id="inputProfilePicture"
aria-describedby="inputProfilePicture"
(change)="handleFileInput($event)">
<label class="custom-file-label"
for="inputProfilePicture">
{{isProfilePictureSelected ? getFileName() : 'Profile Picture'}}
(change)="handleFileInput($event)"
/>
<label class="custom-file-label" for="inputProfilePicture">
{{
isProfilePictureSelected ? getFileName() : "Profile Picture"
}}
</label>
</div>
<div class="input-group-append">
<button class="btn btn-outline-secondary"
<button
class="btn btn-outline-secondary"
[disabled]="!isProfilePictureSelected"
(click)="uploadProfilePicture()">Upload</button>
(click)="uploadProfilePicture()"
>
Upload
</button>
</div>
</div>
</div>

View File

@@ -8,9 +8,8 @@ describe('ProfilePicturePickerComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ProfilePicturePickerComponent ]
})
.compileComponents();
declarations: [ProfilePicturePickerComponent],
}).compileComponents();
fixture = TestBed.createComponent(ProfilePicturePickerComponent);
component = fixture.componentInstance;

View File

@@ -1,19 +1,18 @@
import {Component, EventEmitter, Output} from '@angular/core';
import {AuthService} from "../../../../shared/auth/auth.service";
import { Component, EventEmitter, Output } from '@angular/core';
import { AuthService } from '../../../../shared/auth/auth.service';
@Component({
selector: 'app-profile-picture-picker',
templateUrl: './profile-picture-picker.component.html',
styleUrls: ['./profile-picture-picker.component.css']
styleUrls: ['./profile-picture-picker.component.css'],
})
export class ProfilePicturePickerComponent {
@Output()
imageSent = new EventEmitter<boolean>();
private profilePicture!: File;
constructor(private authService: AuthService) { }
constructor(private authService: AuthService) {}
handleFileInput(event: Event) {
const element = event.currentTarget as HTMLInputElement;
@@ -35,5 +34,4 @@ export class ProfilePicturePickerComponent {
get isProfilePictureSelected(): boolean {
return !!this.profilePicture;
}
}

View File

@@ -22,7 +22,7 @@
}
.auth-body .btn {
background-color: #D8291C !important;
background-color: #d8291c !important;
text-decoration: none;
border-radius: 8px;
color: #ffffff;
@@ -70,7 +70,7 @@
border-bottom: 2px solid #7676769b;
}
.input-div:after {
content: '';
content: "";
left: 0;
right: 0;
width: 0;
@@ -84,11 +84,12 @@
.input-div:hover:after,
.input-div:has(input.form-control:focus):after {
width: 100%;
transition: .4s;
transition: 0.4s;
overflow: hidden;
}
.input-div > .form-control, .input-div > .form-control:focus {
.input-div > .form-control,
.input-div > .form-control:focus {
border: none;
border-color: inherit;
-webkit-box-shadow: none;
@@ -101,7 +102,7 @@
font-size: 17px;
color: #767676;
}
.input-div >.form-control:-webkit-autofill {
.input-div > .form-control:-webkit-autofill {
-webkit-text-fill-color: #767676;
box-shadow: 0 0 0px 1000px #ffffff inset;
-webkit-box-shadow: 0 0 0px 1000px #ffffff inset;
@@ -111,17 +112,16 @@
.input-div:hover > .form-control::placeholder,
.input-div:hover > .input-div-icon,
.input-div:has(input.form-control:focus) > .input-div-icon {
color: #D8291C;
transition: .3s;
color: #d8291c;
transition: 0.3s;
}
.input-div:hover > .form-control::placeholder,
.input-div:focus > .form-control::placeholder{
.input-div:focus > .form-control::placeholder {
font-weight: 500;
transition: .3s;
transition: 0.3s;
}
@media (min-width:767px) {
@media (min-width: 767px) {
.authentication-container {
min-width: 630px;
}
@@ -151,5 +151,4 @@
border-right: 2px solid #80808076;
height: 100%;
}
}

View File

@@ -1,60 +1,81 @@
<app-popup [state]="state"
<app-popup
[state]="state"
(stateChange)="onStateChange($event)"
[ignoreClickOutside]="ignoreClickOutside">
<div class="container m-0 overflow-hidden"
[@resizeContainerForErrorMessage]="hideErrorMessage()">
<app-error-box [errorMessage]="errorMessage"
[@showErrorMessage]="showErrorMessage()">
[ignoreClickOutside]="ignoreClickOutside"
>
<div
class="container m-0 overflow-hidden"
[@resizeContainerForErrorMessage]="hideErrorMessage()"
>
<app-error-box
[errorMessage]="errorMessage"
[@showErrorMessage]="showErrorMessage()"
>
</app-error-box>
<div class="container authentication-container"
<div
class="container authentication-container"
[@hideAuthContainer]="hideErrorMessage()"
(@hideAuthContainer.done)="hideAuthContainer($event)">
(@hideAuthContainer.done)="hideAuthContainer($event)"
>
<div class="row">
<div class="col-lg-6 auth-body auth-body-form">
<form [formGroup]="signupForm" (ngSubmit)="onSignUp()">
<div class="input-div">
<fa-icon class="input-div-icon"
[icon]="_fullnameIcon">
<fa-icon
class="input-div-icon"
[icon]="_fullnameIcon"
>
</fa-icon>
<input type="text" id="fullname"
<input
type="text"
id="fullname"
formControlName="fullname"
class="form-control"
placeholder="Full Name">
placeholder="Full Name"
/>
</div>
<div class="input-div">
<fa-icon class="input-div-icon"
[icon]="_emailIcon">
<fa-icon class="input-div-icon" [icon]="_emailIcon">
</fa-icon>
<input type="text" id="email"
<input
type="text"
id="email"
formControlName="email"
class="form-control"
placeholder="Email">
placeholder="Email"
/>
</div>
<div class="input-div">
<fa-icon class="input-div-icon"
[icon]="_userIcon">
<fa-icon class="input-div-icon" [icon]="_userIcon">
</fa-icon>
<input type="text" id="username"
<input
type="text"
id="username"
formControlName="username"
class="form-control"
placeholder="Username">
placeholder="Username"
/>
</div>
<div class="input-div">
<fa-icon class="input-div-icon"
[icon]="_passwordIcon">
<fa-icon
class="input-div-icon"
[icon]="_passwordIcon"
>
</fa-icon>
<input type="password" id="password"
<input
type="password"
id="password"
formControlName="password"
class="form-control"
placeholder="Password">
placeholder="Password"
/>
</div>
<button class="btn"
<button
class="btn"
[disabled]="!signupForm.valid"
type="submit">
type="submit"
>
SignUp
</button>
</form>
@@ -63,24 +84,30 @@
<div class="line"></div>
</div>
<div class="col-lg-6 auth-body auth-body-links">
<button mat-button
<button
mat-button
class="oauth-button d-flex justify-content-center align-items-center"
(click)="onGoogleLogin()">
<mat-icon style="width: 50px; height:30px"
svgIcon="google-logo"></mat-icon>
(click)="onGoogleLogin()"
>
<mat-icon
style="width: 50px; height: 30px"
svgIcon="google-logo"
></mat-icon>
Login With Google
</button>
<button mat-button
<button
mat-button
class="oauth-button d-flex justify-content-center align-items-center"
(click)="onGithubLogin()">
<mat-icon style="width: 50px; height:30px"
svgIcon="github-logo"></mat-icon>
(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

@@ -8,9 +8,8 @@ describe('SignupComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ SignupComponent ]
})
.compileComponents();
declarations: [SignupComponent],
}).compileComponents();
fixture = TestBed.createComponent(SignupComponent);
component = fixture.componentInstance;

View File

@@ -2,21 +2,34 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { MatIconRegistry } from '@angular/material/icon';
import { DomSanitizer } from '@angular/platform-browser';
import { faEnvelope, faFingerprint, faLock, faUser } from '@fortawesome/free-solid-svg-icons';
import {
faEnvelope,
faFingerprint,
faLock,
faUser,
} from '@fortawesome/free-solid-svg-icons';
import { Subscription } from 'rxjs';
import { AuthService } from 'src/app/shared/auth/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, animateChild, group, query, state, style, transition, trigger} from "@angular/animations";
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 {
animate,
animateChild,
group,
query,
state,
style,
transition,
trigger,
} from '@angular/animations';
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';
const GOOGLE_LOGO_SVG = "assets/img/providers/google.svg";
const GITHUB_LOGO_SVG = "assets/img/providers/github.svg";
const GOOGLE_LOGO_SVG = 'assets/img/providers/google.svg';
const GITHUB_LOGO_SVG = 'assets/img/providers/github.svg';
@Component({
selector: 'app-signup',
@@ -24,70 +37,58 @@ const GITHUB_LOGO_SVG = "assets/img/providers/github.svg";
styleUrls: ['./signup.component.css'],
animations: [
trigger('resizeContainerForErrorMessage', [
state('hide',
state(
'hide',
style({
height: '100px',
width: '320px',
})
}),
),
transition(
'show => hide',
group([
query(
"@*",
animateChild(),
{ optional: true }
query('@*', animateChild(), { optional: true }),
animate('1s ease'),
]),
),
animate('1s ease')
])
)
]),
trigger('showErrorMessage', [
state('show',
state(
'show',
style({
opacity: 1,
height: '100px',
width: '320px',
})
}),
),
state('hide',
state(
'hide',
style({
opacity: 0,
height: '0px',
width: '0px',
})
),
transition(
'* => show',
animate(
'500ms ease-in'
)
}),
),
transition('* => show', animate('500ms ease-in')),
]),
trigger('hideAuthContainer', [
state('hide',
state(
'hide',
style({
opacity: 0,
})
}),
),
transition(
'show => hide',
group([
query(
"@*",
animateChild(),
{ optional: true }
),
animate(
'250ms ease-out'
)
])
)
query('@*', animateChild(), { optional: true }),
animate('250ms ease-out'),
]),
]
),
]),
],
})
export class SignupComponent implements OnInit {
@Input()
state: boolean = false;
@@ -113,34 +114,46 @@ export class SignupComponent implements OnInit {
_passwordIcon = faLock;
constructor(private authService: AuthService,
constructor(
private authService: AuthService,
private matIconRegistry: MatIconRegistry,
private domSanitizer: DomSanitizer) {
private domSanitizer: DomSanitizer,
) {
this.matIconRegistry.addSvgIcon(
"google-logo",
this.domSanitizer.bypassSecurityTrustResourceUrl(GOOGLE_LOGO_SVG)
'google-logo',
this.domSanitizer.bypassSecurityTrustResourceUrl(GOOGLE_LOGO_SVG),
);
this.matIconRegistry.addSvgIcon(
"github-logo",
this.domSanitizer.bypassSecurityTrustResourceUrl(GITHUB_LOGO_SVG)
'github-logo',
this.domSanitizer.bypassSecurityTrustResourceUrl(GITHUB_LOGO_SVG),
);
}
ngOnInit(): void {
this.signupForm = new FormGroup({
'fullname': new FormControl(null, [Validators.required, ValidateNotEmptyValidator]),
fullname: new FormControl(null, [
Validators.required,
ValidateNotEmptyValidator,
]),
// Create a Email Validator
'email': new FormControl(null, [Validators.required, ValidateEmailValidator]),
'username': new FormControl(null, [Validators.required, ValidateNotEmptyValidator]),
email: new FormControl(null, [
Validators.required,
ValidateEmailValidator,
]),
username: new FormControl(null, [
Validators.required,
ValidateNotEmptyValidator,
]),
// Create a Password Validator
'password': new FormControl(null, [Validators.required, ValidatePasswordValidator])
password: new FormControl(null, [
Validators.required,
ValidatePasswordValidator,
]),
});
this.errorMessage = null;
this.authSubject = this.authService.authSubject.subscribe(
res => {
this.authSubject = this.authService.authSubject.subscribe((res) => {
this.validateSignup(res);
}
);
});
}
onStateChange(state: boolean) {
@@ -152,8 +165,8 @@ export class SignupComponent implements OnInit {
name: this.signupForm.controls['fullname'].value,
email: this.signupForm.controls['email'].value,
username: this.signupForm.controls['username'].value,
password: this.signupForm.controls['password'].value
}
password: this.signupForm.controls['password'].value,
};
this.authService.signup(user);
}
@@ -167,11 +180,11 @@ export class SignupComponent implements OnInit {
private validateSignup(res: User | HttpError | null) {
if (res && UserChecker.test(res)) {
this.closePopup()
} if (HttpErrorChecker.test(res)) {
this.closePopup();
}
if (HttpErrorChecker.test(res)) {
this.errorMessage = (<HttpError>res).details;
}
}
private closePopup() {
@@ -181,24 +194,22 @@ export class SignupComponent implements OnInit {
public showErrorMessage(): string {
if (this.isShowErrorMessage) {
return "show";
return 'show';
}
return "hide";
return 'hide';
}
public hideErrorMessage(): string {
if (!!this.errorMessage) {
return "hide";
return 'hide';
}
return "show";
return 'show';
}
hideAuthContainer(event: any) {
if (event.toState === "hide") {
event.element.style.display = "none";
if (event.toState === 'hide') {
event.element.style.display = 'none';
this.isShowErrorMessage = true;
}
}
}

View File

@@ -1,8 +1,10 @@
<div class="slider"
appClickedOutside
[ignoreElementList]="ignoreClickOutside"
[clickOutsideStopWatching]="clickOutsideStopWatching"
(clickOutside)="closeNavSlider()"
[@slideState]="sliderStatus">
<div
class="slider"
appClickedOutside
[ignoreElementList]="ignoreClickOutside"
[clickOutsideStopWatching]="clickOutsideStopWatching"
(clickOutside)="closeNavSlider()"
[@slideState]="sliderStatus"
>
<ng-content></ng-content>
</div>

View File

@@ -8,9 +8,8 @@ describe('HeaderSliderComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ HeaderSliderComponent ]
})
.compileComponents();
declarations: [HeaderSliderComponent],
}).compileComponents();
fixture = TestBed.createComponent(HeaderSliderComponent);
component = fixture.componentInstance;

View File

@@ -1,45 +1,49 @@
import { animate, animateChild, group, query, state, style, transition, trigger } from '@angular/animations';
import {
animate,
animateChild,
group,
query,
state,
style,
transition,
trigger,
} from '@angular/animations';
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
@Component({
selector: 'app-header-slider',
templateUrl: './header-slider.component.html',
styleUrls: ['./header-slider.component.css'],
animations:[
animations: [
trigger('slideState', [
state('hide', style({
transform: 'translateX(100%)'
})),
state('show', style({
transform: 'translateX(0%)'
})),
transition(
'hide => show', [
group([
query(
"@*",
animateChild(),
{ optional: true }
state(
'hide',
style({
transform: 'translateX(100%)',
}),
),
animate('600ms ease-in')
])
state(
'show',
style({
transform: 'translateX(0%)',
}),
),
transition('hide => show', [
group([
query('@*', animateChild(), { optional: true }),
animate('600ms ease-in'),
]),
transition(
'show => hide', [
]),
transition('show => hide', [
group([
query(
"@*",
animateChild(),
{ optional: true }
),
animate('500ms ease-out')
])
])
])
]
query('@*', animateChild(), { optional: true }),
animate('500ms ease-out'),
]),
]),
]),
],
})
export class HeaderSliderComponent {
@Input()
ignoreClickOutside!: HTMLDivElement[];
@@ -52,7 +56,7 @@ export class HeaderSliderComponent {
@Output()
stateChange = new EventEmitter<boolean>();
constructor() { }
constructor() {}
get sliderStatus() {
return this.state ? 'show' : 'hide';
@@ -64,7 +68,6 @@ export class HeaderSliderComponent {
}
public changeState() {
this.stateChange.emit(this.state)
this.stateChange.emit(this.state);
}
}

View File

@@ -29,15 +29,14 @@
.nav-links li a {
text-decoration: none;
font-family: 'Montserrat';
font-family: "Montserrat";
font-weight: 400;
color: #ffffff;
font-size: 18px;
}
.nav-links li a:hover {
opacity: .8;
opacity: 0.8;
color: #f44336;
transition: 0.5s;
}

View File

@@ -1,9 +1,18 @@
<div class="links-container">
<div class="nav-links">
<ul>
<li *ngFor="let nav of navLink; let i=index"[@animateSliderItem]="{ value: itemStatus, params: { fadeInTime: .6 + i/20 , fadeOutTime: .6 - i/10 } }">
<li
*ngFor="let nav of navLink; let i = index"
[@animateSliderItem]="{
value: itemStatus,
params: {
fadeInTime: 0.6 + i / 20,
fadeOutTime: 0.6 - i / 10
}
}"
>
<a [routerLink]="nav.link">
{{nav.page}}
{{ nav.page }}
</a>
</li>
</ul>
@@ -11,18 +20,32 @@
</div>
<div class="profile-container">
<div class="profile"
[@animateSliderItem]="{ value: itemStatus, params: { fadeInTime: .6 + (navLink.length+1)/20 , fadeOutTime: .6 - (navLink.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)"
<div
class="profile"
[@animateSliderItem]="{
value: itemStatus,
params: {
fadeInTime: 0.6 + (navLink.length + 1) / 20,
fadeOutTime: 0.6 - (navLink.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/>
width="50"
height="50"
alt="Profile Picture"
priority
/>
</div>
</div>
</div>

View File

@@ -8,9 +8,8 @@ describe('NavSliderComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ NavSliderComponent ]
})
.compileComponents();
declarations: [NavSliderComponent],
}).compileComponents();
fixture = TestBed.createComponent(NavSliderComponent);
component = fixture.componentInstance;

View File

@@ -1,26 +1,34 @@
import {Component, EventEmitter, OnDestroy, OnInit, Output} from '@angular/core';
import {
Component,
EventEmitter,
OnDestroy,
OnInit,
Output,
} 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/auth/auth.service";
import {Subscription} from "rxjs";
import UserChecker from '../../../shared/model/user/user.checker';
import { User } from '../../../shared/model/user/user.model';
import { AuthService } from '../../../shared/auth/auth.service';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-nav-slider',
templateUrl: './nav-slider.component.html',
styleUrls: ['./nav-slider.component.css']
styleUrls: ['./nav-slider.component.css'],
})
export class NavSliderComponent extends SliderItemComponent implements OnInit, OnDestroy {
export class NavSliderComponent
extends SliderItemComponent
implements OnInit, OnDestroy
{
userIcon = faUser;
navLink = [
{ page: "Home", link: "/home" },
{ page: "Work", link: "/home" },
{ page: "Contact", link: "/home" },
{ page: "About", link: "/home" }
]
{ page: 'Home', link: '/home' },
{ page: 'Work', link: '/home' },
{ page: 'Contact', link: '/home' },
{ page: 'About', link: '/home' },
];
loggedUser!: User | null;
@@ -35,14 +43,14 @@ export class NavSliderComponent extends SliderItemComponent implements OnInit, O
ngOnInit(): void {
this.userSubscription = this.authService.authSubject.subscribe(
res => {
(res) => {
if (res && UserChecker.test(res)) {
this.loggedUser = <User>res;
} else {
this.loggedUser = null;
}
}
)
},
);
}
ngOnDestroy(): void {
@@ -52,5 +60,4 @@ export class NavSliderComponent extends SliderItemComponent implements OnInit, O
onProfileButtonClicked() {
this.profileButtonClicked.emit();
}
}

View File

@@ -28,17 +28,16 @@
}
.user-options li a {
font-family: 'Montserrat';
font-family: "Montserrat";
text-decoration: none;
font-weight: 400;
color: #ffffff;
font-size: 18px;
}
.user-options li a:hover {
color: #f44336;
transition: 0.5s;
cursor: pointer;
opacity: .8;
opacity: 0.8;
}

View File

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

View File

@@ -8,9 +8,8 @@ describe('UserSliderComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ UserSliderComponent ]
})
.compileComponents();
declarations: [UserSliderComponent],
}).compileComponents();
fixture = TestBed.createComponent(UserSliderComponent);
component = fixture.componentInstance;

View File

@@ -8,47 +8,46 @@ import { User } from 'src/app/shared/model/user/user.model';
@Component({
selector: 'app-user-slider',
templateUrl: './user-slider.component.html',
styleUrls: ['./user-slider.component.css']
styleUrls: ['./user-slider.component.css'],
})
export class UserSliderComponent extends SliderItemComponent implements OnInit {
userlessOptions = [
{
name: "Login",
name: 'Login',
onClick: () => {
this.onLoginOptionClicked();
}
},
},
{
name: "Signup",
name: 'Signup',
onClick: () => {
this.onSignUpOptionClick();
}
}
]
},
},
];
userOptions = [
{
name: "My Profile",
name: 'My Profile',
onClick: () => {
this.onMyProfileClicked()
}
this.onMyProfileClicked();
},
},
{
name: "Help",
name: 'Help',
onClick: () => {
this.onHelpClicked();
}
},
},
{
name: "Logout",
name: 'Logout',
onClick: () => {
this.onLogout();
}
}
]
},
},
];
user!: User|null;
user!: User | null;
authSubscription!: Subscription;
@@ -69,16 +68,15 @@ export class UserSliderComponent extends SliderItemComponent implements OnInit {
}
ngOnInit() {
this.authSubscription =
this.authService.authSubject.subscribe(
res => {
this.authSubscription = this.authService.authSubject.subscribe(
(res) => {
if (UserChecker.test(res)) {
this.user = <User>res;
} else {
this.user = null;
}
}
)
},
);
}
onLoginOptionClicked(): void {
@@ -100,5 +98,4 @@ export class UserSliderComponent extends SliderItemComponent implements OnInit {
onLogout() {
this.authService.logout();
}
}

View File

@@ -48,20 +48,20 @@
height: 5px;
background: #ffffff;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(255, 101, 47, .2);
transition: all .5s ease-in-out;
box-shadow: 0 2px 5px rgba(255, 101, 47, 0.2);
transition: all 0.5s ease-in-out;
}
.burger-menu::before,
.burger-menu::after {
content: '';
content: "";
position: absolute;
width: 40px;
height: 5px;
background: #ffffff;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(255, 101, 47, .2);
transition: all .5s ease-in-out;
box-shadow: 0 2px 5px rgba(255, 101, 47, 0.2);
transition: all 0.5s ease-in-out;
}
.burger-menu::before {
@@ -75,17 +75,17 @@
.burger-menu.open {
background: transparent;
box-shadow: none;
transition: all .5s ease-in-out;
transition: all 0.5s ease-in-out;
}
.burger-menu.open::before {
transform: rotate(45deg);
transition: all .5s ease-in-out;
transition: all 0.5s ease-in-out;
}
.burger-menu.open::after {
transform: rotate(-45deg);
transition: all .5s ease-in-out;
transition: all 0.5s ease-in-out;
}
.profile {
@@ -99,7 +99,6 @@ app-header-slider {
/* ====================== COMPUTER MEDIA FORMAT ======================== */
@media only screen and (min-width: 712px) {
.nav-links {
all: unset;
width: 50%;
@@ -121,7 +120,7 @@ app-header-slider {
}
.link-container li a {
font-family: 'Montserrat', sans-serif;
font-family: "Montserrat", sans-serif;
text-decoration: none;
letter-spacing: 3px;
color: #ffffff;
@@ -180,5 +179,4 @@ app-header-slider {
app-header-slider {
opacity: 0;
}
}

View File

@@ -2,7 +2,7 @@
<div class="main" #header>
<div class="logo">
<a routerLink="">
<img src="assets/img/logohideyoshi-white.png" alt="">
<img src="assets/img/logohideyoshi-white.png" alt="" />
</a>
</div>
<div class="nav-links">
@@ -15,14 +15,25 @@
</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)"
<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/>
width="50"
height="50"
alt="Profile Picture"
priority
/>
</div>
<app-header-dropdown
@@ -33,22 +44,27 @@
(loginPopupState)="loginPopupStateChange($event)"
(signupPopupState)="signupPopupStateChange($event)"
(myProfilePopupState)="myProfilePopupStateChange($event)"
(helpPopupState)="helpPopupStateChange($event)">
(helpPopupState)="helpPopupStateChange($event)"
>
</app-header-dropdown>
</div>
<div class="burger-container" (click)="toogleNavSlider()">
<div class="burger-menu" [ngClass]="{'open' : navSliderStatus}">
</div>
<div
class="burger-menu"
[ngClass]="{ open: navSliderStatus }"
></div>
</div>
</div>
<div #nav>
<app-header-slider
[(state)]="navSliderStatus"
[clickOutsideStopWatching]="userSliderStatus"
[ignoreClickOutside]="[header, user]">
[ignoreClickOutside]="[header, user]"
>
<app-nav-slider
[state]="navSliderStatus"
(profileButtonClicked)="profileButtonClicked()">
(profileButtonClicked)="profileButtonClicked()"
>
</app-nav-slider>
</app-header-slider>
</div>
@@ -56,13 +72,15 @@
<div #user>
<app-header-slider
[(state)]="userSliderStatus"
[ignoreClickOutside]="[header, nav]">
[ignoreClickOutside]="[header, nav]"
>
<app-user-slider
[state]="userSliderStatus"
(loginPopupState)="loginPopupStateChange($event)"
(signupPopupState)="signupPopupStateChange($event)"
(myProfilePopupState)="myProfilePopupStateChange($event)"
(helpPopupState)="helpPopupStateChange($event)">
(helpPopupState)="helpPopupStateChange($event)"
>
</app-user-slider>
</app-header-slider>
</div>

View File

@@ -8,9 +8,8 @@ describe('HeaderComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ HeaderComponent ]
})
.compileComponents();
declarations: [HeaderComponent],
}).compileComponents();
fixture = TestBed.createComponent(HeaderComponent);
component = fixture.componentInstance;

View File

@@ -1,21 +1,28 @@
import {Component, ComponentRef, ElementRef, OnDestroy, OnInit, ViewChild, ViewContainerRef} from '@angular/core';
import {
Component,
ComponentRef,
ElementRef,
OnDestroy,
OnInit,
ViewChild,
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/auth/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 { AuthService } from '../shared/auth/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';
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.css']
styleUrls: ['./header.component.css'],
})
export class HeaderComponent implements OnInit, OnDestroy {
userIcon = faUser;
profileDropdownState: boolean = false;
@@ -46,25 +53,27 @@ export class HeaderComponent implements OnInit, OnDestroy {
private helpComponent!: ComponentRef<HelpComponent>;
constructor(private viewContainerRef: ViewContainerRef, private authService: AuthService) { }
constructor(
private viewContainerRef: ViewContainerRef,
private authService: AuthService,
) {}
ngOnInit(): void {
this.userSubscription = this.authService.authSubject.subscribe(
res => {
(res) => {
if (res && UserChecker.test(res)) {
this.loggedUser = <User>res;
} else {
this.loggedUser = null;
}
}
)
},
);
}
ngOnDestroy(): void {
this.userSubscription.unsubscribe();
}
public toogleProfileDropdown(): void {
this.profileDropdownState = !this.profileDropdownState;
}
@@ -121,22 +130,21 @@ export class HeaderComponent implements OnInit, OnDestroy {
}
private createLoginPopup(): void {
this.loginComponent = this.viewContainerRef.createComponent(LoginComponent);
this.loginComponent =
this.viewContainerRef.createComponent(LoginComponent);
this.loginComponent.instance.state = true;
this.loginComponent.instance.ignoreClickOutside = [
this.profileBtnElementRef,
this.profileDropdownElementRef,
this.userElementRef
].map(element => element.nativeElement);
this.userElementRef,
].map((element) => element.nativeElement);
this.loginComponent.instance.stateChange.subscribe(
state => {
this.loginComponent.instance.stateChange.subscribe((state) => {
if (!state) {
this.closeLoginPopup()
this.closeLoginPopup();
}
}
);
});
this.navSliderStatus = false;
this.userSliderStatus = false;
@@ -144,22 +152,21 @@ export class HeaderComponent implements OnInit, OnDestroy {
}
private createSignupPopup() {
this.signupComponent = this.viewContainerRef.createComponent(SignupComponent);
this.signupComponent =
this.viewContainerRef.createComponent(SignupComponent);
this.signupComponent.instance.state = true;
this.signupComponent.instance.ignoreClickOutside = [
this.profileBtnElementRef,
this.profileDropdownElementRef,
this.userElementRef
].map(element => element.nativeElement);
this.userElementRef,
].map((element) => element.nativeElement);
this.signupComponent.instance.stateChange.subscribe(
state => {
this.signupComponent.instance.stateChange.subscribe((state) => {
if (!state) {
this.closeSignupPopup()
this.closeSignupPopup();
}
}
);
});
this.navSliderStatus = false;
this.userSliderStatus = false;
@@ -167,17 +174,16 @@ export class HeaderComponent implements OnInit, OnDestroy {
}
private createMyProfilePopup() {
this.myProfileComponent = this.viewContainerRef.createComponent(MyProfileComponent);
this.myProfileComponent =
this.viewContainerRef.createComponent(MyProfileComponent);
this.myProfileComponent.instance.state = true;
this.myProfileComponent.instance.user = this.loggedUser;
this.myProfileComponent.instance.stateChange.subscribe(
state => {
this.myProfileComponent.instance.stateChange.subscribe((state) => {
if (!state) {
this.closeMyProfilePopup()
this.closeMyProfilePopup();
}
}
);
});
this.navSliderStatus = false;
this.userSliderStatus = false;
@@ -185,16 +191,15 @@ export class HeaderComponent implements OnInit, OnDestroy {
}
private createHelpPopup() {
this.helpComponent = this.viewContainerRef.createComponent(HelpComponent);
this.helpComponent =
this.viewContainerRef.createComponent(HelpComponent);
this.helpComponent.instance.state = true;
this.helpComponent.instance.stateChange.subscribe(
state => {
this.helpComponent.instance.stateChange.subscribe((state) => {
if (!state) {
this.closeHelpPopup()
this.closeHelpPopup();
}
}
);
});
this.navSliderStatus = false;
this.userSliderStatus = false;

View File

@@ -1,6 +1,6 @@
import { NgModule } from '@angular/core';
import {CommonModule, NgOptimizedImage} from '@angular/common';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
import { CommonModule, NgOptimizedImage } from '@angular/common';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { HeaderComponent } from './header.component';
import { HeaderSliderComponent } from './header-slider/header-slider.component';
@@ -14,14 +14,12 @@ import { LoginComponent } from './header-popup/login/login.component';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { SignupComponent } from './header-popup/signup/signup.component';
import { CallbackComponent } from './header-popup/callback/callback.component';
import {MatIconModule} from '@angular/material/icon';
import { MatIconModule } from '@angular/material/icon';
import { ErrorBoxComponent } from './header-popup/error-box/error-box.component';
import { HelpComponent } from './header-popup/help/help.component';
import { MyProfileComponent } from './header-popup/my-profile/my-profile.component';
import { ProfilePicturePickerComponent } from './header-popup/my-profile/profile-picture-picker/profile-picture-picker.component';
@NgModule({
declarations: [
HeaderComponent,
@@ -46,12 +44,13 @@ import { ProfilePicturePickerComponent } from './header-popup/my-profile/profile
ReactiveFormsModule,
MatIconModule,
SharedModule,
NgOptimizedImage
], exports: [
NgOptimizedImage,
],
exports: [
HeaderComponent,
HeaderSliderComponent,
NavSliderComponent,
UserSliderComponent
]
UserSliderComponent,
],
})
export class HeaderModule { }
export class HeaderModule {}

View File

@@ -8,9 +8,8 @@ describe('HomeComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ HomeComponent ]
})
.compileComponents();
declarations: [HomeComponent],
}).compileComponents();
fixture = TestBed.createComponent(HomeComponent);
component = fixture.componentInstance;

View File

@@ -3,10 +3,8 @@ import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
styleUrls: ['./home.component.css'],
})
export class HomeComponent {
constructor() { }
constructor() {}
}

View File

@@ -1,17 +1,25 @@
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import {first, firstValueFrom, map, Observable, of, Subject, take, tap} from 'rxjs';
import {
first,
firstValueFrom,
map,
Observable,
of,
Subject,
take,
tap,
} from 'rxjs';
import { catchError } from 'rxjs/operators';
import { environment } from 'src/environments/environment';
import { HttpError } from '../model/httpError/httpError.model';
import { User } from '../model/user/user.model';
import * as http from "http";
import * as http from 'http';
@Injectable({
providedIn: 'root'
providedIn: 'root',
})
export class AuthService {
private userAuthenticated!: User;
authSubject = new Subject<User | HttpError | null>();
@@ -20,30 +28,35 @@ export class AuthService {
readonly BACKEND_OAUTH_PATH = environment.backendOAuthPath;
constructor(private http: HttpClient) { }
constructor(private http: HttpClient) {}
login(userAuthAtempt: User): void {
this.validateUser(this.loginUser(userAuthAtempt));
}
googleLogin() {
window.open(this.BACKEND_OAUTH_PATH + '/oauth2/authorization/google', '_self');
window.open(
this.BACKEND_OAUTH_PATH + '/oauth2/authorization/google',
'_self',
);
}
githubLogin() {
window.open(this.BACKEND_OAUTH_PATH + '/oauth2/authorization/github', '_self');
window.open(
this.BACKEND_OAUTH_PATH + '/oauth2/authorization/github',
'_self',
);
}
loginGoogleUser(p: any): void {
this.validateUser(this.fetchGoogleOAuthToken(p))
this.validateUser(this.fetchGoogleOAuthToken(p));
}
loginGithubUser(p: any): void {
this.validateUser(this.fetchGithubOAuthToken(p))
this.validateUser(this.fetchGithubOAuthToken(p));
}
signup(userAuthAtempt: User): void {
this.validateUser(this.createUser(userAuthAtempt));
}
refresh(): void {
@@ -56,7 +69,7 @@ export class AuthService {
logout() {
this.authSubject.next(null);
this.destroySessions().subscribe()
this.destroySessions().subscribe();
}
deleteAccount() {
@@ -66,7 +79,7 @@ export class AuthService {
addProfilePicture(file: File): void {
const fileType = file.type.split('/')[1];
this.getAddProfilePictureUrl(fileType).subscribe({
next: (url: string|null) => {
next: (url: string | null) => {
if (url != null) {
this.uploadProfilePicture(url, file).then(
(response: Observable<any>) => {
@@ -75,169 +88,160 @@ export class AuthService {
this.processProfilePicture().subscribe(
() => {
this.refresh();
}
},
);
},
});
},
);
}
},
});
}
);
}
}
})
}
private loginUser(userAuthAtempt: User): Observable<User|any> {
private loginUser(userAuthAtempt: User): Observable<User | any> {
let loginParams = new URLSearchParams();
loginParams.set("username", userAuthAtempt.username!);
loginParams.set("password", userAuthAtempt.password!);
loginParams.set('username', userAuthAtempt.username!);
loginParams.set('password', userAuthAtempt.password!);
let headers = new HttpHeaders({
'Content-Type': 'application/x-www-form-urlencoded'
'Content-Type': 'application/x-www-form-urlencoded',
});
return this.http.post<User>(
this.BACKEND_PATH + "/user/login",
loginParams,
{ headers: headers, withCredentials: true }
).pipe(
first()
)
}
private fetchGoogleOAuthToken(p: any): Observable<User|any> {
let params = new HttpParams(
{
fromObject: p
}
);
return this.http.get<User>(
this.BACKEND_OAUTH_PATH + '/login/oauth2/code/google',
{
return this.http
.post<User>(this.BACKEND_PATH + '/user/login', loginParams, {
headers: headers,
withCredentials: true,
params: params
},
).pipe(
first()
);
})
.pipe(first());
}
private fetchGithubOAuthToken(p: any): Observable<User|any> {
private fetchGoogleOAuthToken(p: any): Observable<User | any> {
let params = new HttpParams({
fromObject: p,
});
let params = new HttpParams(
{
fromObject: p
}
);
return this.http.get<User>(
this.BACKEND_OAUTH_PATH + '/login/oauth2/code/github',
{
return this.http
.get<User>(this.BACKEND_OAUTH_PATH + '/login/oauth2/code/google', {
withCredentials: true,
params: params
},
).pipe(
first()
);
params: params,
})
.pipe(first());
}
private fetchGithubOAuthToken(p: any): Observable<User | any> {
let params = new HttpParams({
fromObject: p,
});
return this.http
.get<User>(this.BACKEND_OAUTH_PATH + '/login/oauth2/code/github', {
withCredentials: true,
params: params,
})
.pipe(first());
}
private createUser(newUser: User) {
return this.http.post<User>(
this.BACKEND_PATH + "/user/signup",
newUser,
{ withCredentials: true }
).pipe(
first()
)
return this.http
.post<User>(this.BACKEND_PATH + '/user/signup', newUser, {
withCredentials: true,
})
.pipe(first());
}
private refreshAccessToken() {
return this.http.post<User>(
this.BACKEND_PATH + "/user/login/refresh",
this.BACKEND_PATH + '/user/login/refresh',
this.userAuthenticated.refreshToken,
{ withCredentials: true }
{ withCredentials: true },
);
}
private validateSession(): Observable<User> {
return this.http.get<User>(
this.BACKEND_PATH + '/session/validate',
{ withCredentials: true }
);
return this.http.get<User>(this.BACKEND_PATH + '/session/validate', {
withCredentials: true,
});
}
private destroySessions() {
return this.http.delete(
this.BACKEND_PATH + '/session/destroy',
{ withCredentials: true }
);
return this.http.delete(this.BACKEND_PATH + '/session/destroy', {
withCredentials: true,
});
}
private deleteAccountRequest() {
let headers = this.createAuthorizationHeader()
let headers = this.createAuthorizationHeader();
return this.http.delete(
this.BACKEND_PATH + `/user/delete`,
{ headers: headers, withCredentials: true }
);
return this.http.delete(this.BACKEND_PATH + `/user/delete`, {
headers: headers,
withCredentials: true,
});
}
private validateUser(userAuthAtempt: Observable<User>) {
userAuthAtempt.pipe(
catchError(error => {
userAuthAtempt
.pipe(
catchError((error) => {
if (error.status == 0) {
return of(<HttpError>{
title: "Service Unavailable",
title: 'Service Unavailable',
status: 500,
details: "Service Unavailable, please try again later.",
developerMessage: "Service Unavailable, please try again later.",
timestamp: new Date().toISOString()
details:
'Service Unavailable, please try again later.',
developerMessage:
'Service Unavailable, please try again later.',
timestamp: new Date().toISOString(),
});
}
return of(<HttpError>error.error);
}),
first()
).subscribe({
next: userAuthentication => {
first(),
)
.subscribe({
next: (userAuthentication) => {
this.userAuthenticated = <User>userAuthentication;
this.authSubject.next(this.userAuthenticated);
}
},
});
}
private getAddProfilePictureUrl(fileType: string): Observable<string|null> {
return this.http.post<{ presigned_url: string, file_key: string }>(
this.BACKEND_PATH + '/user/profile-picture?fileType=' + fileType,
private getAddProfilePictureUrl(
fileType: string,
): Observable<string | null> {
return this.http
.post<{ presigned_url: string; file_key: string }>(
this.BACKEND_PATH +
'/user/profile-picture?fileType=' +
fileType,
null,
{
headers: this.createAuthorizationHeader(),
withCredentials: true
}
).pipe(
withCredentials: true,
},
)
.pipe(
first(),
map((res) => {
if (!!res && !!res.presigned_url) {
return res.presigned_url;
}
return null
})
)
return null;
}),
);
}
private async uploadProfilePicture(url: string, file: File): Promise<Observable<any>> {
private async uploadProfilePicture(
url: string,
file: File,
): Promise<Observable<any>> {
const fileData = await this.readAsArrayBuffer(file);
let headers = new HttpHeaders({
'Content-Type': file.type
})
return this.http.put(
url,
fileData,
{
'Content-Type': file.type,
});
return this.http.put(url, fileData, {
headers: headers,
}
);
});
}
private processProfilePicture() {
@@ -246,21 +250,22 @@ export class AuthService {
null,
{
headers: this.createAuthorizationHeader(),
withCredentials: true
}
)
withCredentials: true,
},
);
}
private createAuthorizationHeader(): HttpHeaders {
return new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.userAuthenticated.accessToken?.token
Authorization:
'Bearer ' + this.userAuthenticated.accessToken?.token,
});
}
private async readAsArrayBuffer(file: File): Promise<ArrayBuffer> {
const reader = new FileReader();
reader.readAsArrayBuffer(file)
reader.readAsArrayBuffer(file);
return new Promise<ArrayBuffer>((resolve, reject) => {
reader.onload = () => {
resolve(reader.result as ArrayBuffer);

View File

@@ -1,8 +1,8 @@
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700;800;900&display=swap');
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700;800;900&display=swap");
* {
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
font-family: "Poppins", sans-serif;
}
.popup-background {
@@ -51,7 +51,7 @@
background: #808080;
border-radius: 5px;
position: fixed;
content: '';
content: "";
width: 25px;
height: 5px;
}
@@ -73,11 +73,9 @@
height: 60px;
}
@media (min-width:767px) {
@media (min-width: 767px) {
.popup {
width: fit-content;
max-width: unset;
}
}

View File

@@ -1,11 +1,15 @@
<div #popup
class="popup-background"
[@popupState]="popupState"
(@popupState.done)="animationStop()">
<div class="popup"
<div
#popup
class="popup-background"
[@popupState]="popupState"
(@popupState.done)="animationStop()"
>
<div
class="popup"
appClickedOutside
(clickOutside)="closePopup()"
[ignoreElementList]="ignoreClickOutside">
[ignoreElementList]="ignoreClickOutside"
>
<div class="popup-header">
<div class="popup-close-btn" (click)="closePopup()"></div>
</div>

View File

@@ -8,9 +8,8 @@ describe('PopupComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ PopupComponent ]
})
.compileComponents();
declarations: [PopupComponent],
}).compileComponents();
fixture = TestBed.createComponent(PopupComponent);
component = fixture.componentInstance;

View File

@@ -1,5 +1,21 @@
import { animate, animateChild, group, query, state, style, transition, trigger } from '@angular/animations';
import { Component, ElementRef, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core';
import {
animate,
animateChild,
group,
query,
state,
style,
transition,
trigger,
} from '@angular/animations';
import {
Component,
ElementRef,
EventEmitter,
Input,
Output,
ViewEncapsulation,
} from '@angular/core';
@Component({
selector: 'app-popup',
@@ -7,39 +23,36 @@ import { Component, ElementRef, EventEmitter, Input, Output, ViewEncapsulation }
styleUrls: ['./popup.component.css'],
animations: [
trigger('popupState', [
state('hide', style({
'opacity': '0'
})),
state('show', style({
'opacity': '1'
})),
state(
'hide',
style({
opacity: '0',
}),
),
state(
'show',
style({
opacity: '1',
}),
),
transition(
'* => show',
group([
query(
"@*",
animateChild(),
{ optional: true }
),
animate('250ms ease-in')
])
query('@*', animateChild(), { optional: true }),
animate('250ms ease-in'),
]),
),
transition(
'show => hide',
group([
query(
"@*",
animateChild(),
{ optional: true }
query('@*', animateChild(), { optional: true }),
animate('250ms ease-out'),
]),
),
animate('250ms ease-out')
])
)
])
]
]),
],
})
export class PopupComponent {
@Input()
state: boolean = false;
@@ -49,7 +62,7 @@ export class PopupComponent {
@Output()
stateChange = new EventEmitter<boolean>(false);
constructor() { }
constructor() {}
get popupState(): string {
return this.state ? 'show' : 'hide';
@@ -57,7 +70,7 @@ export class PopupComponent {
animationStop() {
if (!this.state) {
this.closePopup()
this.closePopup();
this.stateChange.emit(false);
}
}
@@ -65,5 +78,4 @@ export class PopupComponent {
closePopup(): void {
this.state = false;
}
}

View File

@@ -8,9 +8,8 @@ describe('SliderItemComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ SliderItemComponent ]
})
.compileComponents();
declarations: [SliderItemComponent],
}).compileComponents();
fixture = TestBed.createComponent(SliderItemComponent);
component = fixture.componentInstance;

View File

@@ -1,46 +1,56 @@
import { animate, state, style, transition, trigger } from '@angular/animations';
import {
animate,
state,
style,
transition,
trigger,
} from '@angular/animations';
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
@Component({
selector: 'app-slider-item',
templateUrl: './slider-item.component.html',
styleUrls: ['./slider-item.component.css'],
animations:[
trigger('animateSliderItem',[
state('hide', style({
'opacity': '0',
'transform': 'translateX(150px)'
animations: [
trigger('animateSliderItem', [
state(
'hide',
style({
opacity: '0',
transform: 'translateX(150px)',
}),
{
params: {
fadeInTime: 600,
fadeOutTime: 600
}
}),
state('show', style({
'opacity': '1',
'transform': 'translateX(0px)'
fadeOutTime: 600,
},
},
),
state(
'show',
style({
opacity: '1',
transform: 'translateX(0px)',
}),
{
params: {
fadeOutTime: 600,
fadeInTime: 600
fadeInTime: 600,
},
}),
},
),
transition('hide => show', animate(`{{ fadeInTime }}s ease-in`)),
transition('show => hide', animate(`{{ fadeOutTime }}s ease-out`))
])
]
transition('show => hide', animate(`{{ fadeOutTime }}s ease-out`)),
]),
],
})
export class SliderItemComponent {
@Input()
public state:boolean = false;
public state: boolean = false;
constructor() { }
constructor() {}
get itemStatus(): string {
return this.state ? 'show' : 'hide';
}
}

View File

@@ -1,44 +1,42 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {NgcCookieConsentConfig, NgcCookieConsentModule} from "ngx-cookieconsent";
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import {
NgcCookieConsentConfig,
NgcCookieConsentModule,
} from 'ngx-cookieconsent';
const cookieConfig: NgcCookieConsentConfig = {
"cookie": {
"domain": "tinesoft.github.io"
cookie: {
domain: 'tinesoft.github.io',
},
"position": "bottom-left",
"theme": "classic",
"palette": {
"popup": {
"background": "#4e4e4e",
"text": "#ffffff",
"link": "#ffffff"
position: 'bottom-left',
theme: 'classic',
palette: {
popup: {
background: '#4e4e4e',
text: '#ffffff',
link: '#ffffff',
},
"button": {
"background": "#fa2f22",
"text": "#ffffff",
"border": "transparent"
}
button: {
background: '#fa2f22',
text: '#ffffff',
border: 'transparent',
},
},
type: 'opt-in',
content: {
message:
'This website uses cookies to ensure you get the best experience on our website.',
dismiss: 'Got it!',
deny: 'Refuse cookies',
link: '',
href: '',
policy: 'Cookie Policy',
},
"type": "opt-in",
"content": {
"message": "This website uses cookies to ensure you get the best experience on our website.",
"dismiss": "Got it!",
"deny": "Refuse cookies",
"link": "",
"href": "",
"policy": "Cookie Policy"
}
};
@NgModule({
declarations: [],
imports: [
CommonModule,
NgcCookieConsentModule.forRoot(cookieConfig)
]
imports: [CommonModule, NgcCookieConsentModule.forRoot(cookieConfig)],
})
export class CookieConsentModule {
}
export class CookieConsentModule {}

View File

@@ -1,21 +1,20 @@
import {Injectable} from '@angular/core';
import {BehaviorSubject, Subject, Subscription} from "rxjs";
import {CookieService} from "ngx-cookie-service";
import { Injectable } from '@angular/core';
import { BehaviorSubject, Subject, Subscription } from 'rxjs';
import { CookieService } from 'ngx-cookie-service';
@Injectable({
providedIn: 'root'
providedIn: 'root',
})
export class CookieConsertService {
private storage: Storage;
cookieStatusChangeSubscription!: BehaviorSubject<boolean>
cookieStatusChangeSubscription!: BehaviorSubject<boolean>;
constructor(private cookieService: CookieService) {
this.storage = window.localStorage
this.storage = window.localStorage;
this.cookieStatusChangeSubscription = new BehaviorSubject<boolean>(
this.getCookieConsentStatusFromLocalStorage()
this.getCookieConsentStatusFromLocalStorage(),
);
}
@@ -48,5 +47,4 @@ export class CookieConsertService {
return status === 'true';
}
}

View File

@@ -1,12 +1,28 @@
import { DOCUMENT } from '@angular/common';
import { AfterViewInit, Directive, ElementRef, EventEmitter, Inject, Input, OnDestroy, Output, ViewChild } from '@angular/core';
import { combineLatest, combineLatestWith, filter, fromEvent, merge, Subscription } from 'rxjs';
import {
AfterViewInit,
Directive,
ElementRef,
EventEmitter,
Inject,
Input,
OnDestroy,
Output,
ViewChild,
} from '@angular/core';
import {
combineLatest,
combineLatestWith,
filter,
fromEvent,
merge,
Subscription,
} from 'rxjs';
@Directive({
selector: '[appClickedOutside]'
selector: '[appClickedOutside]',
})
export class ClickedOutsideDirective implements AfterViewInit, OnDestroy {
@Input()
ignoreElementList!: HTMLDivElement[];
@@ -23,27 +39,27 @@ export class ClickedOutsideDirective implements AfterViewInit, OnDestroy {
constructor(
private element: ElementRef,
@Inject(DOCUMENT) private document: Document
) { }
@Inject(DOCUMENT) private document: Document,
) {}
ngAfterViewInit(): void {
const clickListener$ = fromEvent(this.document, 'click');
this.eventListener = clickListener$.pipe(
this.eventListener = clickListener$
.pipe(
filter((click) => {
return (
(
this.isOutside(click.target as HTMLElement) ||
this.isInIncludedList(click.target as HTMLElement)
) &&
(this.isOutside(click.target as HTMLElement) ||
this.isInIncludedList(
click.target as HTMLElement,
)) &&
this.notInIgnoredList(click.target as HTMLElement)
);
})
). subscribe( () => {
}),
)
.subscribe(() => {
!this.clickOutsideStopWatching && this.clickOutside.emit();
});
}
ngOnDestroy(): void {
@@ -52,12 +68,14 @@ export class ClickedOutsideDirective implements AfterViewInit, OnDestroy {
private isOutside(elementToCheck: HTMLElement): boolean {
let status = true;
if (this.element.nativeElement === elementToCheck ||
this.element.nativeElement.contains(elementToCheck)) {
if (
this.element.nativeElement === elementToCheck ||
this.element.nativeElement.contains(elementToCheck)
) {
status = false;
}
console.log('isOutside', status)
console.log('isOutside', status);
return status;
}
@@ -68,26 +86,32 @@ export class ClickedOutsideDirective implements AfterViewInit, OnDestroy {
}
let validateIsIgnored = (ignoreElement: HTMLDivElement): boolean => {
return ignoreElement === elementToCheck
|| ignoreElement.contains(elementToCheck)
|| elementToCheck.contains(ignoreElement)
}
return (
ignoreElement === elementToCheck ||
ignoreElement.contains(elementToCheck) ||
elementToCheck.contains(ignoreElement)
);
};
return !this.ignoreElementList.some(validateIsIgnored)
return !this.ignoreElementList.some(validateIsIgnored);
}
private isInIncludedList(elementToCheck: HTMLElement): boolean {
if (!this.includeClickedOutside || this.includeClickedOutside.length === 0) {
if (
!this.includeClickedOutside ||
this.includeClickedOutside.length === 0
) {
return false;
}
let validateIsIncluded = (includedElement: HTMLDivElement): boolean => {
return includedElement === elementToCheck
|| includedElement.contains(elementToCheck)
|| elementToCheck.contains(includedElement)
}
return (
includedElement === elementToCheck ||
includedElement.contains(elementToCheck) ||
elementToCheck.contains(includedElement)
);
};
return !this.includeClickedOutside.some(validateIsIncluded)
return !this.includeClickedOutside.some(validateIsIncluded);
}
}

View File

@@ -1,15 +1,15 @@
/**
* This module was automatically generated by `ts-interface-builder`
*/
import * as t from "ts-interface-checker";
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",
title: 'string',
status: 'number',
details: 'string',
developerMessage: 'string',
timestamp: 'string',
});
const exportedTypeSuite: t.ITypeSuite = {

View File

@@ -1,5 +1,5 @@
import { createCheckers } from "ts-interface-checker";
import HttpError from "./httpError.model-ti";
import { createCheckers } from 'ts-interface-checker';
import HttpError from './httpError.model-ti';
const HttpErrorChecker = createCheckers(HttpError)['HttpError'];
export default HttpErrorChecker;

View File

@@ -1,12 +1,12 @@
/**
* This module was automatically generated by `ts-interface-builder`
*/
import * as t from "ts-interface-checker";
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"),
token: 'string',
expirationDate: t.union('string', 'number'),
});
const exportedTypeSuite: t.ITypeSuite = {

View File

@@ -1,4 +1,4 @@
export interface Token {
token: string,
expirationDate: string|number
token: string;
expirationDate: string | number;
}

View File

@@ -1,6 +1,6 @@
import { createCheckers } from "ts-interface-checker";
import User from "./user.model-ti";
import Token from "../token/token.model-ti";
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;

View File

@@ -1,19 +1,19 @@
/**
* This module was automatically generated by `ts-interface-builder`
*/
import * as t from "ts-interface-checker";
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")),
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 = {

View File

@@ -1,14 +1,14 @@
import { Token } from "../token/token.model";
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<string>,
id?: number;
name?: string;
email?: string;
username: string;
password?: string;
profilePictureUrl?: string;
accessToken?: Token;
refreshToken?: Token;
roles?: Array<string>;
validateAccessToken?: () => Token | undefined;
};
}

View File

@@ -1,26 +1,27 @@
import { Injectable } from '@angular/core';
import {SwUpdate} from "@angular/service-worker";
import {interval} from "rxjs";
import { SwUpdate } from '@angular/service-worker';
import { interval } from 'rxjs';
@Injectable({
providedIn: 'root'
providedIn: 'root',
})
export class UpdateService {
constructor(private swUpdate: SwUpdate) {
if (swUpdate.isEnabled) {
interval(6 * 60 * 60).subscribe(() => swUpdate.checkForUpdate()
.then(() => console.log('checking for updates')));
interval(6 * 60 * 60).subscribe(() =>
swUpdate
.checkForUpdate()
.then(() => console.log('checking for updates')),
);
}
}
public checkForUpdates(): void {
this.swUpdate.available.subscribe(event => this.promptUser());
this.swUpdate.available.subscribe((event) => this.promptUser());
}
private promptUser(): void {
console.log('updating to new version');
this.swUpdate.activateUpdate().then(() => document.location.reload());
}
}

View File

@@ -6,7 +6,7 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { HttpClientModule } 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";
import { CookieConsentModule } from './cookie-consent/cookie-consent.module';
@NgModule({
declarations: [
@@ -19,12 +19,8 @@ import {CookieConsentModule} from "./cookie-consent/cookie-consent.module";
HttpClientModule,
BrowserAnimationsModule,
FontAwesomeModule,
CookieConsentModule
CookieConsentModule,
],
exports: [
ClickedOutsideDirective,
SliderItemComponent,
PopupComponent
]
exports: [ClickedOutsideDirective, SliderItemComponent, PopupComponent],
})
export class SharedModule { }
export class SharedModule {}

View File

@@ -1,4 +1,4 @@
import {AbstractControl} from "@angular/forms";
import { AbstractControl } from '@angular/forms';
export function ValidateEmailValidator(control: AbstractControl) {
const email = control.value;

View File

@@ -1,4 +1,4 @@
import {AbstractControl} from "@angular/forms";
import { AbstractControl } from '@angular/forms';
export function ValidateNotEmptyValidator(control: AbstractControl) {
const value = control.value;

View File

@@ -1,8 +1,9 @@
import {AbstractControl} from "@angular/forms";
import { AbstractControl } from '@angular/forms';
export function ValidatePasswordValidator(control: AbstractControl) {
var password = control.value;
var passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/g;
var passwordRegex =
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/g;
var passwordValid = passwordRegex.test(password);
if (!passwordValid) {
return { invalidPassword: true };

View File

@@ -1,7 +1,7 @@
(function (window) {
window['env'] = window['env'] || {};
window["env"] = window["env"] || {};
// Environment variables
window['env']['BACKEND_URL'] = '${BACKEND_URL}';
window['env']['BACKEND_OAUTH_URL'] = '${BACKEND_OAUTH_URL}';
window["env"]["BACKEND_URL"] = "${BACKEND_URL}";
window["env"]["BACKEND_OAUTH_URL"] = "${BACKEND_OAUTH_URL}";
})(this);

View File

@@ -1,5 +1,5 @@
export const environment = {
production: true,
backendPath: (<any>window)['env']['BACKEND_URL'],
backendOAuthPath: (<any>window)['env']['BACKEND_OAUTH_URL']
backendOAuthPath: (<any>window)['env']['BACKEND_OAUTH_URL'],
};

View File

@@ -5,7 +5,7 @@
export const environment = {
production: false,
backendPath: 'http://localhost:8070',
backendOAuthPath: 'http://localhost:8070'
backendOAuthPath: 'http://localhost:8070',
};
/*

View File

@@ -1,71 +1,115 @@
@font-face {
font-family: 'Montserrat';
font-family: "Montserrat";
font-style: italic;
font-weight: 100;
src: url(assets/fonts/Montserrat/Montserrat-italic-100.woff) format('woff'), url(assets/fonts/Montserrat/Montserrat-italic-100.woff2) format('woff2'), url(assets/fonts/Montserrat/Montserrat-italic-100.ttf) format('truetype');
src:
url(assets/fonts/Montserrat/Montserrat-italic-100.woff) format("woff"),
url(assets/fonts/Montserrat/Montserrat-italic-100.woff2) format("woff2"),
url(assets/fonts/Montserrat/Montserrat-italic-100.ttf)
format("truetype");
}
@font-face {
font-family: 'Montserrat';
font-family: "Montserrat";
font-style: italic;
font-weight: 300;
src: url(assets/fonts/Montserrat/Montserrat-italic-300.woff) format('woff'), url(assets/fonts/Montserrat/Montserrat-italic-300.woff2) format('woff2'), url(assets/fonts/Montserrat/Montserrat-italic-300.ttf) format('truetype');
src:
url(assets/fonts/Montserrat/Montserrat-italic-300.woff) format("woff"),
url(assets/fonts/Montserrat/Montserrat-italic-300.woff2) format("woff2"),
url(assets/fonts/Montserrat/Montserrat-italic-300.ttf)
format("truetype");
}
@font-face {
font-family: 'Montserrat';
font-family: "Montserrat";
font-style: italic;
font-weight: 400;
src: url(assets/fonts/Montserrat/Montserrat-italic-400.woff) format('woff'), url(assets/fonts/Montserrat/Montserrat-italic-400.woff2) format('woff2'), url(assets/fonts/Montserrat/Montserrat-italic-400.ttf) format('truetype');
src:
url(assets/fonts/Montserrat/Montserrat-italic-400.woff) format("woff"),
url(assets/fonts/Montserrat/Montserrat-italic-400.woff2) format("woff2"),
url(assets/fonts/Montserrat/Montserrat-italic-400.ttf)
format("truetype");
}
@font-face {
font-family: 'Montserrat';
font-family: "Montserrat";
font-style: italic;
font-weight: 700;
src: url(assets/fonts/Montserrat/Montserrat-italic-700.woff) format('woff'), url(assets/fonts/Montserrat/Montserrat-italic-700.woff2) format('woff2'), url(assets/fonts/Montserrat/Montserrat-italic-700.ttf) format('truetype');
src:
url(assets/fonts/Montserrat/Montserrat-italic-700.woff) format("woff"),
url(assets/fonts/Montserrat/Montserrat-italic-700.woff2) format("woff2"),
url(assets/fonts/Montserrat/Montserrat-italic-700.ttf)
format("truetype");
}
@font-face {
font-family: 'Montserrat';
font-family: "Montserrat";
font-style: italic;
font-weight: 900;
src: url(assets/fonts/Montserrat/Montserrat-italic-900.woff) format('woff'), url(assets/fonts/Montserrat/Montserrat-italic-900.woff2) format('woff2'), url(assets/fonts/Montserrat/Montserrat-italic-900.ttf) format('truetype');
src:
url(assets/fonts/Montserrat/Montserrat-italic-900.woff) format("woff"),
url(assets/fonts/Montserrat/Montserrat-italic-900.woff2) format("woff2"),
url(assets/fonts/Montserrat/Montserrat-italic-900.ttf)
format("truetype");
}
@font-face {
font-family: 'Montserrat';
font-family: "Montserrat";
font-style: normal;
font-weight: 100;
src: url(assets/fonts/Montserrat/Montserrat-normal-100.woff) format('woff'), url(assets/fonts/Montserrat/Montserrat-normal-100.woff2) format('woff2'), url(assets/fonts/Montserrat/Montserrat-normal-100.ttf) format('truetype');
src:
url(assets/fonts/Montserrat/Montserrat-normal-100.woff) format("woff"),
url(assets/fonts/Montserrat/Montserrat-normal-100.woff2) format("woff2"),
url(assets/fonts/Montserrat/Montserrat-normal-100.ttf)
format("truetype");
}
@font-face {
font-family: 'Montserrat';
font-family: "Montserrat";
font-style: normal;
font-weight: 300;
src: url(assets/fonts/Montserrat/Montserrat-normal-300.woff) format('woff'), url(assets/fonts/Montserrat/Montserrat-normal-300.woff2) format('woff2'), url(assets/fonts/Montserrat/Montserrat-normal-300.ttf) format('truetype');
src:
url(assets/fonts/Montserrat/Montserrat-normal-300.woff) format("woff"),
url(assets/fonts/Montserrat/Montserrat-normal-300.woff2) format("woff2"),
url(assets/fonts/Montserrat/Montserrat-normal-300.ttf)
format("truetype");
}
@font-face {
font-family: 'Montserrat';
font-family: "Montserrat";
font-style: normal;
font-weight: 400;
src: url(assets/fonts/Montserrat/Montserrat-normal-400.eot);
src: local('Montserrat'), url(assets/fonts/Montserrat/Montserrat-normal-400.woff) format('woff'), url(assets/fonts/Montserrat/Montserrat-normal-400.woff2) format('woff2'), url(assets/fonts/Montserrat/Montserrat-normal-400.svg#Montserrat) format('svg'), url(assets/fonts/Montserrat/Montserrat-normal-400.eot?#iefix) format('embedded-opentype'), url(assets/fonts/Montserrat/Montserrat-normal-400.ttf) format('truetype');
src:
local("Montserrat"),
url(assets/fonts/Montserrat/Montserrat-normal-400.woff) format("woff"),
url(assets/fonts/Montserrat/Montserrat-normal-400.woff2) format("woff2"),
url(assets/fonts/Montserrat/Montserrat-normal-400.svg#Montserrat)
format("svg"),
url(assets/fonts/Montserrat/Montserrat-normal-400.eot?#iefix)
format("embedded-opentype"),
url(assets/fonts/Montserrat/Montserrat-normal-400.ttf)
format("truetype");
}
@font-face {
font-family: 'Montserrat';
font-family: "Montserrat";
font-style: normal;
font-weight: 700;
src: url(assets/fonts/Montserrat/Montserrat-normal-700.woff) format('woff'), url(assets/fonts/Montserrat/Montserrat-normal-700.woff2) format('woff2'), url(assets/fonts/Montserrat/Montserrat-normal-700.ttf) format('truetype');
src:
url(assets/fonts/Montserrat/Montserrat-normal-700.woff) format("woff"),
url(assets/fonts/Montserrat/Montserrat-normal-700.woff2) format("woff2"),
url(assets/fonts/Montserrat/Montserrat-normal-700.ttf)
format("truetype");
}
@font-face {
font-family: 'Montserrat';
font-family: "Montserrat";
font-style: normal;
font-weight: 900;
src: url(assets/fonts/Montserrat/Montserrat-normal-900.woff) format('woff'), url(assets/fonts/Montserrat/Montserrat-normal-900.woff2) format('woff2'), url(assets/fonts/Montserrat/Montserrat-normal-900.ttf) format('truetype');
src:
url(assets/fonts/Montserrat/Montserrat-normal-900.woff) format("woff"),
url(assets/fonts/Montserrat/Montserrat-normal-900.woff2) format("woff2"),
url(assets/fonts/Montserrat/Montserrat-normal-900.ttf)
format("truetype");
}

View File

@@ -1,31 +1,39 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<head>
<meta charset="utf-8" />
<title>Hideyoshi Portfolio</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="./assets/img/logohideyoshi-red.png">
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
rel="icon"
type="image/x-icon"
href="./assets/img/logohideyoshi-red.png"
/>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="apple-mobile-web-app-title" content="Hideyoshi" />
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="Hideyoshi">
<link rel="manifest" href="manifest.webmanifest">
<link rel="preconnect" href="https://hideyoshi-portfolio-dev.s3.amazonaws.com">
<link rel="manifest" href="manifest.webmanifest" />
<link
rel="preconnect"
href="https://hideyoshi-portfolio-dev.s3.amazonaws.com"
/>
<!-- Loading environment variables -->
<script src="./assets/env.js"></script>
</head>
</head>
<body>
<body>
<app-root></app-root>
<noscript>Please enable JavaScript to continue using this application.</noscript>
</body>
<noscript
>Please enable JavaScript to continue using this
application.</noscript
>
</body>
</html>

View File

@@ -8,5 +8,6 @@ if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch((err) => console.error(err));

View File

@@ -47,7 +47,6 @@
*/
import 'zone.js'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/

View File

@@ -1,11 +1,16 @@
/* You can add global styles to this file, and also import other style files */
@import 'font-montserrat.css';
@import "font-montserrat.css";
* {
padding: 0 auto;
margin: 0 auto;
}
h1, h2, h3, h4, h5, h6 {
font-family: 'Montserrat', sans-serif;
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: "Montserrat", sans-serif;
}

View File

@@ -4,7 +4,7 @@ import 'zone.js/testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
platformBrowserDynamicTesting,
} from '@angular/platform-browser-dynamic/testing';
// First, initialize the Angular testing environment.

View File

@@ -5,11 +5,6 @@
"outDir": "./out-tsc/app",
"types": ["node"]
},
"files": [
"src/main.ts",
"src/polyfills.ts"
],
"include": [
"src/**/*.d.ts"
]
"files": ["src/main.ts", "src/polyfills.ts"],
"include": ["src/**/*.d.ts"]
}

View File

@@ -18,10 +18,7 @@
"importHelpers": true,
"target": "ES2022",
"module": "es2020",
"lib": [
"es2020",
"dom"
],
"lib": ["es2020", "dom"],
"useDefineForClassFields": false
},
"angularCompilerOptions": {

View File

@@ -3,16 +3,8 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"jasmine"
]
"types": ["jasmine"]
},
"files": [
"src/test.ts",
"src/polyfills.ts"
],
"include": [
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
"files": ["src/test.ts", "src/polyfills.ts"],
"include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
}