Merge remote-tracking branch 'origin/master'

pull/8/head
Max Ehrlicher-Schmidt 4 years ago
commit 12643851c3

@ -0,0 +1,37 @@
name: Build Docker Image
on:
push:
branches: [ main, master ]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Copy Repo Files
uses: actions/checkout@master
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Portus
uses: docker/login-action@v1
with:
registry: https://flotte-docker-registry.spdns.org/
username: ${{ secrets.PORTUS_USERNAME }}
password: ${{ secrets.PORTUS_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile
platforms: linux/amd64
push: true
tags: |
flotte-docker-registry.spdns.org/frontend-server:latest

@ -1,6 +1,7 @@
FROM node:14.14.0-alpine3.10 AS builder FROM node:14.14.0-alpine3.10 AS builder
WORKDIR / WORKDIR /
COPY package.json package-lock.json ./ COPY package.json package-lock.json ./
RUN apk add --no-cache python3 build-base
RUN npm install && npm install -g @angular/cli && mkdir frontend RUN npm install && npm install -g @angular/cli && mkdir frontend
RUN mv node_modules ./frontend RUN mv node_modules ./frontend
WORKDIR /frontend WORKDIR /frontend

@ -1,6 +1,6 @@
# FlotteFrontend # FlotteFrontend
[![Build Status](https://travis-ci.com/fLotte-meets-HWR-DB/frontend.svg?token=YfRmpHAXqyUafCgSEexw&branch=master)](https://travis-ci.com/fLotte-meets-HWR-DB/frontend) ![Build Docker Image](https://github.com/fLotte-meets-HWR-DB/frontend/workflows/Build%20Docker%20Image/badge.svg)
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 10.0.8. This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 10.0.8.

@ -12,10 +12,12 @@ import { LendingStationsComponent } from './pages/tables/lending-stations/lendin
import { ParticipantsComponent } from './pages/tables/participants/participants.component'; import { ParticipantsComponent } from './pages/tables/participants/participants.component';
import { TimeFramesComponent } from './pages/tables/time-frames/time-frames.component'; import { TimeFramesComponent } from './pages/tables/time-frames/time-frames.component';
import {AuthGuard} from './helper/auth.guard'; import {AuthGuard} from './helper/auth.guard';
import { ProfileComponent } from './pages/profile/profile.component';
const routes: Routes = [ const routes: Routes = [
{ path: 'login', component: LoginComponent }, { path: 'login', component: LoginComponent },
{ path: 'tableOverview', component: TableOverviewComponent, canActivate: [AuthGuard]}, { path: 'tableOverview', component: TableOverviewComponent, canActivate: [AuthGuard]},
{ path: 'profile', component: ProfileComponent, canActivate: [AuthGuard]},
{ path: 'table/bikes', component: BikesComponent, canActivate: [AuthGuard] }, { path: 'table/bikes', component: BikesComponent, canActivate: [AuthGuard] },
{ path: 'bike/:id', component: BikeComponent, canActivate: [AuthGuard] }, { path: 'bike/:id', component: BikeComponent, canActivate: [AuthGuard] },
{ path: 'table/participants', component: ParticipantsComponent, canActivate: [AuthGuard] }, { path: 'table/participants', component: ParticipantsComponent, canActivate: [AuthGuard] },

@ -65,6 +65,7 @@ import { DateRangeCellComponent } from './components/tableComponents/date-range-
import { SelectObjectDialogComponent } from './components/select-object-dialog/select-object-dialog.component'; import { SelectObjectDialogComponent } from './components/select-object-dialog/select-object-dialog.component';
import { AutocompleteSelectComponent } from './components/autocomplete-select/autocomplete-select.component'; import { AutocompleteSelectComponent } from './components/autocomplete-select/autocomplete-select.component';
import { LendingStationComponent } from './pages/dataPages/lending-station/lending-station.component'; import { LendingStationComponent } from './pages/dataPages/lending-station/lending-station.component';
import { ProfileComponent } from './pages/profile/profile.component';
@ -94,6 +95,7 @@ import { LendingStationComponent } from './pages/dataPages/lending-station/lendi
SelectObjectDialogComponent, SelectObjectDialogComponent,
AutocompleteSelectComponent, AutocompleteSelectComponent,
LendingStationComponent, LendingStationComponent,
ProfileComponent,
], ],
imports: [ imports: [
BrowserModule, BrowserModule,

@ -1,4 +1,4 @@
<div (click)="testSnackBar()"> <div routerLink = "/profile" >
<img class="navbar-photo" src={{profileURL}}> <img class="navbar-photo" src={{profileURL}}>
<p>{{name}}</p> <p>{{name}}</p>
<p>{{email}}</p> <p>{{email}}</p>

@ -1,4 +1,96 @@
<app-table <div id="login-form">
<h1>Profil aktualisieren</h1>
<mat-form-field class="currentPW" (keyup.enter)="login()">
<mat-label>Aktuelles Passwort</mat-label>
<input
matInput
[type]="hide ? 'password' : 'text'"
[formControl]="password"
/>
<mat-error *ngIf="password.hasError('required')">
Bitte geben Sie Ihr Passwort ein.
</mat-error>
<button
mat-icon-button
matSuffix
(click)="hide = !hide"
[attr.aria-label]="'Hide password'"
[attr.aria-pressed]="hide"
>
<mat-icon>{{ hide ? "visibility_off" : "visibility" }}</mat-icon>
</button>
</mat-form-field>
<!--<mat-form-field (keyup.enter)="login()">
<mat-label>E-Mail-Adresse eingeben</mat-label>
<input matInput placeholder="fLotte@beispiel.de" [formControl]="email" />
<mat-error *ngIf="email.hasError('required')">
Bitte geben Sie eine E-Mail-Adresse ein.
</mat-error>
<mat-error *ngIf="email.hasError('email')">
Bitte geben Sie eine valide E-Mail-Adresse ein.
</mat-error>
</mat-form-field>-->
<mat-form-field (keyup.enter)="login()" (input)="onPasswordInput()">
<mat-label>Neues Passwort</mat-label>
<input
matInput
[type]="hide ? 'password' : 'text'"
[formControl]="passwordNew"
/>
<mat-error *ngIf="passwordNew.hasError('required')">
Bitte geben Sie Ihr neues Passwort ein.
</mat-error>
<mat-error *ngIf="passwordNew.hasError('minlength')">
Das Passwort muss mindestens aus {{minPw}} Zeichen bestehen
</mat-error>
<button
mat-icon-button
matSuffix
(click)="hide = !hide"
[attr.aria-label]="'Hide password'"
[attr.aria-pressed]="hide"
>
<mat-icon>{{ hide ? "visibility_off" : "visibility" }}</mat-icon>
</button>
</mat-form-field>
<mat-form-field (keyup.enter)="login()" (input)="onPasswordInput()">
<mat-label>Passwort wiederholen</mat-label>
<input
matInput
[type]="hide ? 'password' : 'text'"
[formControl]="passwordNew2"
/>
<mat-error *ngIf="passwordNew2.hasError('required')">
Bitte wiederholen sie ihr Passwort
</mat-error>
<mat-error *ngIf="passwordNew2.invalid && !passwordNew2.hasError('required')">
Das Passwort stimmt nicht überein
</mat-error>
<button
mat-icon-button
matSuffix
(click)="hide = !hide"
[attr.aria-label]="'Hide password'"
[attr.aria-pressed]="hide"
>
<mat-icon>{{ hide ? "visibility_off" : "visibility" }}</mat-icon>
</button>
</mat-form-field>
<mat-progress-bar mode="indeterminate" id="loading-bar" *ngIf="loading"></mat-progress-bar>
<button mat-stroked-button color="primary" (click)="login()">
Profil aktualisieren
</button>
<mat-error class="login-error-message" *ngIf="errorOccurred">
{{errorMessage}}
</mat-error>
</div>
<!--<app-table
[headline]="headline" [headline]="headline"
[columnInfo]="columnInfo" [columnInfo]="columnInfo"
[dataService]="dataService" [dataService]="dataService"
@ -10,4 +102,4 @@
(saveEvent)="save($event)" (saveEvent)="save($event)"
(cancelEvent)="cancel($event)" (cancelEvent)="cancel($event)"
(deleteEvent)="delete($event)" (deleteEvent)="delete($event)"
></app-table> ></app-table>-->

@ -0,0 +1,27 @@
#login-form {
display: flex;
flex-direction: column;
max-width: 32em;
min-width: 5em;
margin: auto;
margin-top: 3em;
padding: 1em;
.mat-form-field {
margin: 0.5em 0;
}
#loading-bar {
margin-bottom: 1em;
}
.login-error-message {
margin-top: 0.5em;
}
}
hr {
color: #7fc600;
width: 100%;
}
.currentPW {
margin-bottom: 3rem !important;
}

@ -1,4 +1,7 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormControl, FormGroup, ValidationErrors, ValidatorFn, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { AuthService } from 'src/app/services/auth.service';
import { BikesService } from '../../services/bikes.service'; import { BikesService } from '../../services/bikes.service';
@Component({ @Component({
@ -7,149 +10,63 @@ import { BikesService } from '../../services/bikes.service';
styleUrls: ['./profile.component.scss'], styleUrls: ['./profile.component.scss'],
}) })
export class ProfileComponent implements OnInit { export class ProfileComponent implements OnInit {
columnInfo = [
{
dataPath: 'name',
translation: 'Name',
sticky: true,
link: (row: any) => {
return '/bike/' + row.id;
},
},
{ dataPath: 'id', translation: 'ID', readonly: true },
{ dataPath: 'group', translation: 'Gruppe' },
{ dataPath: 'modelName', translation: 'Modell' },
{
dataPath: 'insuranceData.billing',
translation: 'Versicherung Abrechnung',
},
{ dataPath: 'insuranceData.hasFixedRate', translation: 'Pauschale j/n' },
{ dataPath: 'insuranceData.fixedRate', translation: 'Pauschale Betrag' },
{ dataPath: 'insuranceData.name', translation: 'Versicherer' },
{ dataPath: 'insuranceData.benefactor', translation: 'Kostenträger' },
{ dataPath: 'insuranceData.noPnP', translation: 'Nr. P&P' },
{
dataPath: 'insuranceData.maintenanceResponsible',
translation: 'Wartung zuständig',
},
{
dataPath: 'insuranceData.maintenanceBenefactor',
translation: 'Wartung Kostenträger',
},
{
dataPath: 'insuranceData.maintenanceAgreement',
translation: 'Wartungsvereinbarung',
},
{
dataPath: 'insuranceData.projectAllowance',
translation: 'Projektzuschuss',
},
{ dataPath: 'insuranceData.notes', translation: 'Sonstiges' },
{ dataPath: 'dimensionsAndLoad.bikeLength', translation: 'Länge' },
{ dataPath: 'dimensionsAndLoad.bikeWeight', translation: 'Gewicht' },
{ dataPath: 'dimensionsAndLoad.bikeHeight', translation: 'Höhe' },
{ dataPath: 'dimensionsAndLoad.bikeWidth', translation: 'Breite' },
{ dataPath: 'dimensionsAndLoad.boxHeightRange', translation: 'Boxhöhe' },
{ dataPath: 'dimensionsAndLoad.boxLengthRange', translation: 'Boxlänge' },
{ dataPath: 'dimensionsAndLoad.boxWidthRange', translation: 'Boxbreite' },
{
dataPath: 'dimensionsAndLoad.hasCoverBox',
translation: 'Boxabdeckung j/n',
},
{ dataPath: 'dimensionsAndLoad.lockable', translation: 'Box abschließbar' },
{
dataPath: 'dimensionsAndLoad.maxWeightBox',
translation: 'max Zuladung Box',
},
{
dataPath: 'dimensionsAndLoad.maxWeightLuggageRack',
translation: 'max Zuladung Gepäckträger',
},
{
dataPath: 'dimensionsAndLoad.maxWeightTotal',
translation: 'max Gesamtgewicht',
},
{ dataPath: 'numberOfChildren', translation: 'Anzahl Kinder' },
{ dataPath: 'numberOfWheels', translation: 'Anzahl Räder' },
{ dataPath: 'forCargo', translation: 'für Lasten j/n' },
{ dataPath: 'forChildren', translation: 'für Kinder j/n' },
{ dataPath: 'security.frameNumber', translation: 'Rahmennummer' },
{ dataPath: 'security.adfcCoding', translation: 'ADFC Codierung' },
{
dataPath: 'security.keyNumberAXAChain',
translation: 'Schlüsselnrummer Rahmenschloss',
},
{
dataPath: 'security.keyNumberFrameLock',
translation: 'Schlüsselnrummer AXA-Kette',
},
{ dataPath: 'security.policeCoding', translation: 'Polizei Codierung' },
{ dataPath: 'technicalEquipment.bicycleShift', translation: 'Schaltung' },
{ dataPath: 'technicalEquipment.isEBike', translation: 'E-Bike j/n' },
{
dataPath: 'technicalEquipment.hasLightSystem',
translation: 'Lichtanlage j/n',
},
{
dataPath: 'technicalEquipment.specialFeatures',
translation: 'Besonderheiten',
},
{ dataPath: 'stickerBikeNameState', translation: 'Aufkleber Status' },
{ dataPath: 'note', translation: 'Aufkleber Kommentar' },
{ dataPath: 'taxes.costCenter', translation: 'Steuern Kostenstelle' },
{
dataPath: 'taxes.organisationArea',
translation: 'Steuern Vereinsbereich',
},
{ dataPath: 'provider.id', translation: '' },
{ dataPath: 'provider.formName', translation: '' },
{ dataPath: 'provider.privatePerson.id', translation: '' },
{ dataPath: 'provider.privatePerson.person.id', translation: '' },
{ dataPath: 'provider.privatePerson.person.name', translation: '' },
{ dataPath: 'provider.privatePerson.person.firstName', translation: '' },
{
dataPath: 'provider.privatePerson.person.contactInformation.email',
translation: '',
},
{ dataPath: 'lendingStation.id', translation: '' },
{ dataPath: 'lendingStation.name', translation: '' },
{ dataPath: 'lendingStation.address.number', translation: '' },
{ dataPath: 'lendingStation.address.street', translation: '' },
{ dataPath: 'lendingStation.address.zip', translation: '' },
];
dataService: any; minPw: number = 8;
email = new FormControl('', [Validators.required, Validators.email]);
password = new FormControl('', [Validators.required]);
passwordNew = new FormControl('', [Validators.required,Validators.minLength(this.minPw)]);
passwordNew2 = new FormControl('', [Validators.required]);
pwGroup: FormGroup;
hide = true;
loading = false;
errorOccurred = false;
errorMessage = '';
tableDataGQLType: string = 'CargoBike'; returnUrl : string;
tableDataGQLCreateInputType: string = 'CargoBikeCreateInput';
tableDataGQLUpdateInputType: string = 'CargoBikeUpdateInput';
headline = 'Lastenräder'; constructor(private authService: AuthService, private router: Router, private route: ActivatedRoute, private formBuilder : FormBuilder) {}
loadingRowIds: string[] = []; ngOnInit(): void {
constructor(private bikesService: BikesService) {} this.pwGroup = this.formBuilder.group({
passwordNew: this.passwordNew,
ngOnInit() { passwordNew2: this.passwordNew2
this.dataService = this.bikesService; }, {validator: passwordMatchValidator});
}
create(object: { currentId: string; row: any }) {
this.bikesService.createBike(object.currentId, { bike: object.row });
}
lock(row: any) {
this.bikesService.lockBike({ id: row.id });
}
save(row: any) {
this.bikesService.updateBike({ bike: row });
} }
cancel(row: any) { onPasswordInput() {
this.bikesService.unlockBike({ id: row.id }); if (this.pwGroup.hasError('passwordMismatch'))
this.passwordNew2.setErrors([{'passwordMismatch': true}]);
else
this.passwordNew2.setErrors(null);
} }
delete(row: any) { login() {
this.bikesService.deleteBike({ id: row.id }); this.errorMessage = '';
this.errorOccurred = false;
if (this.email.invalid || this.password.invalid) {
return;
}
this.loading = true;
this.authService
.login(this.email.value, this.password.value)
.subscribe(
() => this.router.navigateByUrl(this.returnUrl),
(error) => {
this.errorOccurred = true;
this.errorMessage =
error.error.message ||
'Ein Fehler bei Einloggen ist aufgetreten. Überprüfen Sie Ihre Internetverbindung oder versuchen Sie es später erneut.';
}
)
.add(() => {
this.loading = false;
});
} }
} }
export const passwordMatchValidator: ValidatorFn = (formGroup: FormGroup): ValidationErrors | null => {
if (formGroup.get('passwordNew').value === formGroup.get('passwordNew2').value)
return null;
else
return {passwordMismatch: true};
};

Loading…
Cancel
Save