Archived
Private
Public Access
1
0
This commit is contained in:
2022-12-18 13:30:02 +01:00
commit 0e94ffa3c6
85 changed files with 26673 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
<section id="main">
<h1 id="welcome">Willkommen {{crud.user.username}}</h1>
<h2 id="title">Projekte</h2>
<div id="projects">
<span *ngIf="projects.projects.length == 0 && crud.user != undefined" class="disabled">Du hast noch keine Projekte erstellt</span>
<mat-card *ngFor="let project of projects.projects" class="project">
<mat-card-header>
<mat-card-title>{{project.name}}</mat-card-title>
<mat-card-subtitle>{{project.projectId}}</mat-card-subtitle>
</mat-card-header>
<mat-card-actions>
<button mat-button color="primary" (click)="router.navigate(['/project', project.projectId])">Öffnen</button>
<button mat-button color="accent" (click)="editProject(project.projectId)">Bearbeiten</button>
<button mat-button color="warn" (click)="deleteProject(project.projectId)">Löschen</button>
<button mat-icon-button color="warn" *ngIf="project.running" (click)="updateProjectStatus(project.projectId, false)"><mat-icon>pause</mat-icon></button>
<button mat-icon-button color="accent" *ngIf="!project.running" (click)="updateProjectStatus(project.projectId, true)"><mat-icon>play_arrow</mat-icon></button>
</mat-card-actions>
</mat-card>
</div>
</section>

View File

@@ -0,0 +1,46 @@
#main {
height: calc(100vh - 125px);
margin: 30px;
display: flex;
flex-direction: column;
#welcome {
font-size: 30px;
}
#projects {
display: flex;
gap: 30px;
flex-wrap: wrap;
overflow-y: auto;
.project {
width: 350px;
height: 200px;
overflow-y: auto;
opacity: 0;
animation: 200ms project ease-out forwards;
mat-card-actions {
margin-top: auto;
justify-content: space-evenly;
}
}
@for $i from 1 through 100 {
.project:nth-child(#{$i}n) {
animation-delay: #{$i * 0.1}s;
}
}
}
}
@keyframes project {
from {
opacity: 0;
}
to {
opacity: 1;
}
}

View File

@@ -0,0 +1,59 @@
import { Component } from '@angular/core';
import {ProjectService} from "../../services/project.service";
import {CrudService} from "../../services/crud.service";
import {Router} from "@angular/router";
import {MatDialog} from "@angular/material/dialog";
import {DialogComponent} from "../../components/dialog/dialog.component";
import {firstValueFrom} from "rxjs";
import {MatSnackBar} from "@angular/material/snack-bar";
import {TextDialogComponent} from "../../components/text-dialog/text-dialog.component";
import {NavigationComponent} from "../../components/navigation/navigation.component";
@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.scss']
})
export class DashboardComponent {
public constructor(public crud: CrudService, public projects: ProjectService, public router: Router, private dialog: MatDialog, private snackBar: MatSnackBar) {}
public async editProject(projectId: string) {
const dialogRef = this.dialog.open(TextDialogComponent, {
data: {title: "Projekt umbenennen", subtitle: "Name", buttons: [
{text: "Abbrechen", value: false},
{text: "Projekt bearbeiten", value: true, color: 'primary'}
]}
});
const result = await firstValueFrom(dialogRef.afterClosed()) as {success: boolean, data: string};
if (!result?.success) return;
await this.projects.editProject(projectId, result.data);
await this.projects.loadProjects();
this.snackBar.open("Projekt aktualisiert!", undefined, {duration: 2000});
}
public async deleteProject(projectId: string) {
const dialogRef = this.dialog.open(DialogComponent, {
data: {title: "Möchtest du das Projekt wirklich löschen?", subtitle: "Alle gespeicherten Daten gehen dann verloren!", buttons: [
{text: "Abbrechen", value: false},
{text: "Löschen", value: true, color: 'warn'}
]}
});
const result = await firstValueFrom(dialogRef.afterClosed());
if (!result) return;
NavigationComponent.spinnerVisible = true;
await this.projects.deleteProject(projectId);
NavigationComponent.spinnerVisible = false;
await this.projects.loadProjects();
this.snackBar.open("Projekt gelöscht!", undefined, {duration: 2000});
}
public async updateProjectStatus(projectId: string, start: boolean) {
if (start) await this.projects.startProject(projectId);
else await this.projects.stopProject(projectId);
await this.projects.loadProjects();
}
}

View File

@@ -0,0 +1,29 @@
<mat-card>
<mat-card-title>Einloggen</mat-card-title>
<mat-divider></mat-divider>
<mat-card-content>
<form [formGroup]="form" (ngSubmit)="submit()">
<mat-form-field>
<mat-label>E-Mail</mat-label>
<input type="text" matInput formControlName="email" required>
<mat-error *ngIf="form.hasError('required', 'email')">E-Mail ist erforderlich</mat-error>
<mat-error *ngIf="form.hasError('email', 'email') && !form.hasError('required', 'email')">
Bitte geben Sie eine gültige E-Mail-Adresse ein
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>Passwort</mat-label>
<input type="password" matInput formControlName="password" required>
<mat-error *ngIf="form.hasError('required', 'password')">Passwort ist erforderlich</mat-error>
</mat-form-field>
<mat-divider></mat-divider>
<span>Du besitzt keinen Account? <a routerLink="/register">Registrieren</a></span>
<mat-error *ngIf="error">{{error}}</mat-error>
<button type="submit" mat-button>Einloggen</button>
</form>
</mat-card-content>
</mat-card>

View File

@@ -0,0 +1,35 @@
:host {
display: flex;
justify-content: center;
padding-top: 300px;
}
mat-card {
user-select: none;
width: 500px;
mat-card-title {
display: flex;
justify-content: center;
align-items: center;
font-size: 30px;
height: 50px;
}
form {
display: flex;
flex-direction: column;
padding-block: 20px;
gap: 20px;
button {
width: min-content;
align-self: center;
}
}
a {
color: unset;
}
}

View File

@@ -0,0 +1,39 @@
import {Component} from '@angular/core';
import {FormControl, FormGroup, Validators} from "@angular/forms";
import {CrudService} from "../../services/crud.service";
import {User} from "../../entities/user";
import {Router} from "@angular/router";
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss']
})
export class LoginComponent {
public form: FormGroup = new FormGroup({
email: new FormControl('', [Validators.email]),
password: new FormControl(''),
});
public error: string;
public constructor(private crud: CrudService, private router: Router) {
this.form.reset();
this.error = "";
}
public async submit() {
this.error = "";
const email = this.form.get("email").value;
const password = this.form.get("password").value;
const user: User = {email: email, password: password};
const result = await this.crud.sendPostRequest<{token: string}>("users/login", user);
if (result.success) {
this.crud.setAuthKey(result.content.token);
await this.crud.loadUser(true);
await this.router.navigate(["/dashboard"]);
}else {
this.error = "E-Mail oder Passwort ist falsch";
}
}
}

View File

@@ -0,0 +1,57 @@
<mat-card id="content">
<mat-card-header>
<mat-card-title>Profil</mat-card-title>
<mat-card-subtitle>Einstellungen</mat-card-subtitle>
</mat-card-header>
<mat-card-content id="main">
<form [formGroup]="form" (ngSubmit)="update()" id="form">
<mat-form-field>
<mat-label>E-Mail</mat-label>
<input type="text" matInput formControlName="email" required>
<mat-error *ngIf="form.hasError('required', 'email')">E-Mail ist erforderlich</mat-error>
<mat-error *ngIf="form.hasError('email', 'email') && !form.hasError('required', 'email')">
Bitte geben Sie eine gültige E-Mail-Adresse ein
</mat-error>
<mat-error *ngIf="form.hasError('maxlength', 'email') && !form.hasError('required', 'email')">
Der eingegebene Wert ist zu lang
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>Benutzername</mat-label>
<input type="text" matInput formControlName="username" required>
<mat-error *ngIf="form.hasError('required', 'username')">Benutzername ist erforderlich</mat-error>
<mat-error *ngIf="form.hasError('maxlength', 'username') && !form.hasError('required', 'username')">
Der eingegebene Wert ist zu lang
</mat-error>
</mat-form-field>
<div>
<mat-form-field>
<mat-label>Passwort</mat-label>
<input type="password" matInput formControlName="password">
<mat-error *ngIf="form.hasError('maxlength', 'password')">
Der eingegebene Wert ist zu lang
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>Passwort wiederholen</mat-label>
<input type="password" matInput formControlName="passwordRepeat">
<mat-error *ngIf="form.hasError('maxlength', 'passwordRepeat')">
Der eingegebene Wert ist zu lang
</mat-error>
</mat-form-field>
</div>
<mat-error>{{error}}</mat-error>
<button type="submit" hidden></button>
</form>
</mat-card-content>
<mat-card-actions id="actions">
<button mat-button color="primary" (click)="update()">Account aktualisieren</button>
<button mat-button color="warn" (click)="delete()">Account löschen</button>
</mat-card-actions>
</mat-card>

View File

@@ -0,0 +1,30 @@
:host {
display: grid;
place-items: center;
height: calc(100% - 64px);
}
#content {
width: 600px;
#main {
margin-top: 30px;
margin-bottom: 30px;
#form {
display: flex;
flex-direction: column;
gap: 20px;
div {
display: grid;
gap: 10px;
grid-template-columns: repeat(2, 1fr);
}
}
}
#actions {
justify-content: space-evenly;
}
}

View File

@@ -0,0 +1,86 @@
import {Component} from '@angular/core';
import {CrudService} from "../../services/crud.service";
import {FormControl, FormGroup, Validators} from "@angular/forms";
import {MatDialog} from "@angular/material/dialog";
import {DialogComponent} from "../../components/dialog/dialog.component";
import {firstValueFrom} from "rxjs";
import {User} from "../../entities/user";
import {MatSnackBar} from "@angular/material/snack-bar";
import {Router} from "@angular/router";
@Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.scss']
})
export class ProfileComponent {
public form: FormGroup = new FormGroup({
email: new FormControl('', [Validators.email, Validators.maxLength(255)]),
username: new FormControl('', [Validators.maxLength(255)]),
password: new FormControl('', [Validators.maxLength(255)]),
passwordRepeat: new FormControl('', [Validators.maxLength(255)])
});
public error: string;
public constructor(public crud: CrudService, private router: Router, public dialog: MatDialog, private snackBar: MatSnackBar) {
this.form.get("email").setValue(this.crud.user?.email);
this.form.get("username").setValue(this.crud.user?.username);
}
public async update() {
if (!this.form.valid) return;
const result = await this.openDialog("Änderungen speichern?");
if (!result) return;
this.error = "";
const email = this.form.get("email").value;
const username = this.form.get("username").value;
const password = this.form.get("password").value;
const passwordRepeat = this.form.get("passwordRepeat").value;
if (password != passwordRepeat) {
this.error = "Passwörter stimmen nicht überein";
return;
}
const user: User = {userId: this.crud.user.userId, email, username, password};
const response = await this.crud.sendPutRequest("users", user);
if (!response.success) {
this.error = "Aktualiserung fehlgeschlagen!";
return;
}
await this.crud.loadUser(true);
this.form.reset();
this.snackBar.open("Account aktualisiert!", undefined, {duration: 2000});
await this.router.navigate(["dashboard"]);
}
public async delete() {
const result = await this.openDialog("Möchtest du deinen Account wirklich löschen?", "All deine Projekte werden für immer gelöscht!", ['', 'warn']);
if (!result) return;
await this.crud.sendDeleteRequest("users");
this.crud.setAuthKey(undefined);
this.crud.user = undefined;
this.snackBar.open("Account gelöscht!", undefined, {duration: 2000});
await this.router.navigate(["login"]);
}
private openDialog(title: string, subtitle?: string, colors?: string[]): Promise<boolean> {
if (colors == undefined) colors = ['', 'accent'];
return new Promise<boolean>(async (resolve) => {
const dialogRef = this.dialog.open(DialogComponent, {
data: {title, subtitle, buttons: [
{text: "Abbrechen", value: false, color: colors[0]},
{text: "Bestätigen", value: true, color: colors[1]},
]}
});
resolve(await firstValueFrom(dialogRef.afterClosed()));
})
}
}

View File

@@ -0,0 +1 @@
<iframe #frame></iframe>

View File

@@ -0,0 +1,10 @@
:host {
display: flex;
height: calc(100% - 64px);
}
iframe {
border: none;
flex-grow: 1;
flex-shrink: 1;
}

View File

@@ -0,0 +1,23 @@
import {Component, ElementRef, ViewChild} from '@angular/core';
import {ActivatedRoute} from "@angular/router";
import {CrudService} from "../../services/crud.service";
@Component({
selector: 'app-project',
templateUrl: './project.component.html',
styleUrls: ['./project.component.scss']
})
export class ProjectComponent {
@ViewChild('frame') frame: ElementRef;
public constructor(public route: ActivatedRoute, public crud: CrudService) {
setTimeout(this.getRoute.bind(this), 0);
}
public getRoute() {
this.route.params.subscribe(params => {
this.frame.nativeElement.src = this.crud.backendUrl + 'projects/' + params['id'] + '/url?token=' + this.crud.authKey;
})
}
}

View File

@@ -0,0 +1,53 @@
<mat-card>
<mat-card-title>Registrieren</mat-card-title>
<mat-divider></mat-divider>
<mat-card-content>
<form [formGroup]="form" (ngSubmit)="submit()">
<mat-form-field>
<mat-label>E-Mail</mat-label>
<input type="text" matInput formControlName="email" required>
<mat-error *ngIf="form.hasError('required', 'email')">E-Mail ist erforderlich</mat-error>
<mat-error *ngIf="form.hasError('email', 'email') && !form.hasError('required', 'email')">
Bitte geben Sie eine gültige E-Mail-Adresse ein
</mat-error>
<mat-error *ngIf="form.hasError('maxlength', 'email') && !form.hasError('required', 'email')">
Der eingegebene Wert ist zu lang
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>Benutzername</mat-label>
<input type="text" matInput formControlName="username" required>
<mat-error *ngIf="form.hasError('required', 'username')">Benutzername ist erforderlich</mat-error>
<mat-error *ngIf="form.hasError('maxlength', 'username') && !form.hasError('required', 'username')">
Der eingegebene Wert ist zu lang
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>Passwort</mat-label>
<input type="password" matInput formControlName="password" required>
<mat-error *ngIf="form.hasError('required', 'password')">Passwort ist erforderlich</mat-error>
<mat-error *ngIf="form.hasError('maxlength', 'password') && !form.hasError('required', 'password')">
Der eingegebene Wert ist zu lang
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>Passwort wiederholen</mat-label>
<input type="password" matInput formControlName="passwordRepeat" required>
<mat-error *ngIf="form.hasError('required', 'passwordRepeat')">Passwort ist erforderlich</mat-error>
<mat-error *ngIf="form.hasError('maxlength', 'passwordRepeat') && !form.hasError('required', 'passwordRepeat')">
Der eingegebene Wert ist zu lang
</mat-error>
</mat-form-field>
<mat-divider></mat-divider>
<span>Du hast bereits einen Account? <a routerLink="/login">Einloggen</a></span>
<mat-error *ngIf="error">{{error}}</mat-error>
<button type="submit" mat-button>Registrieren</button>
</form>
</mat-card-content>
</mat-card>

View File

@@ -0,0 +1,35 @@
:host {
display: flex;
justify-content: center;
padding-top: 300px;
}
mat-card {
user-select: none;
width: 500px;
mat-card-title {
display: flex;
justify-content: center;
align-items: center;
font-size: 30px;
height: 50px;
}
form {
display: flex;
flex-direction: column;
padding-block: 20px;
gap: 20px;
button {
width: min-content;
align-self: center;
}
}
a {
color: unset;
}
}

View File

@@ -0,0 +1,46 @@
import { Component } from '@angular/core';
import {FormControl, FormGroup, Validators} from "@angular/forms";
import {CrudService} from "../../services/crud.service";
import {Router} from "@angular/router";
import {User} from "../../entities/user";
@Component({
selector: 'app-register',
templateUrl: './register.component.html',
styleUrls: ['./register.component.scss']
})
export class RegisterComponent {
public form: FormGroup = new FormGroup({
email: new FormControl('', [Validators.email, Validators.maxLength(255)]),
username: new FormControl('', [Validators.maxLength(255)]),
password: new FormControl('', [Validators.maxLength(255)]),
passwordRepeat: new FormControl('', [Validators.maxLength(255)])
});
public error: string;
public constructor(private crud: CrudService, private router: Router) {}
public async submit() {
this.error = "";
const email = this.form.get("email").value;
const username = this.form.get("username").value;
const password = this.form.get("password").value;
const passwordRepeat = this.form.get("passwordRepeat").value;
if (password != passwordRepeat) {
this.error = "Passwörter stimmen nicht überein";
return;
}
const user: User = {email, username, password};
const result = await this.crud.sendPostRequest<{token: string}>("users/register", user);
if (!result.success) {
this.error = "Registrierung fehlgeschlagen";
return;
}
this.crud.setAuthKey(result.content.token);
await this.crud.loadUser(true);
await this.router.navigate(["/dashboard"]);
}
}