diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
new file mode 100644
index 0000000..881ac7f
--- /dev/null
+++ b/.github/workflows/docker.yml
@@ -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
diff --git a/Dockerfile b/Dockerfile
index a33ada4..a4bc160 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,6 +1,7 @@
FROM node:14.14.0-alpine3.10 AS builder
WORKDIR /
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 mv node_modules ./frontend
WORKDIR /frontend
@@ -12,7 +13,7 @@ FROM golang:1.13.4-alpine as builder2
ARG ARCH=amd64
RUN apk add git
WORKDIR /
-COPY --from=builder /frontend/dist /dist
+COPY --from=builder /frontend/dist /dist
RUN go get github.com/rakyll/statik
RUN statik --src=/dist/flotte-frontend
COPY *.go *.sum *.mod /
diff --git a/README.md b/README.md
index eadd3e0..e733c3e 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# 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.
@@ -59,4 +59,4 @@ We can't pass it in an other form, else it would not be possible to pass a value
If the backend url changes, it has to be changed in the following files:
- ./src/environments/
- ./apollo.config.js for autocompletion when writing queries etc.
-- ./codegen.yml for graphQL codegen (to generate schema, types etc.)
\ No newline at end of file
+- ./codegen.yml for graphQL codegen (to generate schema, types etc.)
diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index 93302a0..b4c7ed2 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -12,10 +12,12 @@ import { LendingStationsComponent } from './pages/tables/lending-stations/lendin
import { ParticipantsComponent } from './pages/tables/participants/participants.component';
import { TimeFramesComponent } from './pages/tables/time-frames/time-frames.component';
import {AuthGuard} from './helper/auth.guard';
+import { ProfileComponent } from './pages/profile/profile.component';
const routes: Routes = [
{ path: 'login', component: LoginComponent },
{ path: 'tableOverview', component: TableOverviewComponent, canActivate: [AuthGuard]},
+ { path: 'profile', component: ProfileComponent, canActivate: [AuthGuard]},
{ path: 'table/bikes', component: BikesComponent, canActivate: [AuthGuard] },
{ path: 'bike/:id', component: BikeComponent, canActivate: [AuthGuard] },
{ path: 'table/participants', component: ParticipantsComponent, canActivate: [AuthGuard] },
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 6ab213a..9505330 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -65,6 +65,7 @@ import { DateRangeCellComponent } from './components/tableComponents/date-range-
import { SelectObjectDialogComponent } from './components/select-object-dialog/select-object-dialog.component';
import { AutocompleteSelectComponent } from './components/autocomplete-select/autocomplete-select.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,
AutocompleteSelectComponent,
LendingStationComponent,
+ ProfileComponent,
],
imports: [
BrowserModule,
diff --git a/src/app/components/sidenav-profile/sidenav-profile.component.html b/src/app/components/sidenav-profile/sidenav-profile.component.html
index 832786d..275b3cd 100644
--- a/src/app/components/sidenav-profile/sidenav-profile.component.html
+++ b/src/app/components/sidenav-profile/sidenav-profile.component.html
@@ -1,4 +1,4 @@
-
+
+
+
+
+
diff --git a/src/app/pages/profile/profile.component.scss b/src/app/pages/profile/profile.component.scss
index e69de29..6d6c43e 100644
--- a/src/app/pages/profile/profile.component.scss
+++ b/src/app/pages/profile/profile.component.scss
@@ -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;
+}
\ No newline at end of file
diff --git a/src/app/pages/profile/profile.component.ts b/src/app/pages/profile/profile.component.ts
index b970ac6..26f0dc0 100644
--- a/src/app/pages/profile/profile.component.ts
+++ b/src/app/pages/profile/profile.component.ts
@@ -1,4 +1,7 @@
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';
@Component({
@@ -7,149 +10,63 @@ import { BikesService } from '../../services/bikes.service';
styleUrls: ['./profile.component.scss'],
})
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';
- tableDataGQLCreateInputType: string = 'CargoBikeCreateInput';
- tableDataGQLUpdateInputType: string = 'CargoBikeUpdateInput';
+ returnUrl : string;
- headline = 'Lastenräder';
+ constructor(private authService: AuthService, private router: Router, private route: ActivatedRoute, private formBuilder : FormBuilder) {}
- loadingRowIds: string[] = [];
- constructor(private bikesService: BikesService) {}
-
- ngOnInit() {
- this.dataService = this.bikesService;
- }
-
- 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 });
+ ngOnInit(): void {
+ this.pwGroup = this.formBuilder.group({
+ passwordNew: this.passwordNew,
+ passwordNew2: this.passwordNew2
+ }, {validator: passwordMatchValidator});
}
- cancel(row: any) {
- this.bikesService.unlockBike({ id: row.id });
+ onPasswordInput() {
+ if (this.pwGroup.hasError('passwordMismatch'))
+ this.passwordNew2.setErrors([{'passwordMismatch': true}]);
+ else
+ this.passwordNew2.setErrors(null);
}
- delete(row: any) {
- this.bikesService.deleteBike({ id: row.id });
+ login() {
+ 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};
+};