style: format code and add prettier configuration files
This commit is contained in:
		
					parent
					
						
							
								3e0b17ebe6
							
						
					
				
			
			
				commit
				
					
						395c1bc75f
					
				
			
		
					 39 changed files with 338 additions and 207 deletions
				
			
		| 
						 | 
					@ -29,4 +29,3 @@ jobs:
 | 
				
			||||||
        with:
 | 
					        with:
 | 
				
			||||||
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 | 
					          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 | 
				
			||||||
          GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
 | 
					          GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										3
									
								
								.prettierignore
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.prettierignore
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					# Ignore artifacts:
 | 
				
			||||||
 | 
					build
 | 
				
			||||||
 | 
					coverage
 | 
				
			||||||
							
								
								
									
										1
									
								
								.prettierrc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.prettierrc
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					{}
 | 
				
			||||||
| 
						 | 
					@ -16,9 +16,7 @@
 | 
				
			||||||
            "outputPath": "dist/jklink",
 | 
					            "outputPath": "dist/jklink",
 | 
				
			||||||
            "index": "src/index.html",
 | 
					            "index": "src/index.html",
 | 
				
			||||||
            "browser": "src/main.ts",
 | 
					            "browser": "src/main.ts",
 | 
				
			||||||
            "polyfills": [
 | 
					            "polyfills": ["zone.js"],
 | 
				
			||||||
              "zone.js"
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            "tsConfig": "tsconfig.app.json",
 | 
					            "tsConfig": "tsconfig.app.json",
 | 
				
			||||||
            "assets": [
 | 
					            "assets": [
 | 
				
			||||||
              {
 | 
					              {
 | 
				
			||||||
| 
						 | 
					@ -86,10 +84,7 @@
 | 
				
			||||||
        "test": {
 | 
					        "test": {
 | 
				
			||||||
          "builder": "@angular-devkit/build-angular:karma",
 | 
					          "builder": "@angular-devkit/build-angular:karma",
 | 
				
			||||||
          "options": {
 | 
					          "options": {
 | 
				
			||||||
            "polyfills": [
 | 
					            "polyfills": ["zone.js", "zone.js/testing"],
 | 
				
			||||||
              "zone.js",
 | 
					 | 
				
			||||||
              "zone.js/testing"
 | 
					 | 
				
			||||||
            ],
 | 
					 | 
				
			||||||
            "tsConfig": "tsconfig.spec.json",
 | 
					            "tsConfig": "tsconfig.spec.json",
 | 
				
			||||||
            "assets": [
 | 
					            "assets": [
 | 
				
			||||||
              {
 | 
					              {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										17
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										17
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							| 
						 | 
					@ -38,6 +38,7 @@
 | 
				
			||||||
        "karma-jasmine": "~5.1.0",
 | 
					        "karma-jasmine": "~5.1.0",
 | 
				
			||||||
        "karma-jasmine-html-reporter": "~2.1.0",
 | 
					        "karma-jasmine-html-reporter": "~2.1.0",
 | 
				
			||||||
        "postcss": "^8.5.1",
 | 
					        "postcss": "^8.5.1",
 | 
				
			||||||
 | 
					        "prettier": "3.4.2",
 | 
				
			||||||
        "tailwindcss": "^3.4.17",
 | 
					        "tailwindcss": "^3.4.17",
 | 
				
			||||||
        "typescript": "~5.6.2"
 | 
					        "typescript": "~5.6.2"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
| 
						 | 
					@ -10845,6 +10846,22 @@
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
 | 
				
			||||||
      "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
 | 
					      "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/prettier": {
 | 
				
			||||||
 | 
					      "version": "3.4.2",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==",
 | 
				
			||||||
 | 
					      "dev": true,
 | 
				
			||||||
 | 
					      "license": "MIT",
 | 
				
			||||||
 | 
					      "bin": {
 | 
				
			||||||
 | 
					        "prettier": "bin/prettier.cjs"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "node": ">=14"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "funding": {
 | 
				
			||||||
 | 
					        "url": "https://github.com/prettier/prettier?sponsor=1"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/proc-log": {
 | 
					    "node_modules/proc-log": {
 | 
				
			||||||
      "version": "5.0.0",
 | 
					      "version": "5.0.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -40,6 +40,7 @@
 | 
				
			||||||
    "karma-jasmine": "~5.1.0",
 | 
					    "karma-jasmine": "~5.1.0",
 | 
				
			||||||
    "karma-jasmine-html-reporter": "~2.1.0",
 | 
					    "karma-jasmine-html-reporter": "~2.1.0",
 | 
				
			||||||
    "postcss": "^8.5.1",
 | 
					    "postcss": "^8.5.1",
 | 
				
			||||||
 | 
					    "prettier": "3.4.2",
 | 
				
			||||||
    "tailwindcss": "^3.4.17",
 | 
					    "tailwindcss": "^3.4.17",
 | 
				
			||||||
    "typescript": "~5.6.2"
 | 
					    "typescript": "~5.6.2"
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,14 @@
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
  branches: ['prod'],
 | 
					  branches: ["prod"],
 | 
				
			||||||
  plugins: [
 | 
					  plugins: [
 | 
				
			||||||
    '@semantic-release/commit-analyzer',
 | 
					    "@semantic-release/commit-analyzer",
 | 
				
			||||||
    '@semantic-release/release-notes-generator',
 | 
					    "@semantic-release/release-notes-generator",
 | 
				
			||||||
    '@semantic-release/changelog',
 | 
					    "@semantic-release/changelog",
 | 
				
			||||||
      ["@saithodev/semantic-release-gitea", {
 | 
					    [
 | 
				
			||||||
      "giteaUrl": "https://git.kjan.de"
 | 
					      "@saithodev/semantic-release-gitea",
 | 
				
			||||||
    }],
 | 
					      {
 | 
				
			||||||
 | 
					        giteaUrl: "https://git.kjan.de",
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,6 +24,8 @@ describe('AppComponent', () => {
 | 
				
			||||||
    const fixture = TestBed.createComponent(AppComponent);
 | 
					    const fixture = TestBed.createComponent(AppComponent);
 | 
				
			||||||
    fixture.detectChanges();
 | 
					    fixture.detectChanges();
 | 
				
			||||||
    const compiled = fixture.nativeElement as HTMLElement;
 | 
					    const compiled = fixture.nativeElement as HTMLElement;
 | 
				
			||||||
    expect(compiled.querySelector('h1')?.textContent).toContain('Hello, jklink');
 | 
					    expect(compiled.querySelector('h1')?.textContent).toContain(
 | 
				
			||||||
 | 
					      'Hello, jklink',
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,6 @@ import { RouterOutlet } from '@angular/router';
 | 
				
			||||||
  selector: 'app-root',
 | 
					  selector: 'app-root',
 | 
				
			||||||
  imports: [RouterOutlet],
 | 
					  imports: [RouterOutlet],
 | 
				
			||||||
  templateUrl: './app.component.html',
 | 
					  templateUrl: './app.component.html',
 | 
				
			||||||
  styleUrl: './app.component.css'
 | 
					  styleUrl: './app.component.css',
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class AppComponent {
 | 
					export class AppComponent {}
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,5 +5,9 @@ import { routes } from './app.routes';
 | 
				
			||||||
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
 | 
					import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const appConfig: ApplicationConfig = {
 | 
					export const appConfig: ApplicationConfig = {
 | 
				
			||||||
  providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideAnimationsAsync()]
 | 
					  providers: [
 | 
				
			||||||
 | 
					    provideZoneChangeDetection({ eventCoalescing: true }),
 | 
				
			||||||
 | 
					    provideRouter(routes),
 | 
				
			||||||
 | 
					    provideAnimationsAsync(),
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,7 +25,7 @@ export const routes: Routes = [
 | 
				
			||||||
    component: ViewLinkComponent,
 | 
					    component: ViewLinkComponent,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    path: "**",
 | 
					    path: '**',
 | 
				
			||||||
    redirectTo: "",
 | 
					    redirectTo: '',
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,9 +8,8 @@ describe('VisitLinkConfirmationComponent', () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      imports: [ConfirmationModalComponent]
 | 
					      imports: [ConfirmationModalComponent],
 | 
				
			||||||
    })
 | 
					    }).compileComponents();
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fixture = TestBed.createComponent(ConfirmationModalComponent);
 | 
					    fixture = TestBed.createComponent(ConfirmationModalComponent);
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,9 +12,14 @@ import { ConfirmationModalDto } from '../models/confirmation-modal';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-confirmation-modal',
 | 
					  selector: 'app-confirmation-modal',
 | 
				
			||||||
  imports: [MatDialogTitle, MatDialogContent, MatDialogActions, MatButtonModule],
 | 
					  imports: [
 | 
				
			||||||
 | 
					    MatDialogTitle,
 | 
				
			||||||
 | 
					    MatDialogContent,
 | 
				
			||||||
 | 
					    MatDialogActions,
 | 
				
			||||||
 | 
					    MatButtonModule,
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
  templateUrl: './confirmation-modal.component.html',
 | 
					  templateUrl: './confirmation-modal.component.html',
 | 
				
			||||||
  styleUrl: './confirmation-modal.component.css'
 | 
					  styleUrl: './confirmation-modal.component.css',
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class ConfirmationModalComponent {
 | 
					export class ConfirmationModalComponent {
 | 
				
			||||||
  public data: ConfirmationModalDto = inject(MAT_DIALOG_DATA);
 | 
					  public data: ConfirmationModalDto = inject(MAT_DIALOG_DATA);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,17 +1,24 @@
 | 
				
			||||||
<div class="mx-auto container">
 | 
					<div class="mx-auto container">
 | 
				
			||||||
  <app-navbar></app-navbar>
 | 
					  <app-navbar></app-navbar>
 | 
				
			||||||
  <button mat-flat-button class="mt-3" color="warn" (click)="back()">Back</button>
 | 
					  <button mat-flat-button class="mt-3" color="warn" (click)="back()">
 | 
				
			||||||
 | 
					    Back
 | 
				
			||||||
 | 
					  </button>
 | 
				
			||||||
  <mat-card class="mt-3 p-3" appearance="outlined">
 | 
					  <mat-card class="mt-3 p-3" appearance="outlined">
 | 
				
			||||||
    <form class="flex flex-col" [formGroup]="createLinkForm">
 | 
					    <form class="flex flex-col" [formGroup]="createLinkForm">
 | 
				
			||||||
      <mat-form-field appearance="outline">
 | 
					      <mat-form-field appearance="outline">
 | 
				
			||||||
        <mat-error>{{errorMessages['name']}}</mat-error>
 | 
					        <mat-error>{{ errorMessages["name"] }}</mat-error>
 | 
				
			||||||
        <mat-label>Name</mat-label>
 | 
					        <mat-label>Name</mat-label>
 | 
				
			||||||
        <input formControlName="name" matInput placeholder="My Awesome link">
 | 
					        <input formControlName="name" matInput placeholder="My Awesome link" />
 | 
				
			||||||
      </mat-form-field>
 | 
					      </mat-form-field>
 | 
				
			||||||
      <mat-form-field appearance="outline">
 | 
					      <mat-form-field appearance="outline">
 | 
				
			||||||
        <mat-error>{{errorMessages['link']}}</mat-error>
 | 
					        <mat-error>{{ errorMessages["link"] }}</mat-error>
 | 
				
			||||||
        <mat-label>Link</mat-label>
 | 
					        <mat-label>Link</mat-label>
 | 
				
			||||||
        <input formControlName="link" matInput type="url" placeholder="https://kjan.de">
 | 
					        <input
 | 
				
			||||||
 | 
					          formControlName="link"
 | 
				
			||||||
 | 
					          matInput
 | 
				
			||||||
 | 
					          type="url"
 | 
				
			||||||
 | 
					          placeholder="https://kjan.de"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
      </mat-form-field>
 | 
					      </mat-form-field>
 | 
				
			||||||
      <button mat-flat-button type="submit" (click)="submit()">Create</button>
 | 
					      <button mat-flat-button type="submit" (click)="submit()">Create</button>
 | 
				
			||||||
    </form>
 | 
					    </form>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,9 +8,8 @@ describe('CreateLinkComponent', () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      imports: [CreateLinkComponent]
 | 
					      imports: [CreateLinkComponent],
 | 
				
			||||||
    })
 | 
					    }).compileComponents();
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fixture = TestBed.createComponent(CreateLinkComponent);
 | 
					    fixture = TestBed.createComponent(CreateLinkComponent);
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,11 @@
 | 
				
			||||||
import { Component } from '@angular/core';
 | 
					import { Component } from '@angular/core';
 | 
				
			||||||
import { NavbarComponent } from '../navbar/navbar.component';
 | 
					import { NavbarComponent } from '../navbar/navbar.component';
 | 
				
			||||||
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
 | 
					import {
 | 
				
			||||||
 | 
					  FormControl,
 | 
				
			||||||
 | 
					  FormGroup,
 | 
				
			||||||
 | 
					  ReactiveFormsModule,
 | 
				
			||||||
 | 
					  Validators,
 | 
				
			||||||
 | 
					} from '@angular/forms';
 | 
				
			||||||
import { LinkService } from '../service/link.service';
 | 
					import { LinkService } from '../service/link.service';
 | 
				
			||||||
import { Router } from '@angular/router';
 | 
					import { Router } from '@angular/router';
 | 
				
			||||||
import { MatCardModule } from '@angular/material/card';
 | 
					import { MatCardModule } from '@angular/material/card';
 | 
				
			||||||
| 
						 | 
					@ -11,31 +16,45 @@ import { debounceTime } from 'rxjs';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-create-link',
 | 
					  selector: 'app-create-link',
 | 
				
			||||||
  imports: [NavbarComponent, ReactiveFormsModule, MatCardModule, MatFormFieldModule, MatInputModule, MatButtonModule],
 | 
					  imports: [
 | 
				
			||||||
 | 
					    NavbarComponent,
 | 
				
			||||||
 | 
					    ReactiveFormsModule,
 | 
				
			||||||
 | 
					    MatCardModule,
 | 
				
			||||||
 | 
					    MatFormFieldModule,
 | 
				
			||||||
 | 
					    MatInputModule,
 | 
				
			||||||
 | 
					    MatButtonModule,
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
  templateUrl: './create-link.component.html',
 | 
					  templateUrl: './create-link.component.html',
 | 
				
			||||||
  styleUrl: './create-link.component.css'
 | 
					  styleUrl: './create-link.component.css',
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class CreateLinkComponent {
 | 
					export class CreateLinkComponent {
 | 
				
			||||||
  public createLinkForm!: FormGroup;
 | 
					  public createLinkForm!: FormGroup;
 | 
				
			||||||
  public requestFailed: boolean = false;
 | 
					  public requestFailed: boolean = false;
 | 
				
			||||||
  public errorMessages: Record<string, string> = {};
 | 
					  public errorMessages: Record<string, string> = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(private linkService: LinkService, private router: Router) { }
 | 
					  constructor(
 | 
				
			||||||
 | 
					    private linkService: LinkService,
 | 
				
			||||||
 | 
					    private router: Router,
 | 
				
			||||||
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private validationErrorMessages: Record<string, string> = {
 | 
					  private validationErrorMessages: Record<string, string> = {
 | 
				
			||||||
    required: "This field is required",
 | 
					    required: 'This field is required',
 | 
				
			||||||
    pattern: "This must be a valid url",
 | 
					    pattern: 'This must be a valid url',
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  updateErrorMessages(): void {
 | 
					  updateErrorMessages(): void {
 | 
				
			||||||
    this.errorMessages = {};
 | 
					    this.errorMessages = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Object.keys(this.createLinkForm.controls).forEach(field => {
 | 
					    Object.keys(this.createLinkForm.controls).forEach((field) => {
 | 
				
			||||||
      const control = this.createLinkForm.get(field);
 | 
					      const control = this.createLinkForm.get(field);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (control && control.errors) {
 | 
					      if (control && control.errors) {
 | 
				
			||||||
        this.errorMessages[field] = Object.keys(control.errors)
 | 
					        this.errorMessages[field] = Object.keys(control.errors)
 | 
				
			||||||
          .map(errorKey => this.validationErrorMessages[errorKey] || `Unknown error: ${errorKey}`)
 | 
					          .map(
 | 
				
			||||||
 | 
					            (errorKey) =>
 | 
				
			||||||
 | 
					              this.validationErrorMessages[errorKey] ||
 | 
				
			||||||
 | 
					              `Unknown error: ${errorKey}`,
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
          .join(' ');
 | 
					          .join(' ');
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
| 
						 | 
					@ -48,7 +67,12 @@ export class CreateLinkComponent {
 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {
 | 
				
			||||||
    this.createLinkForm = new FormGroup({
 | 
					    this.createLinkForm = new FormGroup({
 | 
				
			||||||
      name: new FormControl('', Validators.required),
 | 
					      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]*))?)/)]),
 | 
					      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.createLinkForm.valueChanges.subscribe(() => {
 | 
				
			||||||
      this.updateErrorMessages();
 | 
					      this.updateErrorMessages();
 | 
				
			||||||
| 
						 | 
					@ -56,16 +80,18 @@ export class CreateLinkComponent {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  submit() {
 | 
					  submit() {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!this.createLinkForm.valid) {
 | 
					    if (!this.createLinkForm.valid) {
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.requestFailed = false;
 | 
					    this.requestFailed = false;
 | 
				
			||||||
    this.linkService.createLink({
 | 
					    this.linkService
 | 
				
			||||||
 | 
					      .createLink({
 | 
				
			||||||
        name: this.createLinkForm.get('name')?.value,
 | 
					        name: this.createLinkForm.get('name')?.value,
 | 
				
			||||||
        link: this.createLinkForm.get('link')?.value,
 | 
					        link: this.createLinkForm.get('link')?.value,
 | 
				
			||||||
    }).catch(() => this.requestFailed = true).finally(() => {
 | 
					      })
 | 
				
			||||||
 | 
					      .catch(() => (this.requestFailed = true))
 | 
				
			||||||
 | 
					      .finally(() => {
 | 
				
			||||||
        if (!this.requestFailed) {
 | 
					        if (!this.requestFailed) {
 | 
				
			||||||
          this.router.navigate(['dashboard']);
 | 
					          this.router.navigate(['dashboard']);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,12 +1,18 @@
 | 
				
			||||||
<div class="mx-auto container">
 | 
					<div class="mx-auto container">
 | 
				
			||||||
  <app-navbar></app-navbar>
 | 
					  <app-navbar></app-navbar>
 | 
				
			||||||
  <div class="overflow-x-auto">
 | 
					  <div class="overflow-x-auto">
 | 
				
			||||||
    <button mat-flat-button color="secondary" class="my-3" (click)="createLink()">Create new Link</button>
 | 
					    <button
 | 
				
			||||||
 | 
					      mat-flat-button
 | 
				
			||||||
 | 
					      color="secondary"
 | 
				
			||||||
 | 
					      class="my-3"
 | 
				
			||||||
 | 
					      (click)="createLink()"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      Create new Link
 | 
				
			||||||
 | 
					    </button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <mat-card appearance="outlined">
 | 
					    <mat-card appearance="outlined">
 | 
				
			||||||
      <mat-card-content>
 | 
					      <mat-card-content>
 | 
				
			||||||
        <table mat-table [dataSource]="links" class="mat-elevation-z8">
 | 
					        <table mat-table [dataSource]="links" class="mat-elevation-z8">
 | 
				
			||||||
 | 
					 | 
				
			||||||
          <!--- Note that these columns can be defined in any order.
 | 
					          <!--- Note that these columns can be defined in any order.
 | 
				
			||||||
        The actual rendered columns are set as a property on the row definition" -->
 | 
					        The actual rendered columns are set as a property on the row definition" -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,19 +42,31 @@
 | 
				
			||||||
          <ng-container matColumnDef="shortLink">
 | 
					          <ng-container matColumnDef="shortLink">
 | 
				
			||||||
            <th mat-header-cell *matHeaderCellDef>Short link</th>
 | 
					            <th mat-header-cell *matHeaderCellDef>Short link</th>
 | 
				
			||||||
            <td mat-cell *matCellDef="let element">
 | 
					            <td mat-cell *matCellDef="let element">
 | 
				
			||||||
              <button mat-flat-button class="my-3" (click)="copyLink(element.id)">Copy short link</button>
 | 
					              <button
 | 
				
			||||||
 | 
					                mat-flat-button
 | 
				
			||||||
 | 
					                class="my-3"
 | 
				
			||||||
 | 
					                (click)="copyLink(element.id)"
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					                Copy short link
 | 
				
			||||||
 | 
					              </button>
 | 
				
			||||||
            </td>
 | 
					            </td>
 | 
				
			||||||
          </ng-container>
 | 
					          </ng-container>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <ng-container matColumnDef="actions">
 | 
					          <ng-container matColumnDef="actions">
 | 
				
			||||||
            <th mat-header-cell *matHeaderCellDef>Actions</th>
 | 
					            <th mat-header-cell *matHeaderCellDef>Actions</th>
 | 
				
			||||||
            <td mat-cell *matCellDef="let element">
 | 
					            <td mat-cell *matCellDef="let element">
 | 
				
			||||||
              <button mat-flat-button color="warn" (click)="deleteLink(element)">Delete</button>
 | 
					              <button
 | 
				
			||||||
 | 
					                mat-flat-button
 | 
				
			||||||
 | 
					                color="warn"
 | 
				
			||||||
 | 
					                (click)="deleteLink(element)"
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
 | 
					                Delete
 | 
				
			||||||
 | 
					              </button>
 | 
				
			||||||
            </td>
 | 
					            </td>
 | 
				
			||||||
          </ng-container>
 | 
					          </ng-container>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
 | 
					          <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
 | 
				
			||||||
          <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
 | 
					          <tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
 | 
				
			||||||
        </table>
 | 
					        </table>
 | 
				
			||||||
      </mat-card-content>
 | 
					      </mat-card-content>
 | 
				
			||||||
    </mat-card>
 | 
					    </mat-card>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,9 +8,8 @@ describe('DashboardComponent', () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      imports: [DashboardComponent]
 | 
					      imports: [DashboardComponent],
 | 
				
			||||||
    })
 | 
					    }).compileComponents();
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fixture = TestBed.createComponent(DashboardComponent);
 | 
					    fixture = TestBed.createComponent(DashboardComponent);
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,29 +15,38 @@ import { MatSnackBar } from '@angular/material/snack-bar';
 | 
				
			||||||
  selector: 'app-dashboard',
 | 
					  selector: 'app-dashboard',
 | 
				
			||||||
  imports: [NavbarComponent, MatTableModule, MatCardModule, MatButtonModule],
 | 
					  imports: [NavbarComponent, MatTableModule, MatCardModule, MatButtonModule],
 | 
				
			||||||
  templateUrl: './dashboard.component.html',
 | 
					  templateUrl: './dashboard.component.html',
 | 
				
			||||||
  styleUrl: './dashboard.component.css'
 | 
					  styleUrl: './dashboard.component.css',
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class DashboardComponent {
 | 
					export class DashboardComponent {
 | 
				
			||||||
  public links: Link[] = [];
 | 
					  public links: Link[] = [];
 | 
				
			||||||
  displayedColumns: string[] = ['id', 'name', 'link', 'shortLink', 'actions'];
 | 
					  displayedColumns: string[] = ['id', 'name', 'link', 'shortLink', 'actions'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(private linkService: LinkService, private router: Router, private dialog: MatDialog, private snackBar: MatSnackBar) { };
 | 
					  constructor(
 | 
				
			||||||
 | 
					    private linkService: LinkService,
 | 
				
			||||||
 | 
					    private router: Router,
 | 
				
			||||||
 | 
					    private dialog: MatDialog,
 | 
				
			||||||
 | 
					    private snackBar: MatSnackBar,
 | 
				
			||||||
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  copyLink(id: string) {
 | 
					  copyLink(id: string) {
 | 
				
			||||||
    navigator.clipboard.writeText(this.getShortLink(id));
 | 
					    navigator.clipboard.writeText(this.getShortLink(id));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getShortLink(id: string): string {
 | 
					  getShortLink(id: string): string {
 | 
				
			||||||
    return "https://" + window.location.hostname + '/' + id;
 | 
					    return 'https://' + window.location.hostname + '/' + id;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  goToLink(link: Link) {
 | 
					  goToLink(link: Link) {
 | 
				
			||||||
    this.dialog.open(ConfirmationModalComponent, {
 | 
					    this.dialog
 | 
				
			||||||
 | 
					      .open(ConfirmationModalComponent, {
 | 
				
			||||||
        data: {
 | 
					        data: {
 | 
				
			||||||
        title: "Are you sure?",
 | 
					          title: 'Are you sure?',
 | 
				
			||||||
        description: "Are you sure that you want to open " + link.link + " now?",
 | 
					          description:
 | 
				
			||||||
 | 
					            'Are you sure that you want to open ' + link.link + ' now?',
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
    }).afterClosed().subscribe((accepted: Boolean) => {
 | 
					      })
 | 
				
			||||||
 | 
					      .afterClosed()
 | 
				
			||||||
 | 
					      .subscribe((accepted: Boolean) => {
 | 
				
			||||||
        if (accepted) {
 | 
					        if (accepted) {
 | 
				
			||||||
          this.router.navigate([link.id]);
 | 
					          this.router.navigate([link.id]);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -45,10 +54,9 @@ export class DashboardComponent {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {
 | 
				
			||||||
    this.linkService.getLinks().then(links => {
 | 
					    this.linkService.getLinks().then((links) => {
 | 
				
			||||||
      this.links = links;
 | 
					      this.links = links;
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  createLink() {
 | 
					  createLink() {
 | 
				
			||||||
| 
						 | 
					@ -56,25 +64,31 @@ export class DashboardComponent {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  deleteLink(link: Link) {
 | 
					  deleteLink(link: Link) {
 | 
				
			||||||
    this.dialog.open(ConfirmationModalComponent, {
 | 
					    this.dialog
 | 
				
			||||||
 | 
					      .open(ConfirmationModalComponent, {
 | 
				
			||||||
        data: {
 | 
					        data: {
 | 
				
			||||||
        title: "Are you sure?",
 | 
					          title: 'Are you sure?',
 | 
				
			||||||
        description: "Are you sure that you want to delete " + link.name + "?",
 | 
					          description:
 | 
				
			||||||
      }
 | 
					            'Are you sure that you want to delete ' + link.name + '?',
 | 
				
			||||||
    }).afterClosed().subscribe((accepted: boolean) => {
 | 
					        },
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      .afterClosed()
 | 
				
			||||||
 | 
					      .subscribe((accepted: boolean) => {
 | 
				
			||||||
        if (accepted) {
 | 
					        if (accepted) {
 | 
				
			||||||
          this.linkService.deleteLink(link.id).catch(() => {
 | 
					          this.linkService.deleteLink(link.id).catch(() => {
 | 
				
			||||||
          const errorNotification = this.snackBar.open("Something went wrong.");
 | 
					            const errorNotification = this.snackBar.open(
 | 
				
			||||||
 | 
					              'Something went wrong.',
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
            setTimeout(() => {
 | 
					            setTimeout(() => {
 | 
				
			||||||
              errorNotification.dismiss();
 | 
					              errorNotification.dismiss();
 | 
				
			||||||
            }, 5000);
 | 
					            }, 5000);
 | 
				
			||||||
          });
 | 
					          });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.links = this.links.filter(givenLink => {
 | 
					          this.links = this.links.filter((givenLink) => {
 | 
				
			||||||
            return givenLink.id != link.id;
 | 
					            return givenLink.id != link.id;
 | 
				
			||||||
          });
 | 
					          });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const notification = this.snackBar.open(link.name + " was deleted.");
 | 
					          const notification = this.snackBar.open(link.name + ' was deleted.');
 | 
				
			||||||
          setTimeout(() => {
 | 
					          setTimeout(() => {
 | 
				
			||||||
            notification.dismiss();
 | 
					            notification.dismiss();
 | 
				
			||||||
          }, 2000);
 | 
					          }, 2000);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,15 @@
 | 
				
			||||||
<div class="flex min-h-full flex-col justify-center px-6 py-12 lg:px-8">
 | 
					<div class="flex min-h-full flex-col justify-center px-6 py-12 lg:px-8">
 | 
				
			||||||
  <div class="sm:mx-auto sm:w-full sm:max-w-sm">
 | 
					  <div class="sm:mx-auto sm:w-full sm:max-w-sm">
 | 
				
			||||||
    <img class="mx-auto h-10 w-auto" src="https://tailwindui.com/plus/img/logos/mark.svg?color=indigo&shade=600"
 | 
					    <img
 | 
				
			||||||
      alt="Your Company">
 | 
					      class="mx-auto h-10 w-auto"
 | 
				
			||||||
    <h2 class="mt-10 text-center text-2xl/9 font-bold tracking-tight text-gray-900">Sign in to your account</h2>
 | 
					      src="https://tailwindui.com/plus/img/logos/mark.svg?color=indigo&shade=600"
 | 
				
			||||||
 | 
					      alt="Your Company"
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					    <h2
 | 
				
			||||||
 | 
					      class="mt-10 text-center text-2xl/9 font-bold tracking-tight text-gray-900"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      Sign in to your account
 | 
				
			||||||
 | 
					    </h2>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
 | 
					  <div class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
 | 
				
			||||||
| 
						 | 
					@ -10,33 +17,58 @@
 | 
				
			||||||
      @if (invalidCredentials) {
 | 
					      @if (invalidCredentials) {
 | 
				
			||||||
        <div class="mt-2">
 | 
					        <div class="mt-2">
 | 
				
			||||||
          <p
 | 
					          <p
 | 
				
			||||||
          class="block w-full rounded-md bg-white px-3 py-1.5 text-base text-red-500 outline outline-red-500 outline-2 -outline-offset-2 sm:text-sm/6">
 | 
					            class="block w-full rounded-md bg-white px-3 py-1.5 text-base text-red-500 outline outline-red-500 outline-2 -outline-offset-2 sm:text-sm/6"
 | 
				
			||||||
          Invalid Credentials</p>
 | 
					          >
 | 
				
			||||||
 | 
					            Invalid Credentials
 | 
				
			||||||
 | 
					          </p>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      <div>
 | 
					      <div>
 | 
				
			||||||
        <label for="email" class="block text-sm/6 font-medium text-gray-900">Email address</label>
 | 
					        <label for="email" class="block text-sm/6 font-medium text-gray-900"
 | 
				
			||||||
 | 
					          >Email address</label
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
        <div class="mt-2">
 | 
					        <div class="mt-2">
 | 
				
			||||||
          <input formControlName="email" type="email" name="email" id="email" autocomplete="email" required
 | 
					          <input
 | 
				
			||||||
            class="block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6">
 | 
					            formControlName="email"
 | 
				
			||||||
 | 
					            type="email"
 | 
				
			||||||
 | 
					            name="email"
 | 
				
			||||||
 | 
					            id="email"
 | 
				
			||||||
 | 
					            autocomplete="email"
 | 
				
			||||||
 | 
					            required
 | 
				
			||||||
 | 
					            class="block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <div>
 | 
					      <div>
 | 
				
			||||||
        <div class="flex items-center justify-between">
 | 
					        <div class="flex items-center justify-between">
 | 
				
			||||||
          <label for="password" class="block text-sm/6 font-medium text-gray-900">Password</label>
 | 
					          <label
 | 
				
			||||||
 | 
					            for="password"
 | 
				
			||||||
 | 
					            class="block text-sm/6 font-medium text-gray-900"
 | 
				
			||||||
 | 
					            >Password</label
 | 
				
			||||||
 | 
					          >
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div class="mt-2">
 | 
					        <div class="mt-2">
 | 
				
			||||||
          <input formControlName="password" type="password" name="password" id="password"
 | 
					          <input
 | 
				
			||||||
            autocomplete="current-password" required
 | 
					            formControlName="password"
 | 
				
			||||||
            class="block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6">
 | 
					            type="password"
 | 
				
			||||||
 | 
					            name="password"
 | 
				
			||||||
 | 
					            id="password"
 | 
				
			||||||
 | 
					            autocomplete="current-password"
 | 
				
			||||||
 | 
					            required
 | 
				
			||||||
 | 
					            class="block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <div>
 | 
					      <div>
 | 
				
			||||||
        <button (click)="submit()" type="submit"
 | 
					        <button
 | 
				
			||||||
          class="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm/6 font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">Sign
 | 
					          (click)="submit()"
 | 
				
			||||||
          in</button>
 | 
					          type="submit"
 | 
				
			||||||
 | 
					          class="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm/6 font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          Sign in
 | 
				
			||||||
 | 
					        </button>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </form>
 | 
					    </form>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,9 +8,8 @@ describe('LoginComponent', () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      imports: [LoginComponent]
 | 
					      imports: [LoginComponent],
 | 
				
			||||||
    })
 | 
					    }).compileComponents();
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fixture = TestBed.createComponent(LoginComponent);
 | 
					    fixture = TestBed.createComponent(LoginComponent);
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,14 +8,14 @@ import { Router } from '@angular/router';
 | 
				
			||||||
  selector: 'app-login',
 | 
					  selector: 'app-login',
 | 
				
			||||||
  imports: [ReactiveFormsModule],
 | 
					  imports: [ReactiveFormsModule],
 | 
				
			||||||
  templateUrl: './login.component.html',
 | 
					  templateUrl: './login.component.html',
 | 
				
			||||||
  styleUrl: './login.component.css'
 | 
					  styleUrl: './login.component.css',
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class LoginComponent {
 | 
					export class LoginComponent {
 | 
				
			||||||
  public loginForm!: FormGroup;
 | 
					  public loginForm!: FormGroup;
 | 
				
			||||||
  public invalidCredentials = false;
 | 
					  public invalidCredentials = false;
 | 
				
			||||||
  private pb = new PocketBase(environment.POCKETBASE);
 | 
					  private pb = new PocketBase(environment.POCKETBASE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(private router: Router) { };
 | 
					  constructor(private router: Router) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {
 | 
				
			||||||
    this.loginForm = new FormGroup({
 | 
					    this.loginForm = new FormGroup({
 | 
				
			||||||
| 
						 | 
					@ -29,12 +29,16 @@ export class LoginComponent {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  submit() {
 | 
					  submit() {
 | 
				
			||||||
    this.pb.collection("users").authWithPassword(
 | 
					    this.pb
 | 
				
			||||||
 | 
					      .collection('users')
 | 
				
			||||||
 | 
					      .authWithPassword(
 | 
				
			||||||
        this.loginForm.get('email')?.value,
 | 
					        this.loginForm.get('email')?.value,
 | 
				
			||||||
      this.loginForm.get('password')?.value
 | 
					        this.loginForm.get('password')?.value,
 | 
				
			||||||
    ).then(() => {
 | 
					      )
 | 
				
			||||||
 | 
					      .then(() => {
 | 
				
			||||||
        this.router.navigate(['dashboard']);
 | 
					        this.router.navigate(['dashboard']);
 | 
				
			||||||
    }).catch(() => {
 | 
					      })
 | 
				
			||||||
 | 
					      .catch(() => {
 | 
				
			||||||
        this.invalidCredentials = true;
 | 
					        this.invalidCredentials = true;
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
export interface ConfirmationModalDto {
 | 
					export interface ConfirmationModalDto {
 | 
				
			||||||
  title: string,
 | 
					  title: string;
 | 
				
			||||||
  description: string,
 | 
					  description: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,9 +8,8 @@ describe('NavbarComponent', () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      imports: [NavbarComponent]
 | 
					      imports: [NavbarComponent],
 | 
				
			||||||
    })
 | 
					    }).compileComponents();
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fixture = TestBed.createComponent(NavbarComponent);
 | 
					    fixture = TestBed.createComponent(NavbarComponent);
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,4 @@ import { Component, ViewEncapsulation } from '@angular/core';
 | 
				
			||||||
  styleUrl: './navbar.component.css',
 | 
					  styleUrl: './navbar.component.css',
 | 
				
			||||||
  encapsulation: ViewEncapsulation.None,
 | 
					  encapsulation: ViewEncapsulation.None,
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class NavbarComponent {
 | 
					export class NavbarComponent {}
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,20 +5,26 @@ import {
 | 
				
			||||||
  GuardResult,
 | 
					  GuardResult,
 | 
				
			||||||
  MaybeAsync,
 | 
					  MaybeAsync,
 | 
				
			||||||
  Router,
 | 
					  Router,
 | 
				
			||||||
  RouterStateSnapshot
 | 
					  RouterStateSnapshot,
 | 
				
			||||||
} from '@angular/router';
 | 
					} from '@angular/router';
 | 
				
			||||||
import PocketBase from 'pocketbase';
 | 
					import PocketBase from 'pocketbase';
 | 
				
			||||||
import { environment } from '../../environments/environment';
 | 
					import { environment } from '../../environments/environment';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable({
 | 
					@Injectable({
 | 
				
			||||||
  providedIn: 'root'
 | 
					  providedIn: 'root',
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class AuthGuard implements CanActivate {
 | 
					export class AuthGuard implements CanActivate {
 | 
				
			||||||
  constructor(private router: Router) { };
 | 
					  constructor(private router: Router) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
 | 
					  async canActivate(
 | 
				
			||||||
 | 
					    route: ActivatedRouteSnapshot,
 | 
				
			||||||
 | 
					    state: RouterStateSnapshot,
 | 
				
			||||||
 | 
					  ): Promise<boolean> {
 | 
				
			||||||
    const pb = new PocketBase(environment.POCKETBASE);
 | 
					    const pb = new PocketBase(environment.POCKETBASE);
 | 
				
			||||||
    await pb.collection('users').authRefresh().catch(() => this.router.navigate(['']));
 | 
					    await pb
 | 
				
			||||||
 | 
					      .collection('users')
 | 
				
			||||||
 | 
					      .authRefresh()
 | 
				
			||||||
 | 
					      .catch(() => this.router.navigate(['']));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (pb.authStore.isValid) {
 | 
					    if (pb.authStore.isValid) {
 | 
				
			||||||
      return true;
 | 
					      return true;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,10 @@
 | 
				
			||||||
import { Injectable } from "@angular/core";
 | 
					import { Injectable } from '@angular/core';
 | 
				
			||||||
import { environment } from "../../environments/environment";
 | 
					import { environment } from '../../environments/environment';
 | 
				
			||||||
import PocketBase, { RecordModel } from 'pocketbase';
 | 
					import PocketBase, { RecordModel } from 'pocketbase';
 | 
				
			||||||
import { Link } from "../models/link";
 | 
					import { Link } from '../models/link';
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable({
 | 
					@Injectable({
 | 
				
			||||||
  providedIn: 'root'
 | 
					  providedIn: 'root',
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class LinkService {
 | 
					export class LinkService {
 | 
				
			||||||
  private pb = new PocketBase(environment.POCKETBASE);
 | 
					  private pb = new PocketBase(environment.POCKETBASE);
 | 
				
			||||||
| 
						 | 
					@ -24,9 +23,9 @@ export class LinkService {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  createLink(link: any): Promise<RecordModel> {
 | 
					  createLink(link: any): Promise<RecordModel> {
 | 
				
			||||||
    return this.pb.collection('links').create({
 | 
					    return this.pb.collection('links').create({
 | 
				
			||||||
      'name': link.name,
 | 
					      name: link.name,
 | 
				
			||||||
      'link': link.link,
 | 
					      link: link.link,
 | 
				
			||||||
      'owner': this.pb.authStore.record?.id,
 | 
					      owner: this.pb.authStore.record?.id,
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,6 @@
 | 
				
			||||||
<p>Your are being redirected.</p>
 | 
					<p>Your are being redirected.</p>
 | 
				
			||||||
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-5944102294748899"
 | 
					<script
 | 
				
			||||||
  crossorigin="anonymous"></script>
 | 
					  async
 | 
				
			||||||
 | 
					  src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-5944102294748899"
 | 
				
			||||||
 | 
					  crossorigin="anonymous"
 | 
				
			||||||
 | 
					></script>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,9 +8,8 @@ describe('ViewLinkComponent', () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
    await TestBed.configureTestingModule({
 | 
					    await TestBed.configureTestingModule({
 | 
				
			||||||
      imports: [ViewLinkComponent]
 | 
					      imports: [ViewLinkComponent],
 | 
				
			||||||
    })
 | 
					    }).compileComponents();
 | 
				
			||||||
    .compileComponents();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fixture = TestBed.createComponent(ViewLinkComponent);
 | 
					    fixture = TestBed.createComponent(ViewLinkComponent);
 | 
				
			||||||
    component = fixture.componentInstance;
 | 
					    component = fixture.componentInstance;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,13 +6,19 @@ import { ActivatedRoute, Router } from '@angular/router';
 | 
				
			||||||
  selector: 'app-view-link',
 | 
					  selector: 'app-view-link',
 | 
				
			||||||
  imports: [],
 | 
					  imports: [],
 | 
				
			||||||
  templateUrl: './view-link.component.html',
 | 
					  templateUrl: './view-link.component.html',
 | 
				
			||||||
  styleUrl: './view-link.component.css'
 | 
					  styleUrl: './view-link.component.css',
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class ViewLinkComponent {
 | 
					export class ViewLinkComponent {
 | 
				
			||||||
  constructor(private linkService: LinkService, private route: ActivatedRoute, private router: Router) { }
 | 
					  constructor(
 | 
				
			||||||
 | 
					    private linkService: LinkService,
 | 
				
			||||||
 | 
					    private route: ActivatedRoute,
 | 
				
			||||||
 | 
					    private router: Router,
 | 
				
			||||||
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ngOnInit(): void {
 | 
					  ngOnInit(): void {
 | 
				
			||||||
    this.linkService.getLink(this.route.snapshot.params['link']).then(link => {
 | 
					    this.linkService
 | 
				
			||||||
 | 
					      .getLink(this.route.snapshot.params['link'])
 | 
				
			||||||
 | 
					      .then((link) => {
 | 
				
			||||||
        window.location.href = link.link;
 | 
					        window.location.href = link.link;
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,3 @@
 | 
				
			||||||
export const environment = {
 | 
					export const environment = {
 | 
				
			||||||
  POCKETBASE: 'https://jklink-pocketbase-test.intern.kjan.de'
 | 
					  POCKETBASE: 'https://jklink-pocketbase-test.intern.kjan.de',
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,3 @@
 | 
				
			||||||
export const environment = {
 | 
					export const environment = {
 | 
				
			||||||
  POCKETBASE: 'https://jklink-pocketbase-test.intern.kjan.de'
 | 
					  POCKETBASE: 'https://jklink-pocketbase-test.intern.kjan.de',
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,13 +1,19 @@
 | 
				
			||||||
<!doctype html>
 | 
					<!doctype html>
 | 
				
			||||||
<html class="h-full" lang="en" data-theme="light">
 | 
					<html class="h-full" lang="en" data-theme="light">
 | 
				
			||||||
  <head>
 | 
					  <head>
 | 
				
			||||||
  <meta charset="utf-8">
 | 
					    <meta charset="utf-8" />
 | 
				
			||||||
    <title>Jklink</title>
 | 
					    <title>Jklink</title>
 | 
				
			||||||
  <base href="/">
 | 
					    <base href="/" />
 | 
				
			||||||
  <meta name="viewport" content="width=device-width, initial-scale=1">
 | 
					    <meta name="viewport" content="width=device-width, initial-scale=1" />
 | 
				
			||||||
  <link rel="icon" type="image/x-icon" href="favicon.ico">
 | 
					    <link rel="icon" type="image/x-icon" href="favicon.ico" />
 | 
				
			||||||
  <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
 | 
					    <link
 | 
				
			||||||
  <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
 | 
					      href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap"
 | 
				
			||||||
 | 
					      rel="stylesheet"
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					    <link
 | 
				
			||||||
 | 
					      href="https://fonts.googleapis.com/icon?family=Material+Icons"
 | 
				
			||||||
 | 
					      rel="stylesheet"
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
  </head>
 | 
					  </head>
 | 
				
			||||||
  <body class="h-full base-100 mat-typography">
 | 
					  <body class="h-full base-100 mat-typography">
 | 
				
			||||||
    <app-root></app-root>
 | 
					    <app-root></app-root>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,5 +2,6 @@ import { bootstrapApplication } from '@angular/platform-browser';
 | 
				
			||||||
import { appConfig } from './app/app.config';
 | 
					import { appConfig } from './app/app.config';
 | 
				
			||||||
import { AppComponent } from './app/app.component';
 | 
					import { AppComponent } from './app/app.component';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bootstrapApplication(AppComponent, appConfig)
 | 
					bootstrapApplication(AppComponent, appConfig).catch((err) =>
 | 
				
			||||||
  .catch((err) => console.error(err));
 | 
					  console.error(err),
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
@use '@angular/material' as mat;
 | 
					@use "@angular/material" as mat;
 | 
				
			||||||
@tailwind base;
 | 
					@tailwind base;
 | 
				
			||||||
@tailwind components;
 | 
					@tailwind components;
 | 
				
			||||||
@tailwind utilities;
 | 
					@tailwind utilities;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,6 @@
 | 
				
			||||||
/** @type {import('tailwindcss').Config} */
 | 
					/** @type {import('tailwindcss').Config} */
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
  content: [
 | 
					  content: ["./src/**/*.{html,ts}"],
 | 
				
			||||||
    "./src/**/*.{html,ts}",
 | 
					 | 
				
			||||||
  ],
 | 
					 | 
				
			||||||
  theme: {
 | 
					  theme: {
 | 
				
			||||||
    extend: {},
 | 
					    extend: {},
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
| 
						 | 
					@ -10,5 +8,4 @@ module.exports = {
 | 
				
			||||||
    preflight: false,
 | 
					    preflight: false,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  plugins: [],
 | 
					  plugins: [],
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,10 +6,6 @@
 | 
				
			||||||
    "outDir": "./out-tsc/app",
 | 
					    "outDir": "./out-tsc/app",
 | 
				
			||||||
    "types": []
 | 
					    "types": []
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "files": [
 | 
					  "files": ["src/main.ts"],
 | 
				
			||||||
    "src/main.ts"
 | 
					  "include": ["src/**/*.d.ts"]
 | 
				
			||||||
  ],
 | 
					 | 
				
			||||||
  "include": [
 | 
					 | 
				
			||||||
    "src/**/*.d.ts"
 | 
					 | 
				
			||||||
  ]
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,12 +4,7 @@
 | 
				
			||||||
  "extends": "./tsconfig.json",
 | 
					  "extends": "./tsconfig.json",
 | 
				
			||||||
  "compilerOptions": {
 | 
					  "compilerOptions": {
 | 
				
			||||||
    "outDir": "./out-tsc/spec",
 | 
					    "outDir": "./out-tsc/spec",
 | 
				
			||||||
    "types": [
 | 
					    "types": ["jasmine"]
 | 
				
			||||||
      "jasmine"
 | 
					 | 
				
			||||||
    ]
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "include": [
 | 
					  "include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
 | 
				
			||||||
    "src/**/*.spec.ts",
 | 
					 | 
				
			||||||
    "src/**/*.d.ts"
 | 
					 | 
				
			||||||
  ]
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue