v1.0
This commit is contained in:
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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()));
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<iframe #frame></iframe>
|
||||
@@ -0,0 +1,10 @@
|
||||
:host {
|
||||
display: flex;
|
||||
height: calc(100% - 64px);
|
||||
}
|
||||
|
||||
iframe {
|
||||
border: none;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
@@ -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;
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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"]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user