From e8c8aedb67aa5081dbce45a6ef5f0bdd5fae6c73 Mon Sep 17 00:00:00 2001 From: Dominik Kus Date: Fri, 14 Feb 2025 16:32:44 +0100 Subject: [PATCH] add prerendered landing page --- frontend/angular.json | 8 +- frontend/package-lock.json | 448 ++++++++++-------- frontend/package.json | 2 + frontend/src/app/app.component.html | 11 +- frontend/src/app/app.component.scss | 7 - frontend/src/app/app.component.ts | 28 +- frontend/src/app/app.config.server.ts | 11 + frontend/src/app/app.config.ts | 4 +- frontend/src/app/app.routes-prerender.txt | 1 + frontend/src/app/app.routes.ts | 68 +-- .../app/common/header/header.component.html | 32 +- .../landing-page/landing-page.component.html | 1 + .../landing-page/landing-page.component.scss | 0 .../landing-page/landing-page.component.ts | 11 + .../main-layout/main-layout.component.html | 10 + .../main-layout/main-layout.component.scss | 7 + .../main-layout/main-layout.component.ts | 31 ++ frontend/src/main.server.ts | 7 + frontend/src/main.ts | 4 +- frontend/tsconfig.app.json | 2 +- 20 files changed, 414 insertions(+), 279 deletions(-) create mode 100644 frontend/src/app/app.config.server.ts create mode 100644 frontend/src/app/app.routes-prerender.txt create mode 100644 frontend/src/app/pages/landing-page/landing-page.component.html create mode 100644 frontend/src/app/pages/landing-page/landing-page.component.scss create mode 100644 frontend/src/app/pages/landing-page/landing-page.component.ts create mode 100644 frontend/src/app/pages/main-layout/main-layout.component.html create mode 100644 frontend/src/app/pages/main-layout/main-layout.component.scss create mode 100644 frontend/src/app/pages/main-layout/main-layout.component.ts create mode 100644 frontend/src/main.server.ts diff --git a/frontend/angular.json b/frontend/angular.json index 85007ba..25f40d7 100644 --- a/frontend/angular.json +++ b/frontend/angular.json @@ -28,7 +28,13 @@ }, "scripts": [], "baseHref": "/", - "browser": "src/main.ts" + "browser": "src/main.ts", + "server": "src/main.server.ts", + "prerender": { + "discoverRoutes": false, + "routesFile": "src/app/app.routes-prerender.txt" + }, + "ssr": false }, "configurations": { "production": { diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 3e76c6b..78ae4a1 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -15,7 +15,9 @@ "@angular/material": "^19.1.3", "@angular/platform-browser": "^19.1.5", "@angular/platform-browser-dynamic": "^19.1.5", + "@angular/platform-server": "^19.1.5", "@angular/router": "^19.1.5", + "@angular/ssr": "^19.1.7", "@ng-web-apis/audio": "^4.11.1", "fuse.js": "^7.1.0", "lodash-es": "^4.17.21", @@ -73,13 +75,13 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.1901.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1901.6.tgz", - "integrity": "sha512-JiMrs3T1A7RyF5bh0PLGKDjTR8sa/kh8w63+dW0azcNok30tKjLjwJRPTpePokWefjmRgfKaf/iZ8yfFBnpGpA==", + "version": "0.1901.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1901.7.tgz", + "integrity": "sha512-qltyebfbej7joIKZVH8EFfrVDrkw0p9N9ja3A0XeU1sl2vlepHNAQdVm0Os8Vy2XjjyHvT5bXWE3G3/221qEKw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.1.6", + "@angular-devkit/core": "19.1.7", "rxjs": "7.8.1" }, "engines": { @@ -89,17 +91,17 @@ } }, "node_modules/@angular-devkit/build-angular": { - "version": "19.1.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-19.1.6.tgz", - "integrity": "sha512-QZtzkD0PQnBMpIwXyFTa9ZqN2wIEssh8V2VBRXzp0GXOZYvU18ICdZwJCXbRU2Bixwk8mrDALfMfryDWovJkGQ==", + "version": "19.1.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-19.1.7.tgz", + "integrity": "sha512-CMl3D5cpXoY0WuvdYtuOU2TetCwqxNsYM2jpuGG/kuuTEASAOI1cs9OhGwny1A/63bB8eyL33eLe82ON2Oemow==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1901.6", - "@angular-devkit/build-webpack": "0.1901.6", - "@angular-devkit/core": "19.1.6", - "@angular/build": "19.1.6", + "@angular-devkit/architect": "0.1901.7", + "@angular-devkit/build-webpack": "0.1901.7", + "@angular-devkit/core": "19.1.7", + "@angular/build": "19.1.7", "@babel/core": "7.26.0", "@babel/generator": "7.26.3", "@babel/helper-annotate-as-pure": "7.25.9", @@ -110,7 +112,7 @@ "@babel/preset-env": "7.26.0", "@babel/runtime": "7.26.0", "@discoveryjs/json-ext": "0.6.3", - "@ngtools/webpack": "19.1.6", + "@ngtools/webpack": "19.1.7", "@vitejs/plugin-basic-ssl": "1.2.0", "ansi-colors": "4.1.3", "autoprefixer": "10.4.20", @@ -164,7 +166,7 @@ "@angular/localize": "^19.0.0", "@angular/platform-server": "^19.0.0", "@angular/service-worker": "^19.0.0", - "@angular/ssr": "^19.1.6", + "@angular/ssr": "^19.1.7", "@web/test-runner": "^0.19.0", "browser-sync": "^3.0.2", "jest": "^29.5.0", @@ -215,13 +217,13 @@ } }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1901.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1901.6.tgz", - "integrity": "sha512-QNcc1XrmkyPuUBn0OGZjUAZZiKstaEHWkk1lSXN2YqRasGcoQjZdKZC3/Xl6ng/A4WjNSFFpImSIiSRI8xd0Og==", + "version": "0.1901.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1901.7.tgz", + "integrity": "sha512-g7xPN7unBnqP9HsgFvEV1DIhNYmVwmWR9ZiSP0xJq+EjpjWlz2vmgru4a5WKwGeuLsP8vg7RKV0kCH3bunOmFA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1901.6", + "@angular-devkit/architect": "0.1901.7", "rxjs": "7.8.1" }, "engines": { @@ -235,9 +237,9 @@ } }, "node_modules/@angular-devkit/core": { - "version": "19.1.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.1.6.tgz", - "integrity": "sha512-4s1RpYFGb/yP6OZ1dnYmU7maFYdhZS9pnUHKKiL9rSDhUHkX+VZlf9WFFrHv2RMWg+evrrwPtiFOTMBLShUi8g==", + "version": "19.1.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.1.7.tgz", + "integrity": "sha512-q0I6L9KTqyQ7D5M8H+fWLT+yjapvMNb7SRdfU6GzmexO66Dpo83q4HDzuDKIPDF29Yl0ELs9ICJqe9yUXh6yDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -263,13 +265,13 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "19.1.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.1.6.tgz", - "integrity": "sha512-6ljZSVTFqnk0utnXLLd82wM6nj68984n5gfrpT1PlOff6MHHNH2YCfwNSlwg6Q5UfDxhEDIT9/MTLnXd6znIRQ==", + "version": "19.1.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.1.7.tgz", + "integrity": "sha512-AP6FvhMybCYs3gs+vzEAzSU1K//AFT3SVTRFv+C3WMO5dLeAHeGzM8I2dxD5EHQQtqIE/8apP6CxGrnpA5YlFg==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.1.6", + "@angular-devkit/core": "19.1.7", "jsonc-parser": "3.3.1", "magic-string": "0.30.17", "ora": "5.4.1", @@ -385,9 +387,9 @@ } }, "node_modules/@angular/animations": { - "version": "19.1.5", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-19.1.5.tgz", - "integrity": "sha512-jRZgLdSjr94EpBFIyCUZM7YKBi5TO2+J8PKmz7IdNrYNuUaGfy8k816/57Vgmsb18dnpA2Kf7R2AlOpNcDcsOA==", + "version": "19.1.6", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-19.1.6.tgz", + "integrity": "sha512-iacosz3fygp0AyT57+suVpLChl10xS5RBje09TfQIKHTUY0LWkMspgaK9gwtlflpIhjedPV0UmgRIKhhFcQM1A==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -396,19 +398,19 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "19.1.5" + "@angular/core": "19.1.6" } }, "node_modules/@angular/build": { - "version": "19.1.6", - "resolved": "https://registry.npmjs.org/@angular/build/-/build-19.1.6.tgz", - "integrity": "sha512-6zGdMxMITBj5oVRDKcOL+ufrCSsPLPd5AeRcGkaCYQDshaOmn0UXL4HQylU3nswhVT0dtCd4eDA7fh2dlyVF6A==", + "version": "19.1.7", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-19.1.7.tgz", + "integrity": "sha512-22SjHZDTk91JHU5aFVDU2n+xkPolDosRVfsK4zs+RRXQs30LYPH9KCLiUWCYjFbRj7oYvw7sbrs94szo7dWYvw==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1901.6", - "@angular-devkit/core": "19.1.6", + "@angular-devkit/architect": "0.1901.7", + "@angular-devkit/core": "19.1.7", "@babel/core": "7.26.0", "@babel/helper-annotate-as-pure": "7.25.9", "@babel/helper-split-export-declaration": "7.24.7", @@ -447,7 +449,7 @@ "@angular/localize": "^19.0.0", "@angular/platform-server": "^19.0.0", "@angular/service-worker": "^19.0.0", - "@angular/ssr": "^19.1.6", + "@angular/ssr": "^19.1.7", "less": "^4.2.0", "ng-packagr": "^19.0.0", "postcss": "^8.4.0", @@ -554,9 +556,9 @@ } }, "node_modules/@angular/cdk": { - "version": "19.1.3", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-19.1.3.tgz", - "integrity": "sha512-A8d1V4AU2ZcNnEEwAUp4W1uYdT7EKHZM0PGicVhLyeetwYrpHiLoPioD7sw89TlPuJcd6mS7xV6AnXQ8peOoXg==", + "version": "19.1.4", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-19.1.4.tgz", + "integrity": "sha512-PyvJ1VbYjW8tVnVHvcasiqI9eNWf8EJnr0in1QWnhpSbpVpVpc4yjbgnu2pTrW9mPo/YjV4pF+qs6E97y9mdYQ==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -571,18 +573,18 @@ } }, "node_modules/@angular/cli": { - "version": "19.1.6", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-19.1.6.tgz", - "integrity": "sha512-5H9Ri+YNPBnac/h1wTPQ+9mLSXfT1n99FwCtMVy6YnG+akRqOKFmPWB29hkFQAgfXi/MYIj+rQKv+d/9yWJibQ==", + "version": "19.1.7", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-19.1.7.tgz", + "integrity": "sha512-qVEy0R4QKQ2QAGfpj2mPVxRxgOVst+rIgZBtLwf/mrbN9YyzJUaBKvaVslUpOqkvoW9mX5myf0iZkT5NykrIoA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1901.6", - "@angular-devkit/core": "19.1.6", - "@angular-devkit/schematics": "19.1.6", + "@angular-devkit/architect": "0.1901.7", + "@angular-devkit/core": "19.1.7", + "@angular-devkit/schematics": "19.1.7", "@inquirer/prompts": "7.2.1", "@listr2/prompt-adapter-inquirer": "2.0.18", - "@schematics/angular": "19.1.6", + "@schematics/angular": "19.1.7", "@yarnpkg/lockfile": "1.1.0", "ini": "5.0.0", "jsonc-parser": "3.3.1", @@ -605,9 +607,9 @@ } }, "node_modules/@angular/common": { - "version": "19.1.5", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-19.1.5.tgz", - "integrity": "sha512-8jR3c5IBMlfiiHvrO8Y2z8y9n4Moy4mI7bS0eu3hmI3m5Vvrgd2Z4GCaQ/Dt4wCtFxcgSsVXiF+/H0QbVdwulA==", + "version": "19.1.6", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-19.1.6.tgz", + "integrity": "sha512-FkuejwbxsOLhcyOgDM/7YEYvMG3tuyOvr+831VzPwMwYp5QO9AUYtn4ffGf698JccbA+Ocw3BAdhPU6i+YZC1A==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -616,14 +618,14 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "19.1.5", + "@angular/core": "19.1.6", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "19.1.5", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-19.1.5.tgz", - "integrity": "sha512-8dhticSq98qZanbPBqLACykR08eHbh9WyXG4VJB7Ru9465DjOd6sRM3gmGDNvNlohh30S4xJzPhVrzYXmIyqiA==", + "version": "19.1.6", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-19.1.6.tgz", + "integrity": "sha512-Tl2PFEtnU8UgSqtEKG827xDUGZrErhR6S1JICeV1kbRCYmwQA4hhG25tzi+ifSAOPW7eJiyzP2LWIvOuZkq3Vw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -632,7 +634,7 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "19.1.5" + "@angular/core": "19.1.6" }, "peerDependenciesMeta": { "@angular/core": { @@ -641,9 +643,9 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "19.1.5", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-19.1.5.tgz", - "integrity": "sha512-7IHfGklqiTsDYjk2SgOi5sG63gZ60LguT7dhMGtUdy+fUyK0KGofE1w74LwPHQ3huCdu3rBp7HZvC0/IsmiYtA==", + "version": "19.1.6", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-19.1.6.tgz", + "integrity": "sha512-rTpHC/tfLBj+5a3X+BA/4s2w5T/cHT6x3RgO8CYy7003Musn0/BiqjfE6VCIllQgLaOQRhCcf51T6Kerkzv8Dw==", "dev": true, "license": "MIT", "dependencies": { @@ -665,14 +667,14 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/compiler": "19.1.5", + "@angular/compiler": "19.1.6", "typescript": ">=5.5 <5.8" } }, "node_modules/@angular/core": { - "version": "19.1.5", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-19.1.5.tgz", - "integrity": "sha512-N4Uh/jRV2Ksj1iBnhIHkB5hzeiF7J9rhUTiztDPaRT7YpFVt2MKiBXrn52HDcKXPaPFrsZBotbZ6oOMdP4rd5g==", + "version": "19.1.6", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-19.1.6.tgz", + "integrity": "sha512-FD167URT+apxjzj9sG/MzffW5G6YyQiPQ6nrrIoYi9jeY3LYurybuOgvcXrU8PT4Z3+CKMq9k/ZnmrlHU72BpA==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -686,9 +688,9 @@ } }, "node_modules/@angular/forms": { - "version": "19.1.5", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-19.1.5.tgz", - "integrity": "sha512-MUebiFrIhwB1m9rp8v/tgftsCmcI5OjUjnbsiuDsPp/291qxbsJ3P/wmvmCHYEJOoFxVLEgOjJvFcmYN/VbxLw==", + "version": "19.1.6", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-19.1.6.tgz", + "integrity": "sha512-uu/76KAwCAcDuhD67Vv78UvOC/tiprtFXOgqNCj0LK8vyFcvPsunb3nF/PtfF9rSHyslXAqxZhME+Ha2tU6Lpw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -697,23 +699,23 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "19.1.5", - "@angular/core": "19.1.5", - "@angular/platform-browser": "19.1.5", + "@angular/common": "19.1.6", + "@angular/core": "19.1.6", + "@angular/platform-browser": "19.1.6", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/material": { - "version": "19.1.3", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-19.1.3.tgz", - "integrity": "sha512-ii19ow7V8fLsgTvnghDBObte8G0I2orgsG+jwR8fdO1Hp+9d+IEeITLvn2sc7qVofkv/DzG4rCTFaLQdOXRWmg==", + "version": "19.1.4", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-19.1.4.tgz", + "integrity": "sha512-bqliTnUnMiTG6Quvk16epiQPZQB0zV/L2ctPinFcP6NhahcQFfahjabUlgMlfBk5qObolJArJr5HCMiOmpOGIQ==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/animations": "^19.0.0 || ^20.0.0", - "@angular/cdk": "19.1.3", + "@angular/cdk": "19.1.4", "@angular/common": "^19.0.0 || ^20.0.0", "@angular/core": "^19.0.0 || ^20.0.0", "@angular/forms": "^19.0.0 || ^20.0.0", @@ -722,9 +724,9 @@ } }, "node_modules/@angular/platform-browser": { - "version": "19.1.5", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-19.1.5.tgz", - "integrity": "sha512-wqM4OlGncXNmROTS0mpUpnzzG5DsIZi1U0gzQp5bDOknaFFmg2C2ExCi29CwFZfaOeDw135AyXtu4qItfDOW9A==", + "version": "19.1.6", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-19.1.6.tgz", + "integrity": "sha512-sfWU+gMpqQ6GYtE3tAfDktftC01NgtqAOKfeCQ/KY2rxRTIxYahenW0Licuzgmd+8AZtmncoZaYX0Fd/5XMqzQ==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -733,9 +735,9 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/animations": "19.1.5", - "@angular/common": "19.1.5", - "@angular/core": "19.1.5" + "@angular/animations": "19.1.6", + "@angular/common": "19.1.6", + "@angular/core": "19.1.6" }, "peerDependenciesMeta": { "@angular/animations": { @@ -744,9 +746,9 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "19.1.5", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-19.1.5.tgz", - "integrity": "sha512-mW9Ru5C0/Jg+b2/pWfzfkWmFZ6Exn2J2k+6Unv1Vprh6whF4ch7v5AdBaCuLiK5kUPpQQMHhRz7VY+3mb/dgqQ==", + "version": "19.1.6", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-19.1.6.tgz", + "integrity": "sha512-QedjG7/ctPtzgJ3LcWv4yMcSivKlwcZ8ge8zPe7eu9Ft6mDZZat65gJEjDuvevJoeNbo2dQODFDiyPJNmnNA9A==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -755,16 +757,36 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "19.1.5", - "@angular/compiler": "19.1.5", - "@angular/core": "19.1.5", - "@angular/platform-browser": "19.1.5" + "@angular/common": "19.1.6", + "@angular/compiler": "19.1.6", + "@angular/core": "19.1.6", + "@angular/platform-browser": "19.1.6" + } + }, + "node_modules/@angular/platform-server": { + "version": "19.1.6", + "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-19.1.6.tgz", + "integrity": "sha512-yCFkByyHDtxXZaaaRV+268WN8s45ie+R5bcJ9oPKYE3Td0f/7iTRUatDfTI8h7/xbYK4nRgGZmxuiREpjt6y6Q==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0", + "xhr2": "^0.2.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/animations": "19.1.6", + "@angular/common": "19.1.6", + "@angular/compiler": "19.1.6", + "@angular/core": "19.1.6", + "@angular/platform-browser": "19.1.6" } }, "node_modules/@angular/router": { - "version": "19.1.5", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-19.1.5.tgz", - "integrity": "sha512-g5JLymyi+/PTIqKcImSUB9ac1g7szMG/jGax3nTXqwMOzWmxZJJIEKlXWmHJYjUyYEhKBdqLPUMa4JbkD+/jnA==", + "version": "19.1.6", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-19.1.6.tgz", + "integrity": "sha512-TEfw3W5jVodVDMD4krhXGog1THZN3x1yoh2oZmCv3lXg22+pVC6Cp+x3vVExq0mS+g3/6uZwy/3qAYdlzqYjTg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -773,12 +795,32 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "19.1.5", - "@angular/core": "19.1.5", - "@angular/platform-browser": "19.1.5", + "@angular/common": "19.1.6", + "@angular/core": "19.1.6", + "@angular/platform-browser": "19.1.6", "rxjs": "^6.5.3 || ^7.4.0" } }, + "node_modules/@angular/ssr": { + "version": "19.1.7", + "resolved": "https://registry.npmjs.org/@angular/ssr/-/ssr-19.1.7.tgz", + "integrity": "sha512-MNjjSWNv1K0E1K/U/pRgFo54n/1qyaFcYGYKSbnpwAGjtiCiHLudL3Dycke3mHTmcPFX61DniX1MdJtLhhFsVA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": "^19.0.0", + "@angular/core": "^19.0.0", + "@angular/platform-server": "^19.0.0", + "@angular/router": "^19.0.0" + }, + "peerDependenciesMeta": { + "@angular/platform-server": { + "optional": true + } + } + }, "node_modules/@babel/code-frame": { "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", @@ -910,18 +952,18 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", - "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.26.9.tgz", + "integrity": "sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", - "@babel/helper-replace-supers": "^7.25.9", + "@babel/helper-replace-supers": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", - "@babel/traverse": "^7.25.9", + "@babel/traverse": "^7.26.9", "semver": "^6.3.1" }, "engines": { @@ -1164,27 +1206,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.7.tgz", - "integrity": "sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz", + "integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.7" + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.8.tgz", - "integrity": "sha512-TZIQ25pkSoaKEYYaHbbxkfL36GNsQ6iFiBbeuzAkLnXayKR1yP1zFe+NxuZWWsUyvt8icPU9CCq0sgWGXR1GEw==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz", + "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.26.8" + "@babel/types": "^7.26.9" }, "bin": { "parser": "bin/babel-parser.js" @@ -1610,13 +1652,13 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", - "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.26.9.tgz", + "integrity": "sha512-Hry8AusVm8LW5BVFgiyUReuoGzPUpdHQQqJY5bZnbbf+ngOHWuCuYFKw/BqaaWlvEUrF91HMhDtEaI1hZzNbLg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { @@ -2330,32 +2372,32 @@ } }, "node_modules/@babel/template": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.8.tgz", - "integrity": "sha512-iNKaX3ZebKIsCvJ+0jd6embf+Aulaa3vNBqZ41kM7iTWjx5qzWKXGHiJUW3+nTpQ18SG11hdF8OAzKrpXkb96Q==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", + "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.26.8", - "@babel/types": "^7.26.8" + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.8.tgz", - "integrity": "sha512-nic9tRkjYH0oB2dzr/JoGIm+4Q6SuYeLEiIiZDwBscRMYFJ+tMAz98fuel9ZnbXViA2I0HVSSRRK8DW5fjXStA==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz", + "integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.8", - "@babel/parser": "^7.26.8", - "@babel/template": "^7.26.8", - "@babel/types": "^7.26.8", + "@babel/generator": "^7.26.9", + "@babel/parser": "^7.26.9", + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.9", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -2364,14 +2406,14 @@ } }, "node_modules/@babel/traverse/node_modules/@babel/generator": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.8.tgz", - "integrity": "sha512-ef383X5++iZHWAXX0SXQR6ZyQhw/0KtTkrTz61WXRhFM6dhpHulO/RJz79L8S6ugZHJkOOkUrUdxgdF2YiPFnA==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz", + "integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.8", - "@babel/types": "^7.26.8", + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -2381,9 +2423,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.8.tgz", - "integrity": "sha512-eUuWapzEGWFEpHFxgEaBG8e3n6S8L3MSu0oda755rOfabWPnh0Our1AozNFVUxGFIhbKgd1ksprsoDGMinTOTA==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz", + "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==", "dev": true, "license": "MIT", "dependencies": { @@ -4163,9 +4205,9 @@ } }, "node_modules/@ngtools/webpack": { - "version": "19.1.6", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-19.1.6.tgz", - "integrity": "sha512-36I/y4KIRWyE35OgZXzYnDCNQ2G3VB/2awj8L3VowDyTP9enJj6V044UGpCNiKOgYerubgMN3gcPIsOzg0pLpg==", + "version": "19.1.7", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-19.1.7.tgz", + "integrity": "sha512-U39LVqHWj+GtKzBA3+AseHZgLPlL5YE/iRkZJ4PHQVrgW9LtyMzPuUmnW+e0XQwPFHq9xQxaoj3w8gApj4/MIg==", "dev": true, "license": "MIT", "engines": { @@ -5085,14 +5127,14 @@ "license": "MIT" }, "node_modules/@schematics/angular": { - "version": "19.1.6", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-19.1.6.tgz", - "integrity": "sha512-TxFp6iHBqXcuyZIW84HA4z3XkAMz3wTw46K3GNhzyfhFTFD0YD+DtaR3MfQ+vcj3YUYu9j44zrB9nchzugR9Ew==", + "version": "19.1.7", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-19.1.7.tgz", + "integrity": "sha512-BB8yMGmYDZzSb8Nu+Ln0TKyeoS3++f9STCYw30NwM3IViHxJJYxu/zowzwSa9TjftIzdCpbOaPxGS0vU9UOUDQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.1.6", - "@angular-devkit/schematics": "19.1.6", + "@angular-devkit/core": "19.1.7", + "@angular-devkit/schematics": "19.1.7", "jsonc-parser": "3.3.1" }, "engines": { @@ -5391,9 +5433,9 @@ } }, "node_modules/@types/jasmine": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.5.tgz", - "integrity": "sha512-SaCZ3kM5NjOiJqMRYwHpLbTfUC2Dyk1KS3QanNFsUYPGTk70CWVK/J9ueun6zNhw/UkgV7xl8V4ZLQZNRbfnNw==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.6.tgz", + "integrity": "sha512-JDwKwipGFDwf021BtRTuluYe1aMDNimtO72ygPrVXnZSC8Df2V22AHeIgGa84tbF4SLkRvN+dJnlV8aMwQjkVw==", "dev": true, "license": "MIT" }, @@ -5446,9 +5488,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.13.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz", - "integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==", + "version": "22.13.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.4.tgz", + "integrity": "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==", "dev": true, "license": "MIT", "dependencies": { @@ -5775,9 +5817,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.23.0.tgz", - "integrity": "sha512-1sK4ILJbCmZOTt9k4vkoulT6/y5CHJ1qUYxqpF1K/DBAd8+ZUL4LlSCxOssuH5m4rUaaN0uS0HlVPvd45zjduQ==", + "version": "8.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.24.0.tgz", + "integrity": "sha512-VacJCBTyje7HGAw7xp11q439A+zeGG0p0/p2zsZwpnMzjPB5WteaWqt4g2iysgGFafrqvyLWqq6ZPZAOCoefCw==", "dev": true, "license": "MIT", "peer": true, @@ -5833,17 +5875,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.23.0.tgz", - "integrity": "sha512-uB/+PSo6Exu02b5ZEiVtmY6RVYO7YU5xqgzTIVZwTHvvK3HsL8tZZHFaTLFtRG3CsV4A5mhOv+NZx5BlhXPyIA==", + "version": "8.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.24.0.tgz", + "integrity": "sha512-07rLuUBElvvEb1ICnafYWr4hk8/U7X9RDCOqd9JcAMtjh/9oRmcfN4yGzbPVirgMR0+HLVHehmu19CWeh7fsmQ==", "dev": true, "license": "MIT", "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.23.0", - "@typescript-eslint/types": "8.23.0", - "@typescript-eslint/typescript-estree": "8.23.0" + "@typescript-eslint/scope-manager": "8.24.0", + "@typescript-eslint/types": "8.24.0", + "@typescript-eslint/typescript-estree": "8.24.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5858,15 +5900,15 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { - "version": "8.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.23.0.tgz", - "integrity": "sha512-OGqo7+dXHqI7Hfm+WqkZjKjsiRtFUQHPdGMXzk5mYXhJUedO7e/Y7i8AK3MyLMgZR93TX4bIzYrfyVjLC+0VSw==", + "version": "8.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.24.0.tgz", + "integrity": "sha512-HZIX0UByphEtdVBKaQBgTDdn9z16l4aTUz8e8zPQnyxwHBtf5vtl1L+OhH+m1FGV9DrRmoDuYKqzVrvWDcDozw==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@typescript-eslint/types": "8.23.0", - "@typescript-eslint/visitor-keys": "8.23.0" + "@typescript-eslint/types": "8.24.0", + "@typescript-eslint/visitor-keys": "8.24.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5877,15 +5919,15 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.23.0.tgz", - "integrity": "sha512-LcqzfipsB8RTvH8FX24W4UUFk1bl+0yTOf9ZA08XngFwMg4Kj8A+9hwz8Cr/ZS4KwHrmo9PJiLZkOt49vPnuvQ==", + "version": "8.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.24.0.tgz", + "integrity": "sha512-ITjYcP0+8kbsvT9bysygfIfb+hBj6koDsu37JZG7xrCiy3fPJyNmfVtaGsgTUSEuTzcvME5YI5uyL5LD1EV5ZQ==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@typescript-eslint/types": "8.23.0", - "@typescript-eslint/visitor-keys": "8.23.0", + "@typescript-eslint/types": "8.24.0", + "@typescript-eslint/visitor-keys": "8.24.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -5905,14 +5947,14 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.23.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.23.0.tgz", - "integrity": "sha512-oWWhcWDLwDfu++BGTZcmXWqpwtkwb5o7fxUIGksMQQDSdPW9prsSnfIOZMlsj4vBOSrcnjIUZMiIjODgGosFhQ==", + "version": "8.24.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.24.0.tgz", + "integrity": "sha512-kArLq83QxGLbuHrTMoOEWO+l2MwsNS2TGISEdx8xgqpkbytB07XmlQyQdNDrCc1ecSqx0cnmhGvpX+VBwqqSkg==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@typescript-eslint/types": "8.23.0", + "@typescript-eslint/types": "8.24.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -7285,9 +7327,9 @@ } }, "node_modules/call-bind-apply-helpers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", - "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7336,9 +7378,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001698", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001698.tgz", - "integrity": "sha512-xJ3km2oiG/MbNU8G6zIq6XRZ6HtAOVXsbOrP/blGazi52kc5Yy7b6sDA5O+FbROzRrV7BSTllLHuNvmawYUJjw==", + "version": "1.0.30001699", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001699.tgz", + "integrity": "sha512-b+uH5BakXZ9Do9iK+CkDmctUSEqZl+SP056vc5usa0PL+ev5OHw003rZXcnjNDv3L8P5j6rwT6C0BPKSikW08w==", "dev": true, "funding": [ { @@ -7660,9 +7702,9 @@ } }, "node_modules/compression": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.5.tgz", - "integrity": "sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz", + "integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==", "dev": true, "license": "MIT", "dependencies": { @@ -8560,9 +8602,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.96", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.96.tgz", - "integrity": "sha512-8AJUW6dh75Fm/ny8+kZKJzI1pgoE8bKLZlzDU2W1ENd+DXKJrx7I7l9hb8UWR4ojlnb5OlixMt00QWiYJoVw1w==", + "version": "1.5.100", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.100.tgz", + "integrity": "sha512-u1z9VuzDXV86X2r3vAns0/5ojfXBue9o0+JDUDBKYqGLjxLkSqsSUoPU/6kW0gx76V44frHaf6Zo+QF74TQCMg==", "dev": true, "license": "ISC" }, @@ -8887,13 +8929,16 @@ } }, "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/es-to-primitive": { @@ -9884,9 +9929,9 @@ } }, "node_modules/for-each": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.4.tgz", - "integrity": "sha512-kKaIINnFpzW6ffJNDjjyjrk21BkDx38c0xa/klsT8VzLCaMEefv4ZTacrcVR4DmgTeBra++jMDAfS/tS799YDw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "dev": true, "license": "MIT", "dependencies": { @@ -13617,9 +13662,9 @@ } }, "node_modules/node-gyp": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-11.0.0.tgz", - "integrity": "sha512-zQS+9MTTeCMgY0F3cWPyJyRFAkVltQ1uXm+xXu/ES6KFgC6Czo1Seb9vQW2wNxSX2OrDTiqL0ojtkFxBQ0ypIw==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-11.1.0.tgz", + "integrity": "sha512-/+7TuHKnBpnMvUQnsYEb0JOozDZqarQbfNuSGLXIjhStMT0fbw7IdSqWgopOP5xhRZE+lsbIvAHcekddruPZgQ==", "dev": true, "license": "MIT", "dependencies": { @@ -14893,9 +14938,9 @@ } }, "node_modules/prettier": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", - "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.1.tgz", + "integrity": "sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw==", "dev": true, "license": "MIT", "bin": { @@ -15560,9 +15605,9 @@ } }, "node_modules/regex-parser": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.0.tgz", - "integrity": "sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.1.tgz", + "integrity": "sha512-yXLRqatcCuKtVHsWrNg0JL3l1zGfdXeEvDa0bdu4tCDQw0RpMDZsqbkyRTUnKMR0tXF627V2oEWjBEaEdqTwtQ==", "dev": true, "license": "MIT" }, @@ -16848,9 +16893,9 @@ } }, "node_modules/socks": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", - "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz", + "integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==", "dev": true, "license": "MIT", "dependencies": { @@ -18247,9 +18292,9 @@ } }, "node_modules/vite/node_modules/postcss": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", - "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz", + "integrity": "sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==", "dev": true, "funding": [ { @@ -19125,6 +19170,15 @@ } } }, + "node_modules/xhr2": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/xhr2/-/xhr2-0.2.1.tgz", + "integrity": "sha512-sID0rrVCqkVNUn8t6xuv9+6FViXjUVXq8H5rWOH2rz9fDNQEd4g0EA2XlcEdJXRz5BMEn4O1pJFdT+z4YHhoWw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/xml2js": { "version": "0.4.23", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", diff --git a/frontend/package.json b/frontend/package.json index 502787b..7ef1c05 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -20,7 +20,9 @@ "@angular/material": "^19.1.3", "@angular/platform-browser": "^19.1.5", "@angular/platform-browser-dynamic": "^19.1.5", + "@angular/platform-server": "^19.1.5", "@angular/router": "^19.1.5", + "@angular/ssr": "^19.1.7", "@ng-web-apis/audio": "^4.11.1", "fuse.js": "^7.1.0", "lodash-es": "^4.17.21", diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html index 18c5f80..0680b43 100644 --- a/frontend/src/app/app.component.html +++ b/frontend/src/app/app.component.html @@ -1,10 +1 @@ - - @switch (apiService.user() != null) { - @case (true) { - - } - @case (false) { - - } - } - + diff --git a/frontend/src/app/app.component.scss b/frontend/src/app/app.component.scss index a6f99dd..e69de29 100644 --- a/frontend/src/app/app.component.scss +++ b/frontend/src/app/app.component.scss @@ -1,7 +0,0 @@ -:host { - min-height: 100vh; - display: flex; - flex-direction: column; - max-width: 100vw; - overflow-x: hidden; -} diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts index 024d5d1..ecc4642 100644 --- a/frontend/src/app/app.component.ts +++ b/frontend/src/app/app.component.ts @@ -1,33 +1,11 @@ -import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; -import { forkJoin, of, Subject } from 'rxjs'; -import { catchError } from 'rxjs/operators'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; -import { ApiService, AppInfo, User } from './services/api.service'; -import { DataLoadDirective } from './common/data-load/data-load.directive'; - -import { LoginComponent } from './pages/login/login.component'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, - imports: [DataLoadDirective, RouterOutlet, LoginComponent], + imports: [RouterOutlet], }) -export class AppComponent { - protected apiService = inject(ApiService); - - readonly data$ = forkJoin([ - this.apiService.loadAppInfo(), - this.apiService.loadUser().pipe(catchError(() => of(null))), - ]); - readonly loadedData$ = new Subject<[AppInfo, User]>(); - - constructor() { - this.loadedData$.pipe(takeUntilDestroyed()).subscribe(data => { - this.apiService.appInfo.set(data[0]); - this.apiService.user.set(data[1]); - }); - } -} +export class AppComponent {} diff --git a/frontend/src/app/app.config.server.ts b/frontend/src/app/app.config.server.ts new file mode 100644 index 0000000..3514d3a --- /dev/null +++ b/frontend/src/app/app.config.server.ts @@ -0,0 +1,11 @@ +import { mergeApplicationConfig, ApplicationConfig } from '@angular/core'; +import { provideServerRendering } from '@angular/platform-server'; +import { appConfig } from './app.config'; + +const serverConfig: ApplicationConfig = { + providers: [ + provideServerRendering(), + ] +}; + +export const config = mergeApplicationConfig(appConfig, serverConfig); diff --git a/frontend/src/app/app.config.ts b/frontend/src/app/app.config.ts index f486f7a..c1235cd 100644 --- a/frontend/src/app/app.config.ts +++ b/frontend/src/app/app.config.ts @@ -3,10 +3,11 @@ import { MAT_SNACK_BAR_DEFAULT_OPTIONS } from '@angular/material/snack-bar'; import { provideHttpClient, withInterceptors } from '@angular/common/http'; import { PreloadAllModules, provideRouter, withComponentInputBinding, withPreloading } from '@angular/router'; import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; +import { provideClientHydration, withEventReplay } from '@angular/platform-browser'; import { APP_ROUTES } from './app.routes'; import { authInterceptor } from './services/auth-interceptor'; -export const APP_CONFIG: ApplicationConfig = { +export const appConfig: ApplicationConfig = { providers: [ { provide: MAT_SNACK_BAR_DEFAULT_OPTIONS, @@ -23,5 +24,6 @@ export const APP_CONFIG: ApplicationConfig = { provideRouter(APP_ROUTES, withComponentInputBinding(), withPreloading(PreloadAllModules)), provideHttpClient(withInterceptors([authInterceptor])), provideAnimationsAsync(), + provideClientHydration(withEventReplay()), ], }; diff --git a/frontend/src/app/app.routes-prerender.txt b/frontend/src/app/app.routes-prerender.txt new file mode 100644 index 0000000..b498fd4 --- /dev/null +++ b/frontend/src/app/app.routes-prerender.txt @@ -0,0 +1 @@ +/ diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts index 97f3bb8..6c83a92 100644 --- a/frontend/src/app/app.routes.ts +++ b/frontend/src/app/app.routes.ts @@ -2,55 +2,69 @@ import { Routes } from '@angular/router'; import { guildPermissionGuard } from './guards/guild-permission.guard'; import { canDeactivateSoundManagerGuard } from './pages/settings/sound-manager/can-deactivate-sound-manager.guard'; import { canDeactivateGuildSettingsGuard } from './pages/settings/guild-settings/can-deactivate-guild-settings.guard'; +import { LandingPageComponent } from './pages/landing-page/landing-page.component'; +import { MainLayoutComponent } from './pages/main-layout/main-layout.component'; export const APP_ROUTES: Routes = [ { path: '', - loadComponent: () => import('./pages/soundboard/soundboard.component').then(m => m.SoundboardComponent), + component: LandingPageComponent, }, { - path: 'keybind-generator', - loadComponent: () => - import('./pages/keybind-generator/keybind-generator.component').then(m => m.KeybindGeneratorComponent), - }, - { - path: 'recorder', - loadComponent: () => import('./pages/recorder/recorder.component').then(m => m.RecorderComponent), - }, - { - path: 'settings', - loadComponent: () => import('./pages/settings/settings.component').then(m => m.SettingsComponent), + path: '', + component: MainLayoutComponent, children: [ { - path: '', - pathMatch: 'full', - redirectTo: 'user', + path: 'soundboard', + loadComponent: () => import('./pages/soundboard/soundboard.component').then(m => m.SoundboardComponent), }, { - path: 'user', + path: 'keybind-generator', loadComponent: () => - import('./pages/settings/user-settings/user-settings.component').then(m => m.UserSettingsComponent), + import('./pages/keybind-generator/keybind-generator.component').then(m => m.KeybindGeneratorComponent), + }, + { + path: 'recorder', + loadComponent: () => import('./pages/recorder/recorder.component').then(m => m.RecorderComponent), }, { - path: 'guilds/:guildId', - canActivate: [guildPermissionGuard], + path: 'settings', + loadComponent: () => import('./pages/settings/settings.component').then(m => m.SettingsComponent), children: [ { path: '', pathMatch: 'full', - redirectTo: 'settings', + redirectTo: 'user', }, { - path: 'settings', + path: 'user', loadComponent: () => - import('./pages/settings/guild-settings/guild-settings.component').then(m => m.GuildSettingsComponent), - canDeactivate: [canDeactivateGuildSettingsGuard], + import('./pages/settings/user-settings/user-settings.component').then(m => m.UserSettingsComponent), }, { - path: 'sounds', - loadComponent: () => - import('./pages/settings/sound-manager/sound-manager.component').then(m => m.SoundManagerComponent), - canDeactivate: [canDeactivateSoundManagerGuard], + path: 'guilds/:guildId', + canActivate: [guildPermissionGuard], + children: [ + { + path: '', + pathMatch: 'full', + redirectTo: 'settings', + }, + { + path: 'settings', + loadComponent: () => + import('./pages/settings/guild-settings/guild-settings.component').then( + m => m.GuildSettingsComponent, + ), + canDeactivate: [canDeactivateGuildSettingsGuard], + }, + { + path: 'sounds', + loadComponent: () => + import('./pages/settings/sound-manager/sound-manager.component').then(m => m.SoundManagerComponent), + canDeactivate: [canDeactivateSoundManagerGuard], + }, + ], }, ], }, diff --git a/frontend/src/app/common/header/header.component.html b/frontend/src/app/common/header/header.component.html index 5b27331..cb3fdad 100644 --- a/frontend/src/app/common/header/header.component.html +++ b/frontend/src/app/common/header/header.component.html @@ -1,9 +1,11 @@ @if (showSidenavToggle) { - + } - 📢 {{ pageTitle }} + 📢 {{ pageTitle }}
@@ -22,8 +24,14 @@
- settings Settings - keyboard Keybinds + + settings + Settings + + keyboard + Keybinds add Add bot to server + add + Add bot to server - +
@@ -45,9 +58,12 @@
- + speaker Soundboard - voicemail Recorder + + voicemail + Recorder diff --git a/frontend/src/app/pages/landing-page/landing-page.component.html b/frontend/src/app/pages/landing-page/landing-page.component.html new file mode 100644 index 0000000..8644f7a --- /dev/null +++ b/frontend/src/app/pages/landing-page/landing-page.component.html @@ -0,0 +1 @@ +Soundboard diff --git a/frontend/src/app/pages/landing-page/landing-page.component.scss b/frontend/src/app/pages/landing-page/landing-page.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/app/pages/landing-page/landing-page.component.ts b/frontend/src/app/pages/landing-page/landing-page.component.ts new file mode 100644 index 0000000..050f732 --- /dev/null +++ b/frontend/src/app/pages/landing-page/landing-page.component.ts @@ -0,0 +1,11 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { MatAnchor } from '@angular/material/button'; +import { RouterLink } from '@angular/router'; + +@Component({ + imports: [MatAnchor, RouterLink], + templateUrl: './landing-page.component.html', + styleUrl: './landing-page.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class LandingPageComponent {} diff --git a/frontend/src/app/pages/main-layout/main-layout.component.html b/frontend/src/app/pages/main-layout/main-layout.component.html new file mode 100644 index 0000000..18c5f80 --- /dev/null +++ b/frontend/src/app/pages/main-layout/main-layout.component.html @@ -0,0 +1,10 @@ + + @switch (apiService.user() != null) { + @case (true) { + + } + @case (false) { + + } + } + diff --git a/frontend/src/app/pages/main-layout/main-layout.component.scss b/frontend/src/app/pages/main-layout/main-layout.component.scss new file mode 100644 index 0000000..a6f99dd --- /dev/null +++ b/frontend/src/app/pages/main-layout/main-layout.component.scss @@ -0,0 +1,7 @@ +:host { + min-height: 100vh; + display: flex; + flex-direction: column; + max-width: 100vw; + overflow-x: hidden; +} diff --git a/frontend/src/app/pages/main-layout/main-layout.component.ts b/frontend/src/app/pages/main-layout/main-layout.component.ts new file mode 100644 index 0000000..f4ce9a7 --- /dev/null +++ b/frontend/src/app/pages/main-layout/main-layout.component.ts @@ -0,0 +1,31 @@ +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; +import { forkJoin, of, Subject } from 'rxjs'; +import { catchError } from 'rxjs/operators'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { RouterOutlet } from '@angular/router'; +import { ApiService, AppInfo, User } from '../../services/api.service'; +import { LoginComponent } from '../login/login.component'; +import { DataLoadDirective } from '../../common/data-load/data-load.directive'; + +@Component({ + imports: [RouterOutlet, LoginComponent, DataLoadDirective], + templateUrl: './main-layout.component.html', + styleUrl: './main-layout.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MainLayoutComponent { + protected apiService = inject(ApiService); + + readonly data$ = forkJoin([ + this.apiService.loadAppInfo(), + this.apiService.loadUser().pipe(catchError(() => of(null))), + ]); + readonly loadedData$ = new Subject<[AppInfo, User]>(); + + constructor() { + this.loadedData$.pipe(takeUntilDestroyed()).subscribe(data => { + this.apiService.appInfo.set(data[0]); + this.apiService.user.set(data[1]); + }); + } +} diff --git a/frontend/src/main.server.ts b/frontend/src/main.server.ts new file mode 100644 index 0000000..4b9d4d1 --- /dev/null +++ b/frontend/src/main.server.ts @@ -0,0 +1,7 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { AppComponent } from './app/app.component'; +import { config } from './app/app.config.server'; + +const bootstrap = () => bootstrapApplication(AppComponent, config); + +export default bootstrap; diff --git a/frontend/src/main.ts b/frontend/src/main.ts index 991a9a5..2896ea7 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -1,5 +1,5 @@ import { bootstrapApplication } from '@angular/platform-browser'; import { AppComponent } from './app/app.component'; -import { APP_CONFIG } from './app/app.config'; +import { appConfig } from './app/app.config'; -bootstrapApplication(AppComponent, APP_CONFIG).catch(err => console.error(err)); +bootstrapApplication(AppComponent, appConfig).catch(err => console.error(err)); diff --git a/frontend/tsconfig.app.json b/frontend/tsconfig.app.json index ff396d4..bc46605 100644 --- a/frontend/tsconfig.app.json +++ b/frontend/tsconfig.app.json @@ -5,6 +5,6 @@ "outDir": "./out-tsc/app", "types": [] }, - "files": ["src/main.ts", "src/polyfills.ts"], + "files": ["src/main.ts", "src/polyfills.ts", "src/main.server.ts"], "include": ["src/**/*.d.ts"] }