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

View File

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

View File

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

View File

@@ -3,14 +3,14 @@
module.exports = function (config) { module.exports = function (config) {
config.set({ config.set({
basePath: '', basePath: "",
frameworks: ['jasmine', '@angular-devkit/build-angular'], frameworks: ["jasmine", "@angular-devkit/build-angular"],
plugins: [ plugins: [
require('karma-jasmine'), require("karma-jasmine"),
require('karma-chrome-launcher'), require("karma-chrome-launcher"),
require('karma-jasmine-html-reporter'), require("karma-jasmine-html-reporter"),
require('karma-coverage'), require("karma-coverage"),
require('@angular-devkit/build-angular/plugins/karma') require("@angular-devkit/build-angular/plugins/karma"),
], ],
client: { client: {
jasmine: { jasmine: {
@@ -19,26 +19,26 @@ module.exports = function (config) {
// for example, you can disable the random execution with `random: false` // for example, you can disable the random execution with `random: false`
// or set a specific seed with `seed: 4321` // 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: { jasmineHtmlReporter: {
suppressAll: true // removes the duplicated traces suppressAll: true, // removes the duplicated traces
}, },
coverageReporter: { coverageReporter: {
dir: require('path').join(__dirname, './coverage/frontend-hideyoshi.com'), dir: require("path").join(
subdir: '.', __dirname,
reporters: [ "./coverage/frontend-hideyoshi.com",
{ type: 'html' }, ),
{ type: 'text-summary' } subdir: ".",
] reporters: [{ type: "html" }, { type: "text-summary" }],
}, },
reporters: ['progress', 'kjhtml'], reporters: ["progress", "kjhtml"],
port: 9876, port: 9876,
colors: true, colors: true,
logLevel: config.LOG_INFO, logLevel: config.LOG_INFO,
autoWatch: true, autoWatch: true,
browsers: ['Chrome'], browsers: ["Chrome"],
singleRun: false, 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/eslint-plugin": "^5.59.2",
"@typescript-eslint/parser": "^5.59.2", "@typescript-eslint/parser": "^5.59.2",
"eslint": "^8.39.0", "eslint": "^8.39.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.1",
"jasmine-core": "~4.1.0", "jasmine-core": "~4.1.0",
"karma": "~6.3.0", "karma": "~6.3.0",
"karma-chrome-launcher": "~3.1.0", "karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.2.0", "karma-coverage": "~2.2.0",
"karma-jasmine": "~5.0.0", "karma-jasmine": "~5.0.0",
"karma-jasmine-html-reporter": "~1.7.0", "karma-jasmine-html-reporter": "~1.7.0",
"prettier": "^3.0.3",
"prettier-eslint": "^16.1.1",
"ts-interface-builder": "^0.3.3", "ts-interface-builder": "^0.3.3",
"typescript": "~4.9.5" "typescript": "~4.9.5"
} }
@@ -3251,6 +3255,18 @@
"node": ">=8" "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": { "node_modules/@jridgewell/gen-mapping": {
"version": "0.3.3", "version": "0.3.3",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
@@ -4515,6 +4531,56 @@
"node": ">=14" "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": { "node_modules/@schematics/angular": {
"version": "16.2.0", "version": "16.2.0",
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-16.2.0.tgz", "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": "^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": { "node_modules/@socket.io/component-emitter": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", "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==", "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==",
"dev": true "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": { "node_modules/big.js": {
"version": "5.2.2", "version": "5.2.2",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
@@ -6013,6 +6094,18 @@
"popper.js": "^1.16.1" "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": { "node_modules/brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -6112,6 +6205,21 @@
"semver": "^7.0.0" "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": { "node_modules/bytes": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@@ -6452,6 +6560,15 @@
"integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==",
"dev": true "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": { "node_modules/compressible": {
"version": "2.0.18", "version": "2.0.18",
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", "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==", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
"dev": true "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": { "node_modules/default-gateway": {
"version": "6.0.3", "version": "6.0.3",
"resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz",
@@ -7181,6 +7442,12 @@
"node": ">=8" "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": { "node_modules/dns-equal": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
@@ -7724,6 +7991,47 @@
"url": "https://opencollective.com/eslint" "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": { "node_modules/eslint-scope": {
"version": "7.2.2", "version": "7.2.2",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
@@ -8196,6 +8504,12 @@
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true "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": { "node_modules/fast-glob": {
"version": "3.3.1", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz",
@@ -8775,6 +9089,27 @@
"node": ">= 0.4.0" "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": { "node_modules/has-bigints": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
@@ -9561,6 +9896,39 @@
"node": ">=0.10.0" "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": { "node_modules/is-interactive": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
@@ -10708,6 +11076,84 @@
"node": ">=8.0" "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": { "node_modules/lru-cache": {
"version": "5.1.1", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -12634,6 +13080,171 @@
"node": ">= 0.8.0" "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": { "node_modules/pretty-bytes": {
"version": "5.6.0", "version": "5.6.0",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
@@ -12646,6 +13257,32 @@
"url": "https://github.com/sponsors/sindresorhus" "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": { "node_modules/proc-log": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz",
@@ -12800,6 +13437,12 @@
"node": ">= 0.8" "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": { "node_modules/read-package-json": {
"version": "6.0.4", "version": "6.0.4",
"resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-6.0.4.tgz", "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-6.0.4.tgz",
@@ -13036,6 +13679,12 @@
"node": ">=0.10.0" "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": { "node_modules/requires-port": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
@@ -13176,6 +13825,21 @@
"fsevents": "~2.3.2" "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": { "node_modules/run-async": {
"version": "2.4.1", "version": "2.4.1",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", "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==", "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
"dev": true "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": { "node_modules/tapable": {
"version": "2.2.1", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
@@ -14342,6 +15022,18 @@
"integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
"dev": true "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": { "node_modules/tmp": {
"version": "0.2.1", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
@@ -14428,6 +15120,18 @@
"tree-kill": "cli.js" "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": { "node_modules/ts-interface-builder": {
"version": "0.3.3", "version": "0.3.3",
"resolved": "https://registry.npmjs.org/ts-interface-builder/-/ts-interface-builder-0.3.3.tgz", "resolved": "https://registry.npmjs.org/ts-interface-builder/-/ts-interface-builder-0.3.3.tgz",
@@ -14699,6 +15403,15 @@
"node": ">= 0.8" "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": { "node_modules/update-browserslist-db": {
"version": "1.0.11", "version": "1.0.11",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz",
@@ -14871,6 +15584,30 @@
"node": ">=0.10.0" "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": { "node_modules/w3c-hr-time": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", "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/eslint-plugin": "^5.59.2",
"@typescript-eslint/parser": "^5.59.2", "@typescript-eslint/parser": "^5.59.2",
"eslint": "^8.39.0", "eslint": "^8.39.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.1",
"jasmine-core": "~4.1.0", "jasmine-core": "~4.1.0",
"karma": "~6.3.0", "karma": "~6.3.0",
"karma-chrome-launcher": "~3.1.0", "karma-chrome-launcher": "~3.1.0",
"karma-coverage": "~2.2.0", "karma-coverage": "~2.2.0",
"karma-jasmine": "~5.0.0", "karma-jasmine": "~5.0.0",
"karma-jasmine-html-reporter": "~1.7.0", "karma-jasmine-html-reporter": "~1.7.0",
"prettier": "^3.0.3",
"prettier-eslint": "^16.1.1",
"ts-interface-builder": "^0.3.3", "ts-interface-builder": "^0.3.3",
"typescript": "~4.9.5" "typescript": "~4.9.5"
} }

View File

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

View File

@@ -8,26 +8,21 @@ const routes: Routes = [
{ {
path: '', path: '',
redirectTo: '/home', redirectTo: '/home',
pathMatch: 'full' pathMatch: 'full',
}, },
{ {
path: 'home', path: 'home',
component: HomeComponent component: HomeComponent,
}, },
{ {
path: 'callback', path: 'callback',
component: CallbackComponent component: CallbackComponent,
} },
] ];
@NgModule({ @NgModule({
declarations: [], declarations: [],
imports: [ imports: [CommonModule, RouterModule.forRoot(routes)],
CommonModule, exports: [RouterModule],
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 { environment } from 'src/environments/environment';
import { ServiceWorkerModule } from '@angular/service-worker'; import { ServiceWorkerModule } from '@angular/service-worker';
@NgModule({ @NgModule({
declarations: [], declarations: [],
imports: [ imports: [
CommonModule, CommonModule,
ServiceWorkerModule.register('ngsw-worker.js', { ServiceWorkerModule.register('ngsw-worker.js', {
enabled: environment.production, enabled: environment.production,
registrationStrategy: 'registerWhenStable:30000' registrationStrategy: 'registerWhenStable:30000',
}) }),
], ],
exports: [ exports: [ServiceWorkerModule],
ServiceWorkerModule
]
}) })
export class AppServiceWorkerModule { } export class AppServiceWorkerModule {}

View File

@@ -4,9 +4,7 @@ import { AppComponent } from './app.component';
describe('AppComponent', () => { describe('AppComponent', () => {
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ declarations: [AppComponent],
AppComponent
],
}).compileComponents(); }).compileComponents();
}); });
@@ -26,6 +24,8 @@ describe('AppComponent', () => {
const fixture = TestBed.createComponent(AppComponent); const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges(); fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement; 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 { AuthService } from './shared/auth/auth.service';
import {UpdateService} from "./shared/service-worker/update.service"; import { UpdateService } from './shared/service-worker/update.service';
import { import {
NgcCookieConsentService, NgcCookieConsentService,
NgcInitializationErrorEvent, NgcInitializationErrorEvent,
NgcInitializingEvent, NgcInitializingEvent,
NgcNoCookieLawEvent, NgcStatusChangeEvent NgcNoCookieLawEvent,
} from "ngx-cookieconsent"; NgcStatusChangeEvent,
import {Subscription} from "rxjs"; } from 'ngx-cookieconsent';
import {CookieConsertService} from "./shared/cookie-consent/cookie-consert.service"; import { Subscription } from 'rxjs';
import { CookieConsertService } from './shared/cookie-consent/cookie-consert.service';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrls: ['./app.component.css'] styleUrls: ['./app.component.css'],
}) })
export class AppComponent implements OnInit { export class AppComponent implements OnInit {
title = 'frontend-hideyoshi.com'; title = 'frontend-hideyoshi.com';
cookieStatusChangeSubscription!: Subscription; cookieStatusChangeSubscription!: Subscription;
@@ -25,28 +25,30 @@ export class AppComponent implements OnInit {
private authService: AuthService, private authService: AuthService,
private ccService: NgcCookieConsentService, private ccService: NgcCookieConsentService,
private cookieConsentService: CookieConsertService, private cookieConsentService: CookieConsertService,
private serviceWorker: UpdateService) { private serviceWorker: UpdateService,
) {
this.serviceWorker.checkForUpdates(); this.serviceWorker.checkForUpdates();
} }
ngOnInit(): void { ngOnInit(): void {
this.authService.autoLogin(); this.authService.autoLogin();
let cookieConsentStatus = this.cookieConsentService.getCookieConsentStatusFromLocalStorage(); let cookieConsentStatus =
this.cookieConsentService.getCookieConsentStatusFromLocalStorage();
if (cookieConsentStatus) { if (cookieConsentStatus) {
this.ccService.destroy(); this.ccService.destroy();
} }
this.cookieStatusChangeSubscription = this.ccService.statusChange$.subscribe( this.cookieStatusChangeSubscription =
this.ccService.statusChange$.subscribe(
(event: NgcStatusChangeEvent) => { (event: NgcStatusChangeEvent) => {
if (event.status === 'allow') { if (event.status === 'allow') {
this.cookieConsentService.consent(); this.cookieConsentService.consent();
} else if (event.status === 'deny') { } else if (event.status === 'deny') {
this.cookieConsentService.decline(); this.cookieConsentService.decline();
} }
} },
); );
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,9 +6,12 @@
[ignoreElementList]="ignoreClickOutside" [ignoreElementList]="ignoreClickOutside"
[@dropdownState]="dropDownState" [@dropdownState]="dropDownState"
(@dropdownState.start)="$event.element.style.display = 'block'" (@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"> <div class="info">
<h3>{{ this.user ? this.user.username : 'User Account' }}</h3> <h3>{{ this.user ? this.user.username : "User Account" }}</h3>
</div> </div>
<div #management> <div #management>
<ul class="user-management" *ngIf="!this.user"> <ul class="user-management" *ngIf="!this.user">
@@ -34,13 +37,19 @@
</li> </li>
<li class="dropdown-item" (click)="onHelpClicked()"> <li class="dropdown-item" (click)="onHelpClicked()">
<div class="icon-box"> <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> </div>
<p>Help</p> <p>Help</p>
</li> </li>
<li class="dropdown-item" (click)="onLogout()"> <li class="dropdown-item" (click)="onLogout()">
<div class="icon-box"> <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> </div>
<p>Logout</p> <p>Logout</p>
</li> </li>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,9 +8,8 @@ describe('HelpComponent', () => {
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ HelpComponent ] declarations: [HelpComponent],
}) }).compileComponents();
.compileComponents();
fixture = TestBed.createComponent(HelpComponent); fixture = TestBed.createComponent(HelpComponent);
component = fixture.componentInstance; 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({ @Component({
selector: 'app-help', selector: 'app-help',
templateUrl: './help.component.html', templateUrl: './help.component.html',
styleUrls: ['./help.component.css'] styleUrls: ['./help.component.css'],
}) })
export class HelpComponent { export class HelpComponent {
@Input() @Input()
state: boolean = false; state: boolean = false;
@@ -16,7 +15,7 @@ export class HelpComponent {
@Output() @Output()
stateChange = new EventEmitter<boolean>(); stateChange = new EventEmitter<boolean>();
constructor() { } constructor() {}
onStateChange(state: boolean) { onStateChange(state: boolean) {
this.stateChange.emit(state); this.stateChange.emit(state);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,9 +8,8 @@ describe('HeaderSliderComponent', () => {
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ HeaderSliderComponent ] declarations: [HeaderSliderComponent],
}) }).compileComponents();
.compileComponents();
fixture = TestBed.createComponent(HeaderSliderComponent); fixture = TestBed.createComponent(HeaderSliderComponent);
component = fixture.componentInstance; 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'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
@Component({ @Component({
selector: 'app-header-slider', selector: 'app-header-slider',
templateUrl: './header-slider.component.html', templateUrl: './header-slider.component.html',
styleUrls: ['./header-slider.component.css'], styleUrls: ['./header-slider.component.css'],
animations:[ animations: [
trigger('slideState', [ trigger('slideState', [
state('hide', style({ state(
transform: 'translateX(100%)' 'hide',
})), style({
state('show', style({ transform: 'translateX(100%)',
transform: 'translateX(0%)' }),
})),
transition(
'hide => show', [
group([
query(
"@*",
animateChild(),
{ optional: true }
), ),
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([ group([
query( query('@*', animateChild(), { optional: true }),
"@*", animate('500ms ease-out'),
animateChild(), ]),
{ optional: true } ]),
), ]),
animate('500ms ease-out') ],
])
])
])
]
}) })
export class HeaderSliderComponent { export class HeaderSliderComponent {
@Input() @Input()
ignoreClickOutside!: HTMLDivElement[]; ignoreClickOutside!: HTMLDivElement[];
@@ -52,7 +56,7 @@ export class HeaderSliderComponent {
@Output() @Output()
stateChange = new EventEmitter<boolean>(); stateChange = new EventEmitter<boolean>();
constructor() { } constructor() {}
get sliderStatus() { get sliderStatus() {
return this.state ? 'show' : 'hide'; return this.state ? 'show' : 'hide';
@@ -64,7 +68,6 @@ export class HeaderSliderComponent {
} }
public changeState() { public changeState() {
this.stateChange.emit(this.state) this.stateChange.emit(this.state);
} }
} }

View File

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

View File

@@ -1,9 +1,18 @@
<div class="links-container"> <div class="links-container">
<div class="nav-links"> <div class="nav-links">
<ul> <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"> <a [routerLink]="nav.link">
{{nav.page}} {{ nav.page }}
</a> </a>
</li> </li>
</ul> </ul>
@@ -11,18 +20,32 @@
</div> </div>
<div class="profile-container"> <div class="profile-container">
<div class="profile" <div
[@animateSliderItem]="{ value: itemStatus, params: { fadeInTime: .6 + (navLink.length+1)/20 , fadeOutTime: .6 - (navLink.length+1)/10 } }" class="profile"
#profile> [@animateSliderItem]="{
<div class="profile-btn" value: itemStatus,
(click)="onProfileButtonClicked()"> params: {
<fa-icon *ngIf="!loggedUser || !(loggedUser.profilePictureUrl)" fadeInTime: 0.6 + (navLink.length + 1) / 20,
class="fas fa-user" [icon]="userIcon"></fa-icon> fadeOutTime: 0.6 - (navLink.length + 1) / 10
<img *ngIf="!!loggedUser && !!(loggedUser.profilePictureUrl)" }
}"
#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" class="profile-picture"
[ngSrc]="loggedUser.profilePictureUrl" [ngSrc]="loggedUser.profilePictureUrl"
width="50" height="50" width="50"
alt="Profile Picture" priority/> height="50"
alt="Profile Picture"
priority
/>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -8,9 +8,8 @@ describe('NavSliderComponent', () => {
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ NavSliderComponent ] declarations: [NavSliderComponent],
}) }).compileComponents();
.compileComponents();
fixture = TestBed.createComponent(NavSliderComponent); fixture = TestBed.createComponent(NavSliderComponent);
component = fixture.componentInstance; 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 { faUser } from '@fortawesome/free-solid-svg-icons';
import { SliderItemComponent } from 'src/app/shared/components/slider-item/slider-item.component'; import { SliderItemComponent } from 'src/app/shared/components/slider-item/slider-item.component';
import UserChecker from "../../../shared/model/user/user.checker"; import UserChecker from '../../../shared/model/user/user.checker';
import {User} from "../../../shared/model/user/user.model"; import { User } from '../../../shared/model/user/user.model';
import {AuthService} from "../../../shared/auth/auth.service"; import { AuthService } from '../../../shared/auth/auth.service';
import {Subscription} from "rxjs"; import { Subscription } from 'rxjs';
@Component({ @Component({
selector: 'app-nav-slider', selector: 'app-nav-slider',
templateUrl: './nav-slider.component.html', templateUrl: './nav-slider.component.html',
styleUrls: ['./nav-slider.component.css'] styleUrls: ['./nav-slider.component.css'],
}) })
export class NavSliderComponent extends SliderItemComponent implements OnInit, OnDestroy { export class NavSliderComponent
extends SliderItemComponent
implements OnInit, OnDestroy
{
userIcon = faUser; userIcon = faUser;
navLink = [ navLink = [
{ page: "Home", link: "/home" }, { page: 'Home', link: '/home' },
{ page: "Work", link: "/home" }, { page: 'Work', link: '/home' },
{ page: "Contact", link: "/home" }, { page: 'Contact', link: '/home' },
{ page: "About", link: "/home" } { page: 'About', link: '/home' },
] ];
loggedUser!: User | null; loggedUser!: User | null;
@@ -35,14 +43,14 @@ export class NavSliderComponent extends SliderItemComponent implements OnInit, O
ngOnInit(): void { ngOnInit(): void {
this.userSubscription = this.authService.authSubject.subscribe( this.userSubscription = this.authService.authSubject.subscribe(
res => { (res) => {
if (res && UserChecker.test(res)) { if (res && UserChecker.test(res)) {
this.loggedUser = <User>res; this.loggedUser = <User>res;
} else { } else {
this.loggedUser = null; this.loggedUser = null;
} }
} },
) );
} }
ngOnDestroy(): void { ngOnDestroy(): void {
@@ -52,5 +60,4 @@ export class NavSliderComponent extends SliderItemComponent implements OnInit, O
onProfileButtonClicked() { onProfileButtonClicked() {
this.profileButtonClicked.emit(); this.profileButtonClicked.emit();
} }
} }

View File

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

View File

@@ -1,9 +1,21 @@
<div class="user-container"> <div class="user-container">
<div class="user-options"> <div class="user-options">
<ul> <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()"> <a (click)="options.onClick()">
{{options.name}} {{ options.name }}
</a> </a>
</li> </li>
</ul> </ul>

View File

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

View File

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

View File

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

View File

@@ -2,7 +2,7 @@
<div class="main" #header> <div class="main" #header>
<div class="logo"> <div class="logo">
<a routerLink=""> <a routerLink="">
<img src="assets/img/logohideyoshi-white.png" alt=""> <img src="assets/img/logohideyoshi-white.png" alt="" />
</a> </a>
</div> </div>
<div class="nav-links"> <div class="nav-links">
@@ -15,14 +15,25 @@
</div> </div>
<div class="profile" #profileDropdown> <div class="profile" #profileDropdown>
<div class="profile-btn" (click)="toogleProfileDropdown()" #profileBtn> <div
<fa-icon *ngIf="!loggedUser || !(loggedUser.profilePictureUrl)" class="profile-btn"
class="fas fa-user" [icon]="userIcon"></fa-icon> (click)="toogleProfileDropdown()"
<img *ngIf="!!loggedUser && !!(loggedUser.profilePictureUrl)" #profileBtn
>
<fa-icon
*ngIf="!loggedUser || !loggedUser.profilePictureUrl"
class="fas fa-user"
[icon]="userIcon"
></fa-icon>
<img
*ngIf="!!loggedUser && !!loggedUser.profilePictureUrl"
class="profile-picture" class="profile-picture"
[ngSrc]="loggedUser.profilePictureUrl" [ngSrc]="loggedUser.profilePictureUrl"
width="50" height="50" width="50"
alt="Profile Picture" priority/> height="50"
alt="Profile Picture"
priority
/>
</div> </div>
<app-header-dropdown <app-header-dropdown
@@ -33,22 +44,27 @@
(loginPopupState)="loginPopupStateChange($event)" (loginPopupState)="loginPopupStateChange($event)"
(signupPopupState)="signupPopupStateChange($event)" (signupPopupState)="signupPopupStateChange($event)"
(myProfilePopupState)="myProfilePopupStateChange($event)" (myProfilePopupState)="myProfilePopupStateChange($event)"
(helpPopupState)="helpPopupStateChange($event)"> (helpPopupState)="helpPopupStateChange($event)"
>
</app-header-dropdown> </app-header-dropdown>
</div> </div>
<div class="burger-container" (click)="toogleNavSlider()"> <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> </div>
<div #nav> <div #nav>
<app-header-slider <app-header-slider
[(state)]="navSliderStatus" [(state)]="navSliderStatus"
[clickOutsideStopWatching]="userSliderStatus" [clickOutsideStopWatching]="userSliderStatus"
[ignoreClickOutside]="[header, user]"> [ignoreClickOutside]="[header, user]"
>
<app-nav-slider <app-nav-slider
[state]="navSliderStatus" [state]="navSliderStatus"
(profileButtonClicked)="profileButtonClicked()"> (profileButtonClicked)="profileButtonClicked()"
>
</app-nav-slider> </app-nav-slider>
</app-header-slider> </app-header-slider>
</div> </div>
@@ -56,13 +72,15 @@
<div #user> <div #user>
<app-header-slider <app-header-slider
[(state)]="userSliderStatus" [(state)]="userSliderStatus"
[ignoreClickOutside]="[header, nav]"> [ignoreClickOutside]="[header, nav]"
>
<app-user-slider <app-user-slider
[state]="userSliderStatus" [state]="userSliderStatus"
(loginPopupState)="loginPopupStateChange($event)" (loginPopupState)="loginPopupStateChange($event)"
(signupPopupState)="signupPopupStateChange($event)" (signupPopupState)="signupPopupStateChange($event)"
(myProfilePopupState)="myProfilePopupStateChange($event)" (myProfilePopupState)="myProfilePopupStateChange($event)"
(helpPopupState)="helpPopupStateChange($event)"> (helpPopupState)="helpPopupStateChange($event)"
>
</app-user-slider> </app-user-slider>
</app-header-slider> </app-header-slider>
</div> </div>

View File

@@ -8,9 +8,8 @@ describe('HeaderComponent', () => {
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ HeaderComponent ] declarations: [HeaderComponent],
}) }).compileComponents();
.compileComponents();
fixture = TestBed.createComponent(HeaderComponent); fixture = TestBed.createComponent(HeaderComponent);
component = fixture.componentInstance; 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 { faUser } from '@fortawesome/free-solid-svg-icons';
import { LoginComponent } from './header-popup/login/login.component'; import { LoginComponent } from './header-popup/login/login.component';
import { SignupComponent } from './header-popup/signup/signup.component'; import { SignupComponent } from './header-popup/signup/signup.component';
import {AuthService} from "../shared/auth/auth.service"; import { AuthService } from '../shared/auth/auth.service';
import UserChecker from "../shared/model/user/user.checker"; import UserChecker from '../shared/model/user/user.checker';
import {User} from "../shared/model/user/user.model"; import { User } from '../shared/model/user/user.model';
import {Subscription} from "rxjs"; import { Subscription } from 'rxjs';
import {HelpComponent} from "./header-popup/help/help.component"; import { HelpComponent } from './header-popup/help/help.component';
import {MyProfileComponent} from "./header-popup/my-profile/my-profile.component"; import { MyProfileComponent } from './header-popup/my-profile/my-profile.component';
@Component({ @Component({
selector: 'app-header', selector: 'app-header',
templateUrl: './header.component.html', templateUrl: './header.component.html',
styleUrls: ['./header.component.css'] styleUrls: ['./header.component.css'],
}) })
export class HeaderComponent implements OnInit, OnDestroy { export class HeaderComponent implements OnInit, OnDestroy {
userIcon = faUser; userIcon = faUser;
profileDropdownState: boolean = false; profileDropdownState: boolean = false;
@@ -46,25 +53,27 @@ export class HeaderComponent implements OnInit, OnDestroy {
private helpComponent!: ComponentRef<HelpComponent>; private helpComponent!: ComponentRef<HelpComponent>;
constructor(private viewContainerRef: ViewContainerRef, private authService: AuthService) { } constructor(
private viewContainerRef: ViewContainerRef,
private authService: AuthService,
) {}
ngOnInit(): void { ngOnInit(): void {
this.userSubscription = this.authService.authSubject.subscribe( this.userSubscription = this.authService.authSubject.subscribe(
res => { (res) => {
if (res && UserChecker.test(res)) { if (res && UserChecker.test(res)) {
this.loggedUser = <User>res; this.loggedUser = <User>res;
} else { } else {
this.loggedUser = null; this.loggedUser = null;
} }
} },
) );
} }
ngOnDestroy(): void { ngOnDestroy(): void {
this.userSubscription.unsubscribe(); this.userSubscription.unsubscribe();
} }
public toogleProfileDropdown(): void { public toogleProfileDropdown(): void {
this.profileDropdownState = !this.profileDropdownState; this.profileDropdownState = !this.profileDropdownState;
} }
@@ -121,22 +130,21 @@ export class HeaderComponent implements OnInit, OnDestroy {
} }
private createLoginPopup(): void { private createLoginPopup(): void {
this.loginComponent = this.viewContainerRef.createComponent(LoginComponent); this.loginComponent =
this.viewContainerRef.createComponent(LoginComponent);
this.loginComponent.instance.state = true; this.loginComponent.instance.state = true;
this.loginComponent.instance.ignoreClickOutside = [ this.loginComponent.instance.ignoreClickOutside = [
this.profileBtnElementRef, this.profileBtnElementRef,
this.profileDropdownElementRef, this.profileDropdownElementRef,
this.userElementRef this.userElementRef,
].map(element => element.nativeElement); ].map((element) => element.nativeElement);
this.loginComponent.instance.stateChange.subscribe( this.loginComponent.instance.stateChange.subscribe((state) => {
state => {
if (!state) { if (!state) {
this.closeLoginPopup() this.closeLoginPopup();
} }
} });
);
this.navSliderStatus = false; this.navSliderStatus = false;
this.userSliderStatus = false; this.userSliderStatus = false;
@@ -144,22 +152,21 @@ export class HeaderComponent implements OnInit, OnDestroy {
} }
private createSignupPopup() { private createSignupPopup() {
this.signupComponent = this.viewContainerRef.createComponent(SignupComponent); this.signupComponent =
this.viewContainerRef.createComponent(SignupComponent);
this.signupComponent.instance.state = true; this.signupComponent.instance.state = true;
this.signupComponent.instance.ignoreClickOutside = [ this.signupComponent.instance.ignoreClickOutside = [
this.profileBtnElementRef, this.profileBtnElementRef,
this.profileDropdownElementRef, this.profileDropdownElementRef,
this.userElementRef this.userElementRef,
].map(element => element.nativeElement); ].map((element) => element.nativeElement);
this.signupComponent.instance.stateChange.subscribe( this.signupComponent.instance.stateChange.subscribe((state) => {
state => {
if (!state) { if (!state) {
this.closeSignupPopup() this.closeSignupPopup();
} }
} });
);
this.navSliderStatus = false; this.navSliderStatus = false;
this.userSliderStatus = false; this.userSliderStatus = false;
@@ -167,17 +174,16 @@ export class HeaderComponent implements OnInit, OnDestroy {
} }
private createMyProfilePopup() { private createMyProfilePopup() {
this.myProfileComponent = this.viewContainerRef.createComponent(MyProfileComponent); this.myProfileComponent =
this.viewContainerRef.createComponent(MyProfileComponent);
this.myProfileComponent.instance.state = true; this.myProfileComponent.instance.state = true;
this.myProfileComponent.instance.user = this.loggedUser; this.myProfileComponent.instance.user = this.loggedUser;
this.myProfileComponent.instance.stateChange.subscribe( this.myProfileComponent.instance.stateChange.subscribe((state) => {
state => {
if (!state) { if (!state) {
this.closeMyProfilePopup() this.closeMyProfilePopup();
} }
} });
);
this.navSliderStatus = false; this.navSliderStatus = false;
this.userSliderStatus = false; this.userSliderStatus = false;
@@ -185,16 +191,15 @@ export class HeaderComponent implements OnInit, OnDestroy {
} }
private createHelpPopup() { private createHelpPopup() {
this.helpComponent = this.viewContainerRef.createComponent(HelpComponent); this.helpComponent =
this.viewContainerRef.createComponent(HelpComponent);
this.helpComponent.instance.state = true; this.helpComponent.instance.state = true;
this.helpComponent.instance.stateChange.subscribe( this.helpComponent.instance.stateChange.subscribe((state) => {
state => {
if (!state) { if (!state) {
this.closeHelpPopup() this.closeHelpPopup();
} }
} });
);
this.navSliderStatus = false; this.navSliderStatus = false;
this.userSliderStatus = false; this.userSliderStatus = false;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,9 +8,8 @@ describe('SliderItemComponent', () => {
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
declarations: [ SliderItemComponent ] declarations: [SliderItemComponent],
}) }).compileComponents();
.compileComponents();
fixture = TestBed.createComponent(SliderItemComponent); fixture = TestBed.createComponent(SliderItemComponent);
component = fixture.componentInstance; 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'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
@Component({ @Component({
selector: 'app-slider-item', selector: 'app-slider-item',
templateUrl: './slider-item.component.html', templateUrl: './slider-item.component.html',
styleUrls: ['./slider-item.component.css'], styleUrls: ['./slider-item.component.css'],
animations:[ animations: [
trigger('animateSliderItem',[ trigger('animateSliderItem', [
state('hide', style({ state(
'opacity': '0', 'hide',
'transform': 'translateX(150px)' style({
opacity: '0',
transform: 'translateX(150px)',
}), }),
{ {
params: { params: {
fadeInTime: 600, fadeInTime: 600,
fadeOutTime: 600 fadeOutTime: 600,
} },
}), },
state('show', style({ ),
'opacity': '1', state(
'transform': 'translateX(0px)' 'show',
style({
opacity: '1',
transform: 'translateX(0px)',
}), }),
{ {
params: { params: {
fadeOutTime: 600, fadeOutTime: 600,
fadeInTime: 600 fadeInTime: 600,
}, },
}), },
),
transition('hide => show', animate(`{{ fadeInTime }}s ease-in`)), transition('hide => show', animate(`{{ fadeInTime }}s ease-in`)),
transition('show => hide', animate(`{{ fadeOutTime }}s ease-out`)) transition('show => hide', animate(`{{ fadeOutTime }}s ease-out`)),
]) ]),
] ],
}) })
export class SliderItemComponent { export class SliderItemComponent {
@Input() @Input()
public state:boolean = false; public state: boolean = false;
constructor() { } constructor() {}
get itemStatus(): string { get itemStatus(): string {
return this.state ? 'show' : 'hide'; return this.state ? 'show' : 'hide';
} }
} }

View File

@@ -1,44 +1,42 @@
import {NgModule} from '@angular/core'; import { NgModule } from '@angular/core';
import {CommonModule} from '@angular/common'; import { CommonModule } from '@angular/common';
import {NgcCookieConsentConfig, NgcCookieConsentModule} from "ngx-cookieconsent"; import {
NgcCookieConsentConfig,
NgcCookieConsentModule,
} from 'ngx-cookieconsent';
const cookieConfig: NgcCookieConsentConfig = { const cookieConfig: NgcCookieConsentConfig = {
"cookie": { cookie: {
"domain": "tinesoft.github.io" domain: 'tinesoft.github.io',
}, },
"position": "bottom-left", position: 'bottom-left',
"theme": "classic", theme: 'classic',
"palette": { palette: {
"popup": { popup: {
"background": "#4e4e4e", background: '#4e4e4e',
"text": "#ffffff", text: '#ffffff',
"link": "#ffffff" link: '#ffffff',
}, },
"button": { button: {
"background": "#fa2f22", background: '#fa2f22',
"text": "#ffffff", text: '#ffffff',
"border": "transparent" 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({ @NgModule({
declarations: [], declarations: [],
imports: [ imports: [CommonModule, NgcCookieConsentModule.forRoot(cookieConfig)],
CommonModule,
NgcCookieConsentModule.forRoot(cookieConfig)
]
}) })
export class CookieConsentModule { export class CookieConsentModule {}
}

View File

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

View File

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

View File

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

View File

@@ -1,12 +1,12 @@
/** /**
* This module was automatically generated by `ts-interface-builder` * 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 // tslint:disable:object-literal-key-quotes
export const Token = t.iface([], { export const Token = t.iface([], {
"token": "string", token: 'string',
"expirationDate": t.union("string", "number"), expirationDate: t.union('string', 'number'),
}); });
const exportedTypeSuite: t.ITypeSuite = { const exportedTypeSuite: t.ITypeSuite = {

View File

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

View File

@@ -1,6 +1,6 @@
import { createCheckers } from "ts-interface-checker"; import { createCheckers } from 'ts-interface-checker';
import User from "./user.model-ti"; import User from './user.model-ti';
import Token from "../token/token.model-ti"; import Token from '../token/token.model-ti';
const UserChecker = createCheckers(User, Token)['User']; const UserChecker = createCheckers(User, Token)['User'];
export default UserChecker; export default UserChecker;

View File

@@ -1,19 +1,19 @@
/** /**
* This module was automatically generated by `ts-interface-builder` * 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 // tslint:disable:object-literal-key-quotes
export const User = t.iface([], { export const User = t.iface([], {
"id": t.opt("number"), id: t.opt('number'),
"name": t.opt("string"), name: t.opt('string'),
"email": t.opt("string"), email: t.opt('string'),
"username": "string", username: 'string',
"password": t.opt("string"), password: t.opt('string'),
"profilePictureUrl": t.opt("string"), profilePictureUrl: t.opt('string'),
"accessToken": t.opt("Token"), accessToken: t.opt('Token'),
"refreshToken": t.opt("Token"), refreshToken: t.opt('Token'),
"roles": t.opt(t.array("string")), roles: t.opt(t.array('string')),
}); });
const exportedTypeSuite: t.ITypeSuite = { 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 { export interface User {
id?: number, id?: number;
name?: string, name?: string;
email?: string, email?: string;
username: string, username: string;
password?: string, password?: string;
profilePictureUrl?: string, profilePictureUrl?: string;
accessToken?: Token, accessToken?: Token;
refreshToken?: Token, refreshToken?: Token;
roles?: Array<string>, roles?: Array<string>;
validateAccessToken?: () => Token | undefined; validateAccessToken?: () => Token | undefined;
}; }

View File

@@ -1,26 +1,27 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import {SwUpdate} from "@angular/service-worker"; import { SwUpdate } from '@angular/service-worker';
import {interval} from "rxjs"; import { interval } from 'rxjs';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root',
}) })
export class UpdateService { export class UpdateService {
constructor(private swUpdate: SwUpdate) { constructor(private swUpdate: SwUpdate) {
if (swUpdate.isEnabled) { if (swUpdate.isEnabled) {
interval(6 * 60 * 60).subscribe(() => swUpdate.checkForUpdate() interval(6 * 60 * 60).subscribe(() =>
.then(() => console.log('checking for updates'))); swUpdate
.checkForUpdate()
.then(() => console.log('checking for updates')),
);
} }
} }
public checkForUpdates(): void { public checkForUpdates(): void {
this.swUpdate.available.subscribe(event => this.promptUser()); this.swUpdate.available.subscribe((event) => this.promptUser());
} }
private promptUser(): void { private promptUser(): void {
console.log('updating to new version'); console.log('updating to new version');
this.swUpdate.activateUpdate().then(() => document.location.reload()); 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 { HttpClientModule } from '@angular/common/http';
import { PopupComponent } from './components/popup/popup.component'; import { PopupComponent } from './components/popup/popup.component';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import {CookieConsentModule} from "./cookie-consent/cookie-consent.module"; import { CookieConsentModule } from './cookie-consent/cookie-consent.module';
@NgModule({ @NgModule({
declarations: [ declarations: [
@@ -19,12 +19,8 @@ import {CookieConsentModule} from "./cookie-consent/cookie-consent.module";
HttpClientModule, HttpClientModule,
BrowserAnimationsModule, BrowserAnimationsModule,
FontAwesomeModule, FontAwesomeModule,
CookieConsentModule CookieConsentModule,
], ],
exports: [ exports: [ClickedOutsideDirective, SliderItemComponent, PopupComponent],
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) { export function ValidateEmailValidator(control: AbstractControl) {
const email = control.value; 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) { export function ValidateNotEmptyValidator(control: AbstractControl) {
const value = control.value; 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) { export function ValidatePasswordValidator(control: AbstractControl) {
var password = control.value; 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); var passwordValid = passwordRegex.test(password);
if (!passwordValid) { if (!passwordValid) {
return { invalidPassword: true }; return { invalidPassword: true };

View File

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

View File

@@ -1,5 +1,5 @@
export const environment = { export const environment = {
production: true, production: true,
backendPath: (<any>window)['env']['BACKEND_URL'], 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 = { export const environment = {
production: false, production: false,
backendPath: 'http://localhost:8070', backendPath: 'http://localhost:8070',
backendOAuthPath: 'http://localhost:8070' backendOAuthPath: 'http://localhost:8070',
}; };
/* /*

View File

@@ -1,71 +1,115 @@
@font-face { @font-face {
font-family: 'Montserrat'; font-family: "Montserrat";
font-style: italic; font-style: italic;
font-weight: 100; 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-face {
font-family: 'Montserrat'; font-family: "Montserrat";
font-style: italic; font-style: italic;
font-weight: 300; 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-face {
font-family: 'Montserrat'; font-family: "Montserrat";
font-style: italic; font-style: italic;
font-weight: 400; 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-face {
font-family: 'Montserrat'; font-family: "Montserrat";
font-style: italic; font-style: italic;
font-weight: 700; 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-face {
font-family: 'Montserrat'; font-family: "Montserrat";
font-style: italic; font-style: italic;
font-weight: 900; 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-face {
font-family: 'Montserrat'; font-family: "Montserrat";
font-style: normal; font-style: normal;
font-weight: 100; 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-face {
font-family: 'Montserrat'; font-family: "Montserrat";
font-style: normal; font-style: normal;
font-weight: 300; 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-face {
font-family: 'Montserrat'; font-family: "Montserrat";
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
src: url(assets/fonts/Montserrat/Montserrat-normal-400.eot); 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-face {
font-family: 'Montserrat'; font-family: "Montserrat";
font-style: normal; font-style: normal;
font-weight: 700; 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-face {
font-family: 'Montserrat'; font-family: "Montserrat";
font-style: normal; font-style: normal;
font-weight: 900; 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"> <html lang="en">
<head>
<head> <meta charset="utf-8" />
<meta charset="utf-8">
<title>Hideyoshi Portfolio</title> <title>Hideyoshi Portfolio</title>
<base href="/"> <base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="./assets/img/logohideyoshi-red.png"> <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"> <link rel="manifest" href="manifest.webmanifest" />
<meta name="mobile-web-app-capable" content="yes"> <link
<meta name="apple-mobile-web-app-capable" content="yes"> rel="preconnect"
<meta name="apple-mobile-web-app-capable" content="yes"> href="https://hideyoshi-portfolio-dev.s3.amazonaws.com"
<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">
<!-- Loading environment variables --> <!-- Loading environment variables -->
<script src="./assets/env.js"></script> <script src="./assets/env.js"></script>
</head> </head>
<body> <body>
<app-root></app-root> <app-root></app-root>
<noscript>Please enable JavaScript to continue using this application.</noscript> <noscript
</body> >Please enable JavaScript to continue using this
application.</noscript
>
</body>
</html> </html>

View File

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

View File

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

View File

@@ -1,11 +1,16 @@
/* You can add global styles to this file, and also import other style files */ /* 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; padding: 0 auto;
margin: 0 auto; margin: 0 auto;
} }
h1, h2, h3, h4, h5, h6 { h1,
font-family: 'Montserrat', sans-serif; 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 { getTestBed } from '@angular/core/testing';
import { import {
BrowserDynamicTestingModule, BrowserDynamicTestingModule,
platformBrowserDynamicTesting platformBrowserDynamicTesting,
} from '@angular/platform-browser-dynamic/testing'; } from '@angular/platform-browser-dynamic/testing';
// First, initialize the Angular testing environment. // First, initialize the Angular testing environment.

View File

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

View File

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

View File

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