Merge pull request 'main' (#6) from main into prod
All checks were successful
Release / Release (push) Successful in 2m6s
All checks were successful
Release / Release (push) Successful in 2m6s
Reviewed-on: #6
This commit is contained in:
commit
5b8efc8921
25 changed files with 385 additions and 98 deletions
32
.gitea/workflows/release.yml
Normal file
32
.gitea/workflows/release.yml
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
name: Release
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- prod
|
||||||
|
|
||||||
|
env:
|
||||||
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read # for checkout
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.ref }} # Ensure each branch has its own group
|
||||||
|
cancel-in-progress: false # Prevent new runs from canceling in-progress runs
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
name: Release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write # to be able to publish a GitHub release
|
||||||
|
issues: write # to be able to comment on released issues
|
||||||
|
pull-requests: write # to be able to comment on released pull requests
|
||||||
|
id-token: write # to enable use of OIDC for npm provenance
|
||||||
|
steps:
|
||||||
|
- name: Create Release
|
||||||
|
uses: https://git.kjan.de/actions/semantic-release@main
|
||||||
|
with:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
|
|
@ -27,7 +27,8 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
"src/styles.css"
|
"@angular/material/prebuilt-themes/azure-blue.css",
|
||||||
|
"src/styles.scss"
|
||||||
],
|
],
|
||||||
"scripts": []
|
"scripts": []
|
||||||
},
|
},
|
||||||
|
@ -97,6 +98,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
|
"@angular/material/prebuilt-themes/azure-blue.css",
|
||||||
"src/styles.css"
|
"src/styles.css"
|
||||||
],
|
],
|
||||||
"scripts": []
|
"scripts": []
|
||||||
|
|
41
package-lock.json
generated
41
package-lock.json
generated
|
@ -9,10 +9,12 @@
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^19.0.0",
|
"@angular/animations": "^19.0.0",
|
||||||
|
"@angular/cdk": "^19.1.0",
|
||||||
"@angular/common": "^19.0.0",
|
"@angular/common": "^19.0.0",
|
||||||
"@angular/compiler": "^19.0.0",
|
"@angular/compiler": "^19.0.0",
|
||||||
"@angular/core": "^19.0.0",
|
"@angular/core": "^19.0.0",
|
||||||
"@angular/forms": "^19.0.0",
|
"@angular/forms": "^19.0.0",
|
||||||
|
"@angular/material": "^19.1.0",
|
||||||
"@angular/platform-browser": "^19.0.0",
|
"@angular/platform-browser": "^19.0.0",
|
||||||
"@angular/platform-browser-dynamic": "^19.0.0",
|
"@angular/platform-browser-dynamic": "^19.0.0",
|
||||||
"@angular/router": "^19.0.0",
|
"@angular/router": "^19.0.0",
|
||||||
|
@ -390,6 +392,23 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@angular/cdk": {
|
||||||
|
"version": "19.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-19.1.0.tgz",
|
||||||
|
"integrity": "sha512-h7VSaMA/vFHb7u1bwoHKl3L3mZLIcXNZw6v7Nei9ITfEo1PfSKbrYhleeqpNikzE+LxNDKJrbZtpAckSYHblmA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"parse5": "^7.1.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/common": "^19.0.0 || ^20.0.0",
|
||||||
|
"@angular/core": "^19.0.0 || ^20.0.0",
|
||||||
|
"rxjs": "^6.5.3 || ^7.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@angular/cli": {
|
"node_modules/@angular/cli": {
|
||||||
"version": "19.1.2",
|
"version": "19.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-19.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-19.1.2.tgz",
|
||||||
|
@ -517,6 +536,24 @@
|
||||||
"rxjs": "^6.5.3 || ^7.4.0"
|
"rxjs": "^6.5.3 || ^7.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@angular/material": {
|
||||||
|
"version": "19.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/material/-/material-19.1.0.tgz",
|
||||||
|
"integrity": "sha512-LTQBWtuRGjNpA7ceQu9PyiUUq0KLfBP8LvL04hLFbEsZ0fIPQ/OO/Otn67/7TMtnHRFnPeezYPHcAHBhiNlR4A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.3.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/animations": "^19.0.0 || ^20.0.0",
|
||||||
|
"@angular/cdk": "19.1.0",
|
||||||
|
"@angular/common": "^19.0.0 || ^20.0.0",
|
||||||
|
"@angular/core": "^19.0.0 || ^20.0.0",
|
||||||
|
"@angular/forms": "^19.0.0 || ^20.0.0",
|
||||||
|
"@angular/platform-browser": "^19.0.0 || ^20.0.0",
|
||||||
|
"rxjs": "^6.5.3 || ^7.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@angular/platform-browser": {
|
"node_modules/@angular/platform-browser": {
|
||||||
"version": "19.1.1",
|
"version": "19.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-19.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-19.1.1.tgz",
|
||||||
|
@ -6663,7 +6700,7 @@
|
||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.12"
|
"node": ">=0.12"
|
||||||
},
|
},
|
||||||
|
@ -10387,7 +10424,7 @@
|
||||||
"version": "7.2.1",
|
"version": "7.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz",
|
||||||
"integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==",
|
"integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"entities": "^4.5.0"
|
"entities": "^4.5.0"
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,10 +11,12 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^19.0.0",
|
"@angular/animations": "^19.0.0",
|
||||||
|
"@angular/cdk": "^19.1.0",
|
||||||
"@angular/common": "^19.0.0",
|
"@angular/common": "^19.0.0",
|
||||||
"@angular/compiler": "^19.0.0",
|
"@angular/compiler": "^19.0.0",
|
||||||
"@angular/core": "^19.0.0",
|
"@angular/core": "^19.0.0",
|
||||||
"@angular/forms": "^19.0.0",
|
"@angular/forms": "^19.0.0",
|
||||||
|
"@angular/material": "^19.1.0",
|
||||||
"@angular/platform-browser": "^19.0.0",
|
"@angular/platform-browser": "^19.0.0",
|
||||||
"@angular/platform-browser-dynamic": "^19.0.0",
|
"@angular/platform-browser-dynamic": "^19.0.0",
|
||||||
"@angular/router": "^19.0.0",
|
"@angular/router": "^19.0.0",
|
||||||
|
@ -41,4 +43,4 @@
|
||||||
"tailwindcss": "^3.4.17",
|
"tailwindcss": "^3.4.17",
|
||||||
"typescript": "~5.6.2"
|
"typescript": "~5.6.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,7 +2,8 @@ import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
||||||
import { provideRouter } from '@angular/router';
|
import { provideRouter } from '@angular/router';
|
||||||
|
|
||||||
import { routes } from './app.routes';
|
import { routes } from './app.routes';
|
||||||
|
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
|
||||||
|
|
||||||
export const appConfig: ApplicationConfig = {
|
export const appConfig: ApplicationConfig = {
|
||||||
providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes)]
|
providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideAnimationsAsync()]
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,6 +2,8 @@ import { Routes } from '@angular/router';
|
||||||
import { LoginComponent } from './login/login.component';
|
import { LoginComponent } from './login/login.component';
|
||||||
import { DashboardComponent } from './dashboard/dashboard.component';
|
import { DashboardComponent } from './dashboard/dashboard.component';
|
||||||
import { AuthGuard } from './service/auth.service';
|
import { AuthGuard } from './service/auth.service';
|
||||||
|
import { CreateLinkComponent } from './create-link/create-link.component';
|
||||||
|
import { ViewLinkComponent } from './view-link/view-link.component';
|
||||||
|
|
||||||
export const routes: Routes = [
|
export const routes: Routes = [
|
||||||
{
|
{
|
||||||
|
@ -13,6 +15,15 @@ export const routes: Routes = [
|
||||||
component: DashboardComponent,
|
component: DashboardComponent,
|
||||||
canActivate: [AuthGuard],
|
canActivate: [AuthGuard],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'create-link',
|
||||||
|
component: CreateLinkComponent,
|
||||||
|
canActivate: [AuthGuard],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ':link',
|
||||||
|
component: ViewLinkComponent,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "**",
|
path: "**",
|
||||||
redirectTo: "",
|
redirectTo: "",
|
||||||
|
|
0
src/app/create-link/create-link.component.css
Normal file
0
src/app/create-link/create-link.component.css
Normal file
19
src/app/create-link/create-link.component.html
Normal file
19
src/app/create-link/create-link.component.html
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<div class="mx-auto container">
|
||||||
|
<app-navbar></app-navbar>
|
||||||
|
<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-label>Name</mat-label>
|
||||||
|
<input formControlName="name" matInput placeholder="My Awesome link">
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<mat-error>{{errorMessages['link']}}</mat-error>
|
||||||
|
<mat-label>Link</mat-label>
|
||||||
|
<input formControlName="link" matInput type="url" placeholder="https://kjan.de">
|
||||||
|
</mat-form-field>
|
||||||
|
<button mat-flat-button type="submit" (click)="submit()">Create</button>
|
||||||
|
</form>
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
23
src/app/create-link/create-link.component.spec.ts
Normal file
23
src/app/create-link/create-link.component.spec.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { CreateLinkComponent } from './create-link.component';
|
||||||
|
|
||||||
|
describe('CreateLinkComponent', () => {
|
||||||
|
let component: CreateLinkComponent;
|
||||||
|
let fixture: ComponentFixture<CreateLinkComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [CreateLinkComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(CreateLinkComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
74
src/app/create-link/create-link.component.ts
Normal file
74
src/app/create-link/create-link.component.ts
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { NavbarComponent } from '../navbar/navbar.component';
|
||||||
|
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
|
import { LinkService } from '../service/link.service';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { MatInputModule, MatLabel } from '@angular/material/input';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { debounceTime } from 'rxjs';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-create-link',
|
||||||
|
imports: [NavbarComponent, ReactiveFormsModule, MatCardModule, MatFormFieldModule, MatInputModule, MatButtonModule],
|
||||||
|
templateUrl: './create-link.component.html',
|
||||||
|
styleUrl: './create-link.component.css'
|
||||||
|
})
|
||||||
|
export class CreateLinkComponent {
|
||||||
|
public createLinkForm!: FormGroup;
|
||||||
|
public requestFailed: boolean = false;
|
||||||
|
public errorMessages: Record<string, string> = {};
|
||||||
|
|
||||||
|
constructor(private linkService: LinkService, private router: Router) { }
|
||||||
|
|
||||||
|
private validationErrorMessages: Record<string, string> = {
|
||||||
|
required: "This field is required",
|
||||||
|
pattern: "This must be a valid url",
|
||||||
|
};
|
||||||
|
|
||||||
|
updateErrorMessages(): void {
|
||||||
|
this.errorMessages = {};
|
||||||
|
|
||||||
|
Object.keys(this.createLinkForm.controls).forEach(field => {
|
||||||
|
const control = this.createLinkForm.get(field);
|
||||||
|
|
||||||
|
if (control && control.errors) {
|
||||||
|
this.errorMessages[field] = Object.keys(control.errors)
|
||||||
|
.map(errorKey => this.validationErrorMessages[errorKey] || `Unknown error: ${errorKey}`)
|
||||||
|
.join(' ');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
back() {
|
||||||
|
this.router.navigate(['dashboard']);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.createLinkForm = new FormGroup({
|
||||||
|
name: new FormControl('', Validators.required),
|
||||||
|
link: new FormControl('', [Validators.required, Validators.pattern(/((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)/)]),
|
||||||
|
});
|
||||||
|
this.createLinkForm.valueChanges.subscribe(() => {
|
||||||
|
this.updateErrorMessages();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
submit() {
|
||||||
|
|
||||||
|
if (!this.createLinkForm.valid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.requestFailed = false;
|
||||||
|
this.linkService.createLink({
|
||||||
|
name: this.createLinkForm.get('name')?.value,
|
||||||
|
link: this.createLinkForm.get('link')?.value,
|
||||||
|
}).catch(() => this.requestFailed = true).finally(() => {
|
||||||
|
if (!this.requestFailed) {
|
||||||
|
this.router.navigate(['dashboard']);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,27 +1,56 @@
|
||||||
<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">
|
||||||
<table class="table">
|
<button mat-flat-button color="secondary" class="my-3" (click)="createLink()">Create new Link</button>
|
||||||
<!-- head -->
|
|
||||||
<thead>
|
<mat-card appearance="outlined">
|
||||||
<tr>
|
<mat-card-content>
|
||||||
<th></th>
|
<table mat-table [dataSource]="links" class="mat-elevation-z8">
|
||||||
<th>Name</th>
|
|
||||||
<th>Url</th>
|
<!--- Note that these columns can be defined in any order.
|
||||||
<th>Short Url</th>
|
The actual rendered columns are set as a property on the row definition" -->
|
||||||
</tr>
|
|
||||||
</thead>
|
<!-- Position Column -->
|
||||||
<tbody>
|
<ng-container matColumnDef="id">
|
||||||
<!-- row 1 -->
|
<th mat-header-cell *matHeaderCellDef> ID </th>
|
||||||
@for (link of links; track link) {
|
<td mat-cell *matCellDef="let element">
|
||||||
<tr class="bg-base-200">
|
<a href="{{element.id}}">
|
||||||
<th>{{link.id}}</th>
|
{{element.id}}
|
||||||
<td>{{link.name}}</td>
|
</a>
|
||||||
<td><a href="{{link.link}}">{{link.link}}</a></td>
|
</td>
|
||||||
<td>ndy</td>
|
</ng-container>
|
||||||
</tr>
|
|
||||||
}
|
<!-- Name Column -->
|
||||||
</tbody>
|
<ng-container matColumnDef="name">
|
||||||
</table>
|
<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>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Symbol Column -->
|
||||||
|
<ng-container matColumnDef="shortLink">
|
||||||
|
<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>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="actions">
|
||||||
|
<th mat-header-cell *matHeaderCellDef> Actions </th>
|
||||||
|
<td mat-cell *matCellDef="let element">
|
||||||
|
<button mat-flat-button color="warn" (click)="deleteLink(element.id)">Delete</button>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||||
|
</table>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,21 +2,46 @@ import { Component } from '@angular/core';
|
||||||
import { LinkService } from '../service/link.service';
|
import { LinkService } from '../service/link.service';
|
||||||
import { RecordModel } from 'pocketbase';
|
import { RecordModel } from 'pocketbase';
|
||||||
import { NavbarComponent } from '../navbar/navbar.component';
|
import { NavbarComponent } from '../navbar/navbar.component';
|
||||||
|
import { Router, RouterLink } from '@angular/router';
|
||||||
|
import { MatRow, MatTableModule } from '@angular/material/table';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { Link } from '../models/link';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-dashboard',
|
selector: 'app-dashboard',
|
||||||
imports: [NavbarComponent],
|
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: any[] = [];
|
public links: Link[] = [];
|
||||||
|
displayedColumns: string[] = ['id', 'name', 'link', 'shortLink', 'actions'];
|
||||||
|
|
||||||
constructor(private linkService: LinkService) { };
|
constructor(private linkService: LinkService, private router: Router) { };
|
||||||
|
|
||||||
|
copyLink(id: string) {
|
||||||
|
navigator.clipboard.writeText(this.getShortLink(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
getShortLink(id: string): string {
|
||||||
|
return window.location.hostname + '/' + id;
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.linkService.getLinks().then(links => {
|
this.linkService.getLinks().then(links => {
|
||||||
this.links = links;
|
this.links = links;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createLink() {
|
||||||
|
this.router.navigate(['create-link']);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteLink(id: string) {
|
||||||
|
this.linkService.deleteLink(id); // TODO Check if something went wrong
|
||||||
|
this.links = this.links.filter(link => {
|
||||||
|
return link.id != id;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
6
src/app/models/link.ts
Normal file
6
src/app/models/link.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
export interface Link {
|
||||||
|
id: string;
|
||||||
|
link: string;
|
||||||
|
name: string;
|
||||||
|
owner: string;
|
||||||
|
}
|
|
@ -1,52 +0,0 @@
|
||||||
<div class="navbar bg-base-100 rounded-2xl shadow-xl my-3 z-100">
|
|
||||||
<div class="navbar-start">
|
|
||||||
<div class="dropdown">
|
|
||||||
<div tabindex="0" role="button" class="btn btn-ghost lg:hidden">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="h-5 w-5"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor">
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M4 6h16M4 12h8m-8 6h16" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<ul
|
|
||||||
tabindex="0"
|
|
||||||
class="menu menu-sm dropdown-content bg-base-100 rounded-box z-[10000] mt-3 w-52 p-2 shadow">
|
|
||||||
<li><a>Dashboard</a></li>
|
|
||||||
<li>
|
|
||||||
<a>Parent</a>
|
|
||||||
<ul class="p-2">
|
|
||||||
<li><a>Submenu 1</a></li>
|
|
||||||
<li><a>Submenu 2</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><a>Item 3</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<a class="btn btn-ghost text-xl">jklink</a>
|
|
||||||
</div>
|
|
||||||
<div class="navbar-center hidden lg:flex">
|
|
||||||
<ul class="menu menu-horizontal px-1">
|
|
||||||
<li><a>Item 1</a></li>
|
|
||||||
<li>
|
|
||||||
<details>
|
|
||||||
<summary>Parent</summary>
|
|
||||||
<ul class="p-2">
|
|
||||||
<li><a>Submenu 1</a></li>
|
|
||||||
<li><a>Submenu 2</a></li>
|
|
||||||
</ul>
|
|
||||||
</details>
|
|
||||||
</li>
|
|
||||||
<li><a>Item 3</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="navbar-end">
|
|
||||||
<a class="btn">Button</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,6 +1,7 @@
|
||||||
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";
|
||||||
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
|
@ -9,7 +10,23 @@ import PocketBase, { RecordModel } from 'pocketbase';
|
||||||
export class LinkService {
|
export class LinkService {
|
||||||
private pb = new PocketBase(environment.POCKETBASE);
|
private pb = new PocketBase(environment.POCKETBASE);
|
||||||
|
|
||||||
getLinks(): Promise<RecordModel[]> {
|
getLinks(): Promise<Link[]> {
|
||||||
return this.pb.collection('links').getFullList();
|
return this.pb.collection<Link>('links').getFullList<Link>();
|
||||||
|
}
|
||||||
|
|
||||||
|
getLink(id: string): Promise<Link> {
|
||||||
|
return this.pb.collection('links').getOne<Link>(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteLink(id: string) {
|
||||||
|
this.pb.collection('links').delete(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
createLink(link: any): Promise<RecordModel> {
|
||||||
|
return this.pb.collection('links').create({
|
||||||
|
'name': link.name,
|
||||||
|
'link': link.link,
|
||||||
|
'owner': this.pb.authStore.record?.id,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
0
src/app/view-link/view-link.component.css
Normal file
0
src/app/view-link/view-link.component.css
Normal file
3
src/app/view-link/view-link.component.html
Normal file
3
src/app/view-link/view-link.component.html
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<p>Your are being redirected.</p>
|
||||||
|
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-5944102294748899"
|
||||||
|
crossorigin="anonymous"></script>
|
23
src/app/view-link/view-link.component.spec.ts
Normal file
23
src/app/view-link/view-link.component.spec.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ViewLinkComponent } from './view-link.component';
|
||||||
|
|
||||||
|
describe('ViewLinkComponent', () => {
|
||||||
|
let component: ViewLinkComponent;
|
||||||
|
let fixture: ComponentFixture<ViewLinkComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ViewLinkComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ViewLinkComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
19
src/app/view-link/view-link.component.ts
Normal file
19
src/app/view-link/view-link.component.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { LinkService } from '../service/link.service';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-view-link',
|
||||||
|
imports: [],
|
||||||
|
templateUrl: './view-link.component.html',
|
||||||
|
styleUrl: './view-link.component.css'
|
||||||
|
})
|
||||||
|
export class ViewLinkComponent {
|
||||||
|
constructor(private linkService: LinkService, private route: ActivatedRoute, private router: Router) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.linkService.getLink(this.route.snapshot.params['link']).then(link => {
|
||||||
|
window.location.href = link.link;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,3 @@
|
||||||
export const environment = {
|
export const environment = {
|
||||||
POCKETBASE: 'http://pocketbase-yocs0oko0o8cws44kw8gk8g8.192.168.178.105.sslip.io/'
|
POCKETBASE: 'https://jklink-pocketbase-test.intern.kjan.de'
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
export const environment = {
|
export const environment = {
|
||||||
POCKETBASE: 'http://pocketbase-yocs0oko0o8cws44kw8gk8g8.192.168.178.105.sslip.io/'
|
POCKETBASE: 'https://jklink-pocketbase-test.intern.kjan.de'
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,8 +6,10 @@
|
||||||
<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 href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body class="h-full base-100" >
|
<body class="h-full base-100 mat-typography" >
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
@tailwind base;
|
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
|
22
src/styles.scss
Normal file
22
src/styles.scss
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
@use '@angular/material' as mat;
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
$theme: mat.define-theme();
|
||||||
|
|
||||||
|
html {
|
||||||
|
@include mat.all-component-themes($theme);
|
||||||
|
// This line allows you to use color="..." for your toolbar.
|
||||||
|
@include mat.color-variants-backwards-compatibility($theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: Roboto, "Helvetica Neue", sans-serif;
|
||||||
|
}
|
|
@ -6,14 +6,9 @@ module.exports = {
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {},
|
||||||
},
|
},
|
||||||
daisyui: {
|
corePlugins: {
|
||||||
styled: true,
|
preflight: false,
|
||||||
themes: true,
|
|
||||||
base: true,
|
|
||||||
utils: true,
|
|
||||||
logs: true,
|
|
||||||
rtl: false
|
|
||||||
},
|
},
|
||||||
plugins: [require("@tailwindcss/typography"), require("daisyui")],
|
plugins: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue