diff --git a/.mcp.json b/.mcp.json index c041517..592ba64 100644 --- a/.mcp.json +++ b/.mcp.json @@ -1,8 +1,8 @@ { - "mcpServers": { - "angular-cli": { - "command": "npx", - "args": ["-y", "@angular/cli", "mcp"] + "mcpServers": { + "angular-cli": { + "command": "npx", + "args": ["-y", "@angular/cli", "mcp"] + } } - } } diff --git a/.prettierrc b/.prettierrc index d6c16d7..749fde2 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,12 +1,12 @@ { - "printWidth": 100, - "singleQuote": true, - "overrides": [ - { - "files": "*.html", - "options": { - "parser": "angular" - } - } - ] + "printWidth": 100, + "singleQuote": true, + "overrides": [ + { + "files": "*.html", + "options": { + "parser": "angular" + } + } + ] } diff --git a/.vscode/launch.json b/.vscode/launch.json index 925af83..0ab27bc 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,20 +1,20 @@ { - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "ng serve", - "type": "chrome", - "request": "launch", - "preLaunchTask": "npm: start", - "url": "http://localhost:4200/" - }, - { - "name": "ng test", - "type": "chrome", - "request": "launch", - "preLaunchTask": "npm: test", - "url": "http://localhost:9876/debug.html" - } - ] + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "ng serve", + "type": "chrome", + "request": "launch", + "preLaunchTask": "npm: start", + "url": "http://localhost:4200/" + }, + { + "name": "ng test", + "type": "chrome", + "request": "launch", + "preLaunchTask": "npm: test", + "url": "http://localhost:9876/debug.html" + } + ] } diff --git a/.vscode/mcp.json b/.vscode/mcp.json index 475d0ac..b55c27d 100644 --- a/.vscode/mcp.json +++ b/.vscode/mcp.json @@ -1,8 +1,8 @@ { - "servers": { - "angular-cli": { - "command": "npx", - "args": ["-y", "@angular/cli", "mcp"] + "servers": { + "angular-cli": { + "command": "npx", + "args": ["-y", "@angular/cli", "mcp"] + } } - } } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 244306f..9b9e74e 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,42 +1,42 @@ { - // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558 - "version": "2.0.0", - "tasks": [ - { - "type": "npm", - "script": "start", - "isBackground": true, - "problemMatcher": { - "owner": "typescript", - "pattern": "$tsc", - "background": { - "activeOnStart": true, - "beginsPattern": { - "regexp": "Changes detected" - }, - "endsPattern": { - "regexp": "bundle generation (complete|failed)" - } + // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558 + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "start", + "isBackground": true, + "problemMatcher": { + "owner": "typescript", + "pattern": "$tsc", + "background": { + "activeOnStart": true, + "beginsPattern": { + "regexp": "Changes detected" + }, + "endsPattern": { + "regexp": "bundle generation (complete|failed)" + } + } + } + }, + { + "type": "npm", + "script": "test", + "isBackground": true, + "problemMatcher": { + "owner": "typescript", + "pattern": "$tsc", + "background": { + "activeOnStart": true, + "beginsPattern": { + "regexp": "Changes detected" + }, + "endsPattern": { + "regexp": "bundle generation (complete|failed)" + } + } + } } - } - }, - { - "type": "npm", - "script": "test", - "isBackground": true, - "problemMatcher": { - "owner": "typescript", - "pattern": "$tsc", - "background": { - "activeOnStart": true, - "beginsPattern": { - "regexp": "Changes detected" - }, - "endsPattern": { - "regexp": "bundle generation (complete|failed)" - } - } - } - } - ] + ] } diff --git a/CLAUDE.md b/CLAUDE.md index a3c3f95..4d2341a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -32,7 +32,7 @@ Delivery plan: after the first release the app ships as ONE Docker container (An - Implement lazy loading for feature routes - Do NOT use the `@HostBinding` and `@HostListener` decorators. Put host bindings inside the `host` object of the `@Component` or `@Directive` decorator instead - Use `NgOptimizedImage` for all static images. - - `NgOptimizedImage` does not work for inline base64 images. + - `NgOptimizedImage` does not work for inline base64 images. ## Accessibility Requirements diff --git a/angular.json b/angular.json index fa41d85..4277eb1 100644 --- a/angular.json +++ b/angular.json @@ -1,81 +1,79 @@ { - "$schema": "./node_modules/@angular/cli/lib/config/schema.json", - "version": 1, - "cli": { - "packageManager": "npm" - }, - "newProjectRoot": "projects", - "projects": { - "pchelpcrm": { - "projectType": "application", - "schematics": {}, - "root": "", - "sourceRoot": "src", - "prefix": "app", - "architect": { - "build": { - "builder": "@angular/build:application", - "options": { - "browser": "src/main.ts", - "tsConfig": "tsconfig.app.json", - "assets": [ - { - "glob": "**/*", - "input": "public" - } - ], - "styles": [ - "src/styles.css" - ], - "server": "src/main.server.ts", - "outputMode": "server", - "security": { - "allowedHosts": [] - }, - "ssr": { - "entry": "src/server.ts" - } - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kB", - "maximumError": "1MB" + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "cli": { + "packageManager": "npm" + }, + "newProjectRoot": "projects", + "projects": { + "pchelpcrm": { + "projectType": "application", + "schematics": {}, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular/build:application", + "options": { + "browser": "src/main.ts", + "tsConfig": "tsconfig.app.json", + "assets": [ + { + "glob": "**/*", + "input": "public" + } + ], + "styles": ["src/styles.css"], + "server": "src/main.server.ts", + "outputMode": "server", + "security": { + "allowedHosts": [] + }, + "ssr": { + "entry": "src/server.ts" + } + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kB", + "maximumError": "1MB" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "4kB", + "maximumError": "8kB" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" }, - { - "type": "anyComponentStyle", - "maximumWarning": "4kB", - "maximumError": "8kB" + "serve": { + "builder": "@angular/build:dev-server", + "configurations": { + "production": { + "buildTarget": "pchelpcrm:build:production" + }, + "development": { + "buildTarget": "pchelpcrm:build:development" + } + }, + "defaultConfiguration": "development" + }, + "test": { + "builder": "@angular/build:unit-test" } - ], - "outputHashing": "all" - }, - "development": { - "optimization": false, - "extractLicenses": false, - "sourceMap": true } - }, - "defaultConfiguration": "production" - }, - "serve": { - "builder": "@angular/build:dev-server", - "configurations": { - "production": { - "buildTarget": "pchelpcrm:build:production" - }, - "development": { - "buildTarget": "pchelpcrm:build:development" - } - }, - "defaultConfiguration": "development" - }, - "test": { - "builder": "@angular/build:unit-test" } - } } - } } diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index dc8c1bc..ad53e9d 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,5 +1,5 @@ allowBuilds: - '@parcel/watcher': true - esbuild: true - lmdb: true - msgpackr-extract: true + '@parcel/watcher': true + esbuild: true + lmdb: true + msgpackr-extract: true diff --git a/scripts/pb-superuser.mjs b/scripts/pb-superuser.mjs index 51a061e..6577ba5 100644 --- a/scripts/pb-superuser.mjs +++ b/scripts/pb-superuser.mjs @@ -5,8 +5,10 @@ const { PB_SUPERUSER_EMAIL: email, PB_SUPERUSER_PASSWORD: password } = process.e const dir = process.env.PB_DIR || 'pocketbase/pb_data'; if (!email || !password) { - console.error('PB_SUPERUSER_EMAIL and PB_SUPERUSER_PASSWORD must be set (see .env.example)'); - process.exit(1); + console.error('PB_SUPERUSER_EMAIL and PB_SUPERUSER_PASSWORD must be set (see .env.example)'); + process.exit(1); } -execFileSync('pocketbase', ['superuser', 'upsert', email, password, '--dir', dir], { stdio: 'inherit' }); +execFileSync('pocketbase', ['superuser', 'upsert', email, password, '--dir', dir], { + stdio: 'inherit', +}); diff --git a/src/app/app.config.server.ts b/src/app/app.config.server.ts index 41031f1..98fb421 100644 --- a/src/app/app.config.server.ts +++ b/src/app/app.config.server.ts @@ -4,9 +4,7 @@ import { appConfig } from './app.config'; import { serverRoutes } from './app.routes.server'; const serverConfig: ApplicationConfig = { - providers: [ - provideServerRendering(withRoutes(serverRoutes)) - ] + providers: [provideServerRendering(withRoutes(serverRoutes))], }; export const config = mergeApplicationConfig(appConfig, serverConfig); diff --git a/src/app/app.config.ts b/src/app/app.config.ts index 10e386d..2c069de 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -4,8 +4,9 @@ import { routes } from './app.routes'; import { provideClientHydration } from '@angular/platform-browser'; export const appConfig: ApplicationConfig = { - providers: [ - provideBrowserGlobalErrorListeners(), - provideRouter(routes), provideClientHydration() - ] + providers: [ + provideBrowserGlobalErrorListeners(), + provideRouter(routes), + provideClientHydration(), + ], }; diff --git a/src/app/app.html b/src/app/app.html index a1c4296..68c1dc0 100644 --- a/src/app/app.html +++ b/src/app/app.html @@ -8,328 +8,345 @@
-
-
- -

Hello, {{ title() }}

-

Congratulations! Your app is running. 🎉

-
- -
-
- @for (item of [ - { title: 'Explore the Docs', link: 'https://angular.dev' }, - { title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' }, - { title: 'Prompt and best practices for AI', link: 'https://angular.dev/ai/develop-with-ai'}, - { title: 'CLI Docs', link: 'https://angular.dev/tools/cli' }, - { title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' }, - { title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' }, - ]; track item.title) { - - {{ item.title }} +
+ - +

Hello, {{ title() }}

+

Congratulations! Your app is running. 🎉

+
+ +
+
+ @for ( + item of [ + { title: 'Explore the Docs', link: 'https://angular.dev' }, + { title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' }, + { + title: 'Prompt and best practices for AI', + link: 'https://angular.dev/ai/develop-with-ai', + }, + { title: 'CLI Docs', link: 'https://angular.dev/tools/cli' }, + { + title: 'Angular Language Service', + link: 'https://angular.dev/tools/language-service', + }, + { title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' }, + ]; + track item.title + ) { + + {{ item.title }} + + + + + } +
+ +
-
@@ -340,5 +357,4 @@ - diff --git a/src/app/app.routes.server.ts b/src/app/app.routes.server.ts index ffd37b1..a911aa8 100644 --- a/src/app/app.routes.server.ts +++ b/src/app/app.routes.server.ts @@ -1,8 +1,8 @@ import { RenderMode, ServerRoute } from '@angular/ssr'; export const serverRoutes: ServerRoute[] = [ - { - path: '**', - renderMode: RenderMode.Prerender - } + { + path: '**', + renderMode: RenderMode.Prerender, + }, ]; diff --git a/src/app/app.spec.ts b/src/app/app.spec.ts index e8e550b..8c3a91b 100644 --- a/src/app/app.spec.ts +++ b/src/app/app.spec.ts @@ -2,23 +2,22 @@ import { TestBed } from '@angular/core/testing'; import { App } from './app'; describe('App', () => { - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [App], - }) - .compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [App], + }).compileComponents(); + }); - it('should create the app', () => { - const fixture = TestBed.createComponent(App); - const app = fixture.componentInstance; - expect(app).toBeTruthy(); - }); + it('should create the app', () => { + const fixture = TestBed.createComponent(App); + const app = fixture.componentInstance; + expect(app).toBeTruthy(); + }); - it('should render title', async () => { - const fixture = TestBed.createComponent(App); - await fixture.whenStable(); - const compiled = fixture.nativeElement as HTMLElement; - expect(compiled.querySelector('h1')?.textContent).toContain('Hello, pchelpcrm'); - }); + it('should render title', async () => { + const fixture = TestBed.createComponent(App); + await fixture.whenStable(); + const compiled = fixture.nativeElement as HTMLElement; + expect(compiled.querySelector('h1')?.textContent).toContain('Hello, pchelpcrm'); + }); }); diff --git a/src/app/app.ts b/src/app/app.ts index 541b432..d248b7a 100644 --- a/src/app/app.ts +++ b/src/app/app.ts @@ -2,11 +2,11 @@ import { Component, signal } from '@angular/core'; import { RouterOutlet } from '@angular/router'; @Component({ - imports: [RouterOutlet], - selector: 'app-root', - styleUrl: './app.css', - templateUrl: './app.html', + imports: [RouterOutlet], + selector: 'app-root', + styleUrl: './app.css', + templateUrl: './app.html', }) export class App { - protected readonly title = signal('pchelpcrm'); + protected readonly title = signal('pchelpcrm'); } diff --git a/src/index.html b/src/index.html index 85b7b92..882aba3 100644 --- a/src/index.html +++ b/src/index.html @@ -1,13 +1,13 @@ - - - Pchelpcrm - - - - - - - + + + Pchelpcrm + + + + + + + diff --git a/src/main.server.ts b/src/main.server.ts index 723e001..9b1aba7 100644 --- a/src/main.server.ts +++ b/src/main.server.ts @@ -2,7 +2,6 @@ import { BootstrapContext, bootstrapApplication } from '@angular/platform-browse import { App } from './app/app'; import { config } from './app/app.config.server'; -const bootstrap = (context: BootstrapContext) => - bootstrapApplication(App, config, context); +const bootstrap = (context: BootstrapContext) => bootstrapApplication(App, config, context); export default bootstrap; diff --git a/src/main.ts b/src/main.ts index 5df75f9..190f341 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,5 +2,4 @@ import { bootstrapApplication } from '@angular/platform-browser'; import { appConfig } from './app/app.config'; import { App } from './app/app'; -bootstrapApplication(App, appConfig) - .catch((err) => console.error(err)); +bootstrapApplication(App, appConfig).catch((err) => console.error(err)); diff --git a/src/server.ts b/src/server.ts index 4bf0631..550d05d 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,8 +1,8 @@ import { - AngularNodeAppEngine, - createNodeRequestHandler, - isMainModule, - writeResponseToNodeResponse, + AngularNodeAppEngine, + createNodeRequestHandler, + isMainModule, + writeResponseToNodeResponse, } from '@angular/ssr/node'; import express from 'express'; import { join } from 'node:path'; @@ -28,23 +28,21 @@ const angularApp = new AngularNodeAppEngine(); * Serve static files from /browser */ app.use( - express.static(browserDistFolder, { - maxAge: '1y', - index: false, - redirect: false, - }), + express.static(browserDistFolder, { + maxAge: '1y', + index: false, + redirect: false, + }), ); /** * Handle all other requests by rendering the Angular application. */ app.use((req, res, next) => { - angularApp - .handle(req) - .then((response) => - response ? writeResponseToNodeResponse(response, res) : next(), - ) - .catch(next); + angularApp + .handle(req) + .then((response) => (response ? writeResponseToNodeResponse(response, res) : next())) + .catch(next); }); /** @@ -52,14 +50,14 @@ app.use((req, res, next) => { * The server listens on the port defined by the `PORT` environment variable, or defaults to 4000. */ if (isMainModule(import.meta.url) || process.env['pm_id']) { - const port = process.env['PORT'] || 4000; - app.listen(port, (error) => { - if (error) { - throw error; - } + const port = process.env['PORT'] || 4000; + app.listen(port, (error) => { + if (error) { + throw error; + } - console.log(`Node Express server listening on http://localhost:${port}`); - }); + console.log(`Node Express server listening on http://localhost:${port}`); + }); } /** diff --git a/tsconfig.app.json b/tsconfig.app.json index 11c10da..8d08449 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -1,16 +1,10 @@ /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ { - "extends": "./tsconfig.json", - "compilerOptions": { - "types": [ - "node" - ] - }, - "include": [ - "src/**/*.ts" - ], - "exclude": [ - "src/**/*.spec.ts" - ] + "extends": "./tsconfig.json", + "compilerOptions": { + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.spec.ts"] } diff --git a/tsconfig.json b/tsconfig.json index d2fbb9c..22182b3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,31 +1,31 @@ /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ { - "compileOnSave": false, - "compilerOptions": { - "noImplicitOverride": true, - "noPropertyAccessFromIndexSignature": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "skipLibCheck": true, - "isolatedModules": true, - "experimentalDecorators": true, - "importHelpers": true, - "target": "ES2022", - "module": "preserve" - }, - "angularCompilerOptions": { - "enableI18nLegacyMessageIdFormat": false, - "strictInjectionParameters": true, - "strictInputAccessModifiers": true - }, - "files": [], - "references": [ - { - "path": "./tsconfig.app.json" + "compileOnSave": false, + "compilerOptions": { + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "isolatedModules": true, + "experimentalDecorators": true, + "importHelpers": true, + "target": "ES2022", + "module": "preserve" }, - { - "path": "./tsconfig.spec.json" - } - ] + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true + }, + "files": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] } diff --git a/tsconfig.spec.json b/tsconfig.spec.json index 9c8efb9..23d738d 100644 --- a/tsconfig.spec.json +++ b/tsconfig.spec.json @@ -1,14 +1,9 @@ /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ { - "extends": "./tsconfig.json", - "compilerOptions": { - "types": [ - "vitest/globals" - ] - }, - "include": [ - "src/**/*.d.ts", - "src/**/*.spec.ts" - ] + "extends": "./tsconfig.json", + "compilerOptions": { + "types": ["vitest/globals"] + }, + "include": ["src/**/*.d.ts", "src/**/*.spec.ts"] }