From 6317c97d96ae28bd8f3cb9e63abffd6e2747532e Mon Sep 17 00:00:00 2001 From: Jan Klattenhoff Date: Tue, 21 Jan 2025 09:31:43 +0100 Subject: [PATCH] feat: add Angular Material and refactor UI components --- angular.json | 4 +- package-lock.json | 41 ++++++++++- package.json | 4 +- src/app/app.config.ts | 3 +- .../create-link/create-link.component.html | 44 +++++------- src/app/create-link/create-link.component.ts | 45 ++++++++++-- src/app/dashboard/dashboard.component.html | 72 ++++++++++++------- src/app/dashboard/dashboard.component.ts | 9 ++- src/app/models/link.ts | 6 ++ src/app/navbar/navbar.component.html | 52 -------------- src/app/service/link.service.ts | 5 +- src/index.html | 4 +- src/styles.css | 3 - src/styles.scss | 22 ++++++ tailwind.config.js | 11 +-- 15 files changed, 194 insertions(+), 131 deletions(-) create mode 100644 src/app/models/link.ts delete mode 100644 src/styles.css create mode 100644 src/styles.scss diff --git a/angular.json b/angular.json index f2cd277..3f41ec4 100644 --- a/angular.json +++ b/angular.json @@ -27,7 +27,8 @@ } ], "styles": [ - "src/styles.css" + "@angular/material/prebuilt-themes/azure-blue.css", + "src/styles.scss" ], "scripts": [] }, @@ -97,6 +98,7 @@ } ], "styles": [ + "@angular/material/prebuilt-themes/azure-blue.css", "src/styles.css" ], "scripts": [] diff --git a/package-lock.json b/package-lock.json index d6e2024..6a63114 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,12 @@ "version": "0.0.0", "dependencies": { "@angular/animations": "^19.0.0", + "@angular/cdk": "^19.1.0", "@angular/common": "^19.0.0", "@angular/compiler": "^19.0.0", "@angular/core": "^19.0.0", "@angular/forms": "^19.0.0", + "@angular/material": "^19.1.0", "@angular/platform-browser": "^19.0.0", "@angular/platform-browser-dynamic": "^19.0.0", "@angular/router": "^19.0.0", @@ -390,6 +392,23 @@ } } }, + "node_modules/@angular/cdk": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-19.1.0.tgz", + "integrity": "sha512-h7VSaMA/vFHb7u1bwoHKl3L3mZLIcXNZw6v7Nei9ITfEo1PfSKbrYhleeqpNikzE+LxNDKJrbZtpAckSYHblmA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "optionalDependencies": { + "parse5": "^7.1.2" + }, + "peerDependencies": { + "@angular/common": "^19.0.0 || ^20.0.0", + "@angular/core": "^19.0.0 || ^20.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, "node_modules/@angular/cli": { "version": "19.1.2", "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-19.1.2.tgz", @@ -517,6 +536,24 @@ "rxjs": "^6.5.3 || ^7.4.0" } }, + "node_modules/@angular/material": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-19.1.0.tgz", + "integrity": "sha512-LTQBWtuRGjNpA7ceQu9PyiUUq0KLfBP8LvL04hLFbEsZ0fIPQ/OO/Otn67/7TMtnHRFnPeezYPHcAHBhiNlR4A==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/animations": "^19.0.0 || ^20.0.0", + "@angular/cdk": "19.1.0", + "@angular/common": "^19.0.0 || ^20.0.0", + "@angular/core": "^19.0.0 || ^20.0.0", + "@angular/forms": "^19.0.0 || ^20.0.0", + "@angular/platform-browser": "^19.0.0 || ^20.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, "node_modules/@angular/platform-browser": { "version": "19.1.1", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-19.1.1.tgz", @@ -6663,7 +6700,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.12" }, @@ -10387,7 +10424,7 @@ "version": "7.2.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", - "dev": true, + "devOptional": true, "dependencies": { "entities": "^4.5.0" }, diff --git a/package.json b/package.json index fb2e239..86546d4 100644 --- a/package.json +++ b/package.json @@ -11,10 +11,12 @@ "private": true, "dependencies": { "@angular/animations": "^19.0.0", + "@angular/cdk": "^19.1.0", "@angular/common": "^19.0.0", "@angular/compiler": "^19.0.0", "@angular/core": "^19.0.0", "@angular/forms": "^19.0.0", + "@angular/material": "^19.1.0", "@angular/platform-browser": "^19.0.0", "@angular/platform-browser-dynamic": "^19.0.0", "@angular/router": "^19.0.0", @@ -41,4 +43,4 @@ "tailwindcss": "^3.4.17", "typescript": "~5.6.2" } -} +} \ No newline at end of file diff --git a/src/app/app.config.ts b/src/app/app.config.ts index a1e7d6f..96116fd 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -2,7 +2,8 @@ import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; import { provideRouter } from '@angular/router'; import { routes } from './app.routes'; +import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; export const appConfig: ApplicationConfig = { - providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes)] + providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideAnimationsAsync()] }; diff --git a/src/app/create-link/create-link.component.html b/src/app/create-link/create-link.component.html index 73a1d3f..6bc479c 100644 --- a/src/app/create-link/create-link.component.html +++ b/src/app/create-link/create-link.component.html @@ -1,29 +1,19 @@ -
+
-
- -
-
-

Create link

- @if (requestFailed) { - - } -
- - -
-
- -
-
-
-
+ + +
+ + {{errorMessages['name']}} + Name + + + + {{errorMessages['link']}} + Link + + + +
+
diff --git a/src/app/create-link/create-link.component.ts b/src/app/create-link/create-link.component.ts index 7ba3c72..76c3dcf 100644 --- a/src/app/create-link/create-link.component.ts +++ b/src/app/create-link/create-link.component.ts @@ -1,29 +1,66 @@ import { Component } from '@angular/core'; import { NavbarComponent } from '../navbar/navbar.component'; -import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; +import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; import { LinkService } from '../service/link.service'; import { Router } from '@angular/router'; +import { MatCardModule } from '@angular/material/card'; +import { MatInputModule, MatLabel } from '@angular/material/input'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatButtonModule } from '@angular/material/button'; +import { debounceTime } from 'rxjs'; @Component({ selector: 'app-create-link', - imports: [NavbarComponent, ReactiveFormsModule], + imports: [NavbarComponent, ReactiveFormsModule, MatCardModule, MatFormFieldModule, MatInputModule, MatButtonModule], templateUrl: './create-link.component.html', styleUrl: './create-link.component.css' }) export class CreateLinkComponent { public createLinkForm!: FormGroup; public requestFailed: boolean = false; + public errorMessages: Record = {}; constructor(private linkService: LinkService, private router: Router) { } + private validationErrorMessages: Record = { + required: "This field is required", + pattern: "This must be a valid url", + }; + + updateErrorMessages(): void { + this.errorMessages = {}; + + Object.keys(this.createLinkForm.controls).forEach(field => { + const control = this.createLinkForm.get(field); + + if (control && control.errors) { + this.errorMessages[field] = Object.keys(control.errors) + .map(errorKey => this.validationErrorMessages[errorKey] || `Unknown error: ${errorKey}`) + .join(' '); + } + }); + } + + back() { + this.router.navigate(['dashboard']); + } + ngOnInit(): void { this.createLinkForm = new FormGroup({ - name: new FormControl(''), - link: new FormControl(''), + name: new FormControl('', Validators.required), + link: new FormControl('', [Validators.required, Validators.pattern(/((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)/)]), + }); + this.createLinkForm.valueChanges.subscribe(() => { + this.updateErrorMessages(); }); } submit() { + + if (!this.createLinkForm.valid) { + return; + } + this.requestFailed = false; this.linkService.createLink({ name: this.createLinkForm.get('name')?.value, diff --git a/src/app/dashboard/dashboard.component.html b/src/app/dashboard/dashboard.component.html index 2caa0c5..66e4837 100644 --- a/src/app/dashboard/dashboard.component.html +++ b/src/app/dashboard/dashboard.component.html @@ -1,32 +1,50 @@
- - - - - - - - - - - - - - - @for (link of links; track link) { - - - - - - - - } - -
NameUrlShort UrlActions
{{link.id}}{{link.name}}{{link.link}}ndy - -
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ID {{element.id}} Name {{element.name}} Link {{element.link}} Short Url ndy Actions + +
+
+
diff --git a/src/app/dashboard/dashboard.component.ts b/src/app/dashboard/dashboard.component.ts index ab639a2..f792206 100644 --- a/src/app/dashboard/dashboard.component.ts +++ b/src/app/dashboard/dashboard.component.ts @@ -3,15 +3,20 @@ import { LinkService } from '../service/link.service'; import { RecordModel } from 'pocketbase'; import { NavbarComponent } from '../navbar/navbar.component'; import { Router, RouterLink } from '@angular/router'; +import { MatRow, MatTableModule } from '@angular/material/table'; +import { MatCardModule } from '@angular/material/card'; +import { Link } from '../models/link'; +import { MatButtonModule } from '@angular/material/button'; @Component({ selector: 'app-dashboard', - imports: [NavbarComponent], + imports: [NavbarComponent, MatTableModule, MatCardModule, MatButtonModule], templateUrl: './dashboard.component.html', styleUrl: './dashboard.component.css' }) export class DashboardComponent { - public links: any[] = []; + public links: Link[] = []; + displayedColumns: string[] = ['id', 'name', 'link', 'symbol', 'actions']; constructor(private linkService: LinkService, private router: Router) { }; diff --git a/src/app/models/link.ts b/src/app/models/link.ts new file mode 100644 index 0000000..0f961c3 --- /dev/null +++ b/src/app/models/link.ts @@ -0,0 +1,6 @@ +export interface Link { + id: string; + link: string; + name: string; + owner: string; +} diff --git a/src/app/navbar/navbar.component.html b/src/app/navbar/navbar.component.html index 1d6d14c..e69de29 100644 --- a/src/app/navbar/navbar.component.html +++ b/src/app/navbar/navbar.component.html @@ -1,52 +0,0 @@ - diff --git a/src/app/service/link.service.ts b/src/app/service/link.service.ts index fa08a5a..fcd0537 100644 --- a/src/app/service/link.service.ts +++ b/src/app/service/link.service.ts @@ -1,6 +1,7 @@ import { Injectable } from "@angular/core"; import { environment } from "../../environments/environment"; import PocketBase, { RecordModel } from 'pocketbase'; +import { Link } from "../models/link"; @Injectable({ @@ -9,8 +10,8 @@ import PocketBase, { RecordModel } from 'pocketbase'; export class LinkService { private pb = new PocketBase(environment.POCKETBASE); - getLinks(): Promise { - return this.pb.collection('links').getFullList(); + getLinks(): Promise { + return this.pb.collection('links').getFullList(); } deleteLink(id: string) { diff --git a/src/index.html b/src/index.html index c660faa..5ab9cdc 100644 --- a/src/index.html +++ b/src/index.html @@ -6,8 +6,10 @@ + + - + diff --git a/src/styles.css b/src/styles.css deleted file mode 100644 index b5c61c9..0000000 --- a/src/styles.css +++ /dev/null @@ -1,3 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; diff --git a/src/styles.scss b/src/styles.scss new file mode 100644 index 0000000..2b30ff0 --- /dev/null +++ b/src/styles.scss @@ -0,0 +1,22 @@ +@use '@angular/material' as mat; +@tailwind base; +@tailwind components; +@tailwind utilities; + +$theme: mat.define-theme(); + +html { + @include mat.all-component-themes($theme); + // This line allows you to use color="..." for your toolbar. + @include mat.color-variants-backwards-compatibility($theme); +} + +html, +body { + height: 100%; +} + +body { + margin: 0; + font-family: Roboto, "Helvetica Neue", sans-serif; +} diff --git a/tailwind.config.js b/tailwind.config.js index a57d5cf..f24e856 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -6,14 +6,9 @@ module.exports = { theme: { extend: {}, }, - daisyui: { - styled: true, - themes: true, - base: true, - utils: true, - logs: true, - rtl: false + corePlugins: { + preflight: false, }, - plugins: [require("@tailwindcss/typography"), require("daisyui")], + plugins: [], }