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) {
-
-
-
Something went wrong
-
- }
-
-
-
-
-
-
-
+
+
+
+
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 @@
-
-
-
-
-
- |
- Name |
- Url |
- Short Url |
- Actions |
-
-
-
-
- @for (link of links; track link) {
-
- {{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: [],
}