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:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_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",
|
||||
"index": "src/index.html",
|
||||
"browser": "src/main.ts",
|
||||
"polyfills": [
|
||||
"zone.js"
|
||||
],
|
||||
"polyfills": ["zone.js"],
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"assets": [
|
||||
{
|
||||
|
@ -86,10 +84,7 @@
|
|||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"polyfills": [
|
||||
"zone.js",
|
||||
"zone.js/testing"
|
||||
],
|
||||
"polyfills": ["zone.js", "zone.js/testing"],
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"assets": [
|
||||
{
|
||||
|
|
17
package-lock.json
generated
17
package-lock.json
generated
|
@ -38,6 +38,7 @@
|
|||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "~2.1.0",
|
||||
"postcss": "^8.5.1",
|
||||
"prettier": "3.4.2",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"typescript": "~5.6.2"
|
||||
}
|
||||
|
@ -10845,6 +10846,22 @@
|
|||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
||||
"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": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz",
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "~2.1.0",
|
||||
"postcss": "^8.5.1",
|
||||
"prettier": "3.4.2",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"typescript": "~5.6.2"
|
||||
}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
module.exports = {
|
||||
branches: ['prod'],
|
||||
branches: ["prod"],
|
||||
plugins: [
|
||||
'@semantic-release/commit-analyzer',
|
||||
'@semantic-release/release-notes-generator',
|
||||
'@semantic-release/changelog',
|
||||
["@saithodev/semantic-release-gitea", {
|
||||
"giteaUrl": "https://git.kjan.de"
|
||||
}],
|
||||
"@semantic-release/commit-analyzer",
|
||||
"@semantic-release/release-notes-generator",
|
||||
"@semantic-release/changelog",
|
||||
[
|
||||
"@saithodev/semantic-release-gitea",
|
||||
{
|
||||
giteaUrl: "https://git.kjan.de",
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
||||
|
|
|
@ -24,6 +24,8 @@ describe('AppComponent', () => {
|
|||
const fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.detectChanges();
|
||||
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',
|
||||
imports: [RouterOutlet],
|
||||
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';
|
||||
|
||||
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,
|
||||
},
|
||||
{
|
||||
path: "**",
|
||||
redirectTo: "",
|
||||
path: '**',
|
||||
redirectTo: '',
|
||||
},
|
||||
];
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<h2 mat-dialog-title>{{data.title}}</h2>
|
||||
<mat-dialog-content>{{data.description}}</mat-dialog-content>
|
||||
<h2 mat-dialog-title>{{ data.title }}</h2>
|
||||
<mat-dialog-content>{{ data.description }}</mat-dialog-content>
|
||||
<mat-dialog-actions>
|
||||
<button mat-flat-button color="warn" (click)="close()">No</button>
|
||||
<button mat-flat-button (click)="accept()">Yes</button>
|
||||
|
|
|
@ -8,9 +8,8 @@ describe('VisitLinkConfirmationComponent', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ConfirmationModalComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
imports: [ConfirmationModalComponent],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ConfirmationModalComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
|
|
@ -12,9 +12,14 @@ import { ConfirmationModalDto } from '../models/confirmation-modal';
|
|||
|
||||
@Component({
|
||||
selector: 'app-confirmation-modal',
|
||||
imports: [MatDialogTitle, MatDialogContent, MatDialogActions, MatButtonModule],
|
||||
imports: [
|
||||
MatDialogTitle,
|
||||
MatDialogContent,
|
||||
MatDialogActions,
|
||||
MatButtonModule,
|
||||
],
|
||||
templateUrl: './confirmation-modal.component.html',
|
||||
styleUrl: './confirmation-modal.component.css'
|
||||
styleUrl: './confirmation-modal.component.css',
|
||||
})
|
||||
export class ConfirmationModalComponent {
|
||||
public data: ConfirmationModalDto = inject(MAT_DIALOG_DATA);
|
||||
|
|
|
@ -1,17 +1,24 @@
|
|||
<div class="mx-auto container">
|
||||
<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">
|
||||
<form class="flex flex-col" [formGroup]="createLinkForm">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-error>{{errorMessages['name']}}</mat-error>
|
||||
<mat-error>{{ errorMessages["name"] }}</mat-error>
|
||||
<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 appearance="outline">
|
||||
<mat-error>{{errorMessages['link']}}</mat-error>
|
||||
<mat-error>{{ errorMessages["link"] }}</mat-error>
|
||||
<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>
|
||||
<button mat-flat-button type="submit" (click)="submit()">Create</button>
|
||||
</form>
|
||||
|
|
|
@ -8,9 +8,8 @@ describe('CreateLinkComponent', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [CreateLinkComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
imports: [CreateLinkComponent],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(CreateLinkComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
import { Component } from '@angular/core';
|
||||
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 { Router } from '@angular/router';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
|
@ -11,31 +16,45 @@ import { debounceTime } from 'rxjs';
|
|||
|
||||
@Component({
|
||||
selector: 'app-create-link',
|
||||
imports: [NavbarComponent, ReactiveFormsModule, MatCardModule, MatFormFieldModule, MatInputModule, MatButtonModule],
|
||||
imports: [
|
||||
NavbarComponent,
|
||||
ReactiveFormsModule,
|
||||
MatCardModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
MatButtonModule,
|
||||
],
|
||||
templateUrl: './create-link.component.html',
|
||||
styleUrl: './create-link.component.css'
|
||||
styleUrl: './create-link.component.css',
|
||||
})
|
||||
export class CreateLinkComponent {
|
||||
public createLinkForm!: FormGroup;
|
||||
public requestFailed: boolean = false;
|
||||
public errorMessages: Record<string, string> = {};
|
||||
|
||||
constructor(private linkService: LinkService, private router: Router) { }
|
||||
constructor(
|
||||
private linkService: LinkService,
|
||||
private router: Router,
|
||||
) {}
|
||||
|
||||
private validationErrorMessages: Record<string, string> = {
|
||||
required: "This field is required",
|
||||
pattern: "This must be a valid url",
|
||||
required: 'This field is required',
|
||||
pattern: 'This must be a valid url',
|
||||
};
|
||||
|
||||
updateErrorMessages(): void {
|
||||
this.errorMessages = {};
|
||||
|
||||
Object.keys(this.createLinkForm.controls).forEach(field => {
|
||||
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}`)
|
||||
.map(
|
||||
(errorKey) =>
|
||||
this.validationErrorMessages[errorKey] ||
|
||||
`Unknown error: ${errorKey}`,
|
||||
)
|
||||
.join(' ');
|
||||
}
|
||||
});
|
||||
|
@ -48,7 +67,12 @@ export class CreateLinkComponent {
|
|||
ngOnInit(): void {
|
||||
this.createLinkForm = new FormGroup({
|
||||
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.updateErrorMessages();
|
||||
|
@ -56,16 +80,18 @@ export class CreateLinkComponent {
|
|||
}
|
||||
|
||||
submit() {
|
||||
|
||||
if (!this.createLinkForm.valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.requestFailed = false;
|
||||
this.linkService.createLink({
|
||||
this.linkService
|
||||
.createLink({
|
||||
name: this.createLinkForm.get('name')?.value,
|
||||
link: this.createLinkForm.get('link')?.value,
|
||||
}).catch(() => this.requestFailed = true).finally(() => {
|
||||
})
|
||||
.catch(() => (this.requestFailed = true))
|
||||
.finally(() => {
|
||||
if (!this.requestFailed) {
|
||||
this.router.navigate(['dashboard']);
|
||||
}
|
||||
|
|
|
@ -1,54 +1,72 @@
|
|||
<div class="mx-auto container">
|
||||
<app-navbar></app-navbar>
|
||||
<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-content>
|
||||
<table mat-table [dataSource]="links" class="mat-elevation-z8">
|
||||
|
||||
<!--- Note that these columns can be defined in any order.
|
||||
The actual rendered columns are set as a property on the row definition" -->
|
||||
|
||||
<!-- Position Column -->
|
||||
<ng-container matColumnDef="id">
|
||||
<th mat-header-cell *matHeaderCellDef> ID </th>
|
||||
<th mat-header-cell *matHeaderCellDef>ID</th>
|
||||
<td mat-cell *matCellDef="let element">
|
||||
<a (click)="goToLink(element)" class="link">
|
||||
{{element.id}}
|
||||
{{ element.id }}
|
||||
</a>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Name Column -->
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef> Name </th>
|
||||
<td mat-cell *matCellDef="let element"> {{element.name}} </td>
|
||||
<th mat-header-cell *matHeaderCellDef>Name</th>
|
||||
<td mat-cell *matCellDef="let element">{{ element.name }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Weight Column -->
|
||||
<ng-container matColumnDef="link">
|
||||
<th mat-header-cell *matHeaderCellDef> Link </th>
|
||||
<td mat-cell *matCellDef="let element"> {{element.link}} </td>
|
||||
<th mat-header-cell *matHeaderCellDef>Link</th>
|
||||
<td mat-cell *matCellDef="let element">{{ element.link }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Symbol Column -->
|
||||
<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">
|
||||
<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>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||
<th mat-header-cell *matHeaderCellDef>Actions</th>
|
||||
<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>
|
||||
</ng-container>
|
||||
|
||||
<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>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
|
|
@ -8,9 +8,8 @@ describe('DashboardComponent', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [DashboardComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
imports: [DashboardComponent],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(DashboardComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
|
|
@ -15,29 +15,38 @@ import { MatSnackBar } from '@angular/material/snack-bar';
|
|||
selector: 'app-dashboard',
|
||||
imports: [NavbarComponent, MatTableModule, MatCardModule, MatButtonModule],
|
||||
templateUrl: './dashboard.component.html',
|
||||
styleUrl: './dashboard.component.css'
|
||||
styleUrl: './dashboard.component.css',
|
||||
})
|
||||
export class DashboardComponent {
|
||||
public links: Link[] = [];
|
||||
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) {
|
||||
navigator.clipboard.writeText(this.getShortLink(id));
|
||||
}
|
||||
|
||||
getShortLink(id: string): string {
|
||||
return "https://" + window.location.hostname + '/' + id;
|
||||
return 'https://' + window.location.hostname + '/' + id;
|
||||
}
|
||||
|
||||
goToLink(link: Link) {
|
||||
this.dialog.open(ConfirmationModalComponent, {
|
||||
this.dialog
|
||||
.open(ConfirmationModalComponent, {
|
||||
data: {
|
||||
title: "Are you sure?",
|
||||
description: "Are you sure that you want to open " + link.link + " now?",
|
||||
title: 'Are you sure?',
|
||||
description:
|
||||
'Are you sure that you want to open ' + link.link + ' now?',
|
||||
},
|
||||
}).afterClosed().subscribe((accepted: Boolean) => {
|
||||
})
|
||||
.afterClosed()
|
||||
.subscribe((accepted: Boolean) => {
|
||||
if (accepted) {
|
||||
this.router.navigate([link.id]);
|
||||
}
|
||||
|
@ -45,10 +54,9 @@ export class DashboardComponent {
|
|||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.linkService.getLinks().then(links => {
|
||||
this.linkService.getLinks().then((links) => {
|
||||
this.links = links;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
createLink() {
|
||||
|
@ -56,25 +64,31 @@ export class DashboardComponent {
|
|||
}
|
||||
|
||||
deleteLink(link: Link) {
|
||||
this.dialog.open(ConfirmationModalComponent, {
|
||||
this.dialog
|
||||
.open(ConfirmationModalComponent, {
|
||||
data: {
|
||||
title: "Are you sure?",
|
||||
description: "Are you sure that you want to delete " + link.name + "?",
|
||||
}
|
||||
}).afterClosed().subscribe((accepted: boolean) => {
|
||||
title: 'Are you sure?',
|
||||
description:
|
||||
'Are you sure that you want to delete ' + link.name + '?',
|
||||
},
|
||||
})
|
||||
.afterClosed()
|
||||
.subscribe((accepted: boolean) => {
|
||||
if (accepted) {
|
||||
this.linkService.deleteLink(link.id).catch(() => {
|
||||
const errorNotification = this.snackBar.open("Something went wrong.");
|
||||
const errorNotification = this.snackBar.open(
|
||||
'Something went wrong.',
|
||||
);
|
||||
setTimeout(() => {
|
||||
errorNotification.dismiss();
|
||||
}, 5000);
|
||||
});
|
||||
|
||||
this.links = this.links.filter(givenLink => {
|
||||
this.links = this.links.filter((givenLink) => {
|
||||
return givenLink.id != link.id;
|
||||
});
|
||||
|
||||
const notification = this.snackBar.open(link.name + " was deleted.");
|
||||
const notification = this.snackBar.open(link.name + ' was deleted.');
|
||||
setTimeout(() => {
|
||||
notification.dismiss();
|
||||
}, 2000);
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
<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">
|
||||
<img class="mx-auto h-10 w-auto" 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>
|
||||
<img
|
||||
class="mx-auto h-10 w-auto"
|
||||
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 class="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
|
||||
|
@ -10,33 +17,58 @@
|
|||
@if (invalidCredentials) {
|
||||
<div class="mt-2">
|
||||
<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">
|
||||
Invalid Credentials</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"
|
||||
>
|
||||
Invalid Credentials
|
||||
</p>
|
||||
</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">
|
||||
<input 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">
|
||||
<input
|
||||
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 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 class="mt-2">
|
||||
<input formControlName="password" 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">
|
||||
<input
|
||||
formControlName="password"
|
||||
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>
|
||||
<button (click)="submit()" 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>
|
||||
<button
|
||||
(click)="submit()"
|
||||
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>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -8,9 +8,8 @@ describe('LoginComponent', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [LoginComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
imports: [LoginComponent],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(LoginComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
|
|
@ -8,14 +8,14 @@ import { Router } from '@angular/router';
|
|||
selector: 'app-login',
|
||||
imports: [ReactiveFormsModule],
|
||||
templateUrl: './login.component.html',
|
||||
styleUrl: './login.component.css'
|
||||
styleUrl: './login.component.css',
|
||||
})
|
||||
export class LoginComponent {
|
||||
public loginForm!: FormGroup;
|
||||
public invalidCredentials = false;
|
||||
private pb = new PocketBase(environment.POCKETBASE);
|
||||
|
||||
constructor(private router: Router) { };
|
||||
constructor(private router: Router) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loginForm = new FormGroup({
|
||||
|
@ -29,12 +29,16 @@ export class LoginComponent {
|
|||
}
|
||||
|
||||
submit() {
|
||||
this.pb.collection("users").authWithPassword(
|
||||
this.pb
|
||||
.collection('users')
|
||||
.authWithPassword(
|
||||
this.loginForm.get('email')?.value,
|
||||
this.loginForm.get('password')?.value
|
||||
).then(() => {
|
||||
this.loginForm.get('password')?.value,
|
||||
)
|
||||
.then(() => {
|
||||
this.router.navigate(['dashboard']);
|
||||
}).catch(() => {
|
||||
})
|
||||
.catch(() => {
|
||||
this.invalidCredentials = true;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export interface ConfirmationModalDto {
|
||||
title: string,
|
||||
description: string,
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
|
|
@ -8,9 +8,8 @@ describe('NavbarComponent', () => {
|
|||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [NavbarComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
imports: [NavbarComponent],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(NavbarComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
|
|
@ -7,6 +7,4 @@ import { Component, ViewEncapsulation } from '@angular/core';
|
|||
styleUrl: './navbar.component.css',
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class NavbarComponent {
|
||||
|
||||
}
|
||||
export class NavbarComponent {}
|
||||
|
|
|
@ -5,20 +5,26 @@ import {
|
|||
GuardResult,
|
||||
MaybeAsync,
|
||||
Router,
|
||||
RouterStateSnapshot
|
||||
RouterStateSnapshot,
|
||||
} from '@angular/router';
|
||||
import PocketBase from 'pocketbase';
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
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);
|
||||
await pb.collection('users').authRefresh().catch(() => this.router.navigate(['']));
|
||||
await pb
|
||||
.collection('users')
|
||||
.authRefresh()
|
||||
.catch(() => this.router.navigate(['']));
|
||||
|
||||
if (pb.authStore.isValid) {
|
||||
return true;
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import { Injectable } from "@angular/core";
|
||||
import { environment } from "../../environments/environment";
|
||||
import { Injectable } from '@angular/core';
|
||||
import { environment } from '../../environments/environment';
|
||||
import PocketBase, { RecordModel } from 'pocketbase';
|
||||
import { Link } from "../models/link";
|
||||
|
||||
import { Link } from '../models/link';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class LinkService {
|
||||
private pb = new PocketBase(environment.POCKETBASE);
|
||||
|
@ -24,9 +23,9 @@ export class LinkService {
|
|||
|
||||
createLink(link: any): Promise<RecordModel> {
|
||||
return this.pb.collection('links').create({
|
||||
'name': link.name,
|
||||
'link': link.link,
|
||||
'owner': this.pb.authStore.record?.id,
|
||||
name: link.name,
|
||||
link: link.link,
|
||||
owner: this.pb.authStore.record?.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
<p>Your are being redirected.</p>
|
||||
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-5944102294748899"
|
||||
crossorigin="anonymous"></script>
|
||||
<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 () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ViewLinkComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
imports: [ViewLinkComponent],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ViewLinkComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
|
|
@ -6,13 +6,19 @@ import { ActivatedRoute, Router } from '@angular/router';
|
|||
selector: 'app-view-link',
|
||||
imports: [],
|
||||
templateUrl: './view-link.component.html',
|
||||
styleUrl: './view-link.component.css'
|
||||
styleUrl: './view-link.component.css',
|
||||
})
|
||||
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 {
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
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 = {
|
||||
POCKETBASE: 'https://jklink-pocketbase-test.intern.kjan.de'
|
||||
POCKETBASE: 'https://jklink-pocketbase-test.intern.kjan.de',
|
||||
};
|
||||
|
|
|
@ -1,15 +1,21 @@
|
|||
<!doctype html>
|
||||
<html class="h-full" lang="en" data-theme="light">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Jklink</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<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 href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
</head>
|
||||
<body class="h-full base-100 mat-typography" >
|
||||
<base href="/" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<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
|
||||
href="https://fonts.googleapis.com/icon?family=Material+Icons"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
</head>
|
||||
<body class="h-full base-100 mat-typography">
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -2,5 +2,6 @@ import { bootstrapApplication } from '@angular/platform-browser';
|
|||
import { appConfig } from './app/app.config';
|
||||
import { AppComponent } from './app/app.component';
|
||||
|
||||
bootstrapApplication(AppComponent, appConfig)
|
||||
.catch((err) => console.error(err));
|
||||
bootstrapApplication(AppComponent, appConfig).catch((err) =>
|
||||
console.error(err),
|
||||
);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@use '@angular/material' as mat;
|
||||
@use "@angular/material" as mat;
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
"./src/**/*.{html,ts}",
|
||||
],
|
||||
content: ["./src/**/*.{html,ts}"],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
|
@ -10,5 +8,4 @@ module.exports = {
|
|||
preflight: false,
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -6,10 +6,6 @@
|
|||
"outDir": "./out-tsc/app",
|
||||
"types": []
|
||||
},
|
||||
"files": [
|
||||
"src/main.ts"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
"files": ["src/main.ts"],
|
||||
"include": ["src/**/*.d.ts"]
|
||||
}
|
||||
|
|
|
@ -4,12 +4,7 @@
|
|||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine"
|
||||
]
|
||||
"types": ["jasmine"]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
"include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue