diff --git a/frontend/angular.json b/frontend/angular.json index db6a97e..589f92c 100644 --- a/frontend/angular.json +++ b/frontend/angular.json @@ -26,8 +26,14 @@ "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.app.json", "inlineStyleLanguage": "scss", - "assets": ["src/favicon.ico", "src/assets"], - "styles": ["src/styles.scss"], + "assets": [ + "src/favicon.ico", + "src/assets" + ], + "styles": [ + "./node_modules/@angular/material/prebuilt-themes/pink-bluegrey.css", + "src/styles.scss" + ], "scripts": [] }, "configurations": { @@ -84,7 +90,20 @@ "lint": { "builder": "@angular-eslint/builder:lint", "options": { - "lintFilePatterns": ["src/**/*.ts", "src/**/*.html"] + "main": "src/test.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "tsconfig.spec.json", + "karmaConfig": "karma.conf.js", + "inlineStyleLanguage": "scss", + "assets": [ + "src/favicon.ico", + "src/assets" + ], + "styles": [ + "./node_modules/@angular/material/prebuilt-themes/pink-bluegrey.css", + "src/styles.scss" + ], + "scripts": [] } } } diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index ba8913f..b43c49b 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -3,11 +3,13 @@ import { RouterModule, Routes } from '@angular/router'; import { StudentListComponent } from './components/students/student-list/student-list.component'; import { VisitsComponent } from './components/visits/visits.component'; import { VisitsDatetimeComponent } from './components/visits/visits-datetime/visits-datetime.component'; +import { StudentRegisterComponent } from './components/students/student-register/student-register.component'; const routes: Routes = [ { path: 'students', component: StudentListComponent }, { path: 'visits', component: VisitsComponent }, { path: 'select', component: VisitsDatetimeComponent }, + { path: 'register', component: StudentRegisterComponent }, { path: 'visits/:date/:time', component: VisitsComponent }, { path: '**', redirectTo: 'students' }, ]; diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 59461c3..66ecb37 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { FormsModule } from '@angular/forms'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { StudentListComponent } from './components/students/student-list/student-list.component'; @@ -20,6 +20,15 @@ import { VisitsDatetimeComponent } from './components/visits/visits-datetime/vis import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatNativeDateModule, MatRippleModule } from '@angular/material/core'; +import { StudentRegisterComponent } from './components/students/student-register/student-register.component'; +import { MatStepperModule } from '@angular/material/stepper'; +import { MatInputModule } from '@angular/material/input'; +import { MatButtonModule } from '@angular/material/button'; +import { MatRadioModule } from '@angular/material/radio'; +import { MatSelectModule } from '@angular/material/select'; +import { MatIconModule } from '@angular/material/icon'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatTooltipModule } from '@angular/material/tooltip'; @NgModule({ declarations: [ @@ -33,6 +42,7 @@ import { MatNativeDateModule, MatRippleModule } from '@angular/material/core'; VisitsComponent, StudentEnrollComponent, VisitsDatetimeComponent, + StudentRegisterComponent, ], imports: [ BrowserModule, @@ -47,6 +57,15 @@ import { MatNativeDateModule, MatRippleModule } from '@angular/material/core'; MatFormFieldModule, MatRippleModule, MatNativeDateModule, + MatStepperModule, + ReactiveFormsModule, + MatInputModule, + MatButtonModule, + MatRadioModule, + MatSelectModule, + MatCheckboxModule, + MatTooltipModule, + MatIconModule, ], providers: [], bootstrap: [AppComponent], diff --git a/frontend/src/app/components/students/student-register/student-register.component.html b/frontend/src/app/components/students/student-register/student-register.component.html new file mode 100644 index 0000000..6b75bd7 --- /dev/null +++ b/frontend/src/app/components/students/student-register/student-register.component.html @@ -0,0 +1,212 @@ + + + + + + + +
+ Registration +
+ Für den Tanzunterricht (Tanz-Flatrate) +
+
+ Für den Karateunterricht +
+
+ Für freies Training (unterschriebenes Beiblatt muss der Anmeldung + beiliegen) +
+
+
+ +
+
+ + + +
+ Personal Information +
+ + Name + + + + + Surname + + + + + Birthdate + + MM/DD/YYYY + + + + + + Gender + + Male + Female + Divers + + +
+
+ + Address + + + + + Postal Code + + + + + Phone + + + + + E-Mail + + Invalid E-Mail Address + +
+
+
+ + +
+
+ + + +
+ Banking Information + + Account Holder + + + + + IBAN + + + + + BIC + + + + + Name of Financial Institute + + + + + Amount in €/Month + + + +
+ Einzugsermächtigung + info +
+
+ +
+ + +
+
+ + + +
+ Consent +
+ Einwilligung zur Erhebung, Speicherung, Verarbeitung und Nutzung von + personenbezogenen Daten + + info +
+
+ Einverständniserklärung zur Nutzung von Bild- und Videomaterial + + info +
+
+ +
+ + +
+
+
diff --git a/frontend/src/app/components/students/student-register/student-register.component.scss b/frontend/src/app/components/students/student-register/student-register.component.scss new file mode 100644 index 0000000..4d84e8a --- /dev/null +++ b/frontend/src/app/components/students/student-register/student-register.component.scss @@ -0,0 +1,65 @@ +.mat-stepper-horizontal { + margin-top: 8px; +} + +.mat-form-field { + margin-top: 16px; +} + +.mat-stepper-horizontal { + background-color: transparent; +} + +input, +button { + color: white; +} + +//button { +// background-color: #0366d6; +//} + +::selection { + color: white; + background-color: #1576d5; +} +.flexContainerRow { + display: flex; + flex-direction: row; + justify-content: space-between; + &.width50 { + width: 50%; + } + &.width20 { + width: 20%; + } +} + +.flexContainerColumn { + display: flex; + flex-direction: column; + justify-content: space-between; + &.width30 { + width: 30%; + } +} + +.leftContainer { + display: flex; + flex-direction: column; + width: 45%; +} +.rightContainer { + display: flex; + flex-direction: column; + width: 45%; +} + +.backButton { + margin-right: 10px; +} + +mat-icon { + color: white; + scale: 80%; +} diff --git a/frontend/src/app/components/students/student-register/student-register.component.spec.ts b/frontend/src/app/components/students/student-register/student-register.component.spec.ts new file mode 100644 index 0000000..11107a6 --- /dev/null +++ b/frontend/src/app/components/students/student-register/student-register.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { StudentRegisterComponent } from './student-register.component'; + +describe('StudentRegisterComponent', () => { + let component: StudentRegisterComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [StudentRegisterComponent], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(StudentRegisterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/components/students/student-register/student-register.component.ts b/frontend/src/app/components/students/student-register/student-register.component.ts new file mode 100644 index 0000000..cb23368 --- /dev/null +++ b/frontend/src/app/components/students/student-register/student-register.component.ts @@ -0,0 +1,136 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; + +@Component({ + selector: 'li-student-register', + templateUrl: './student-register.component.html', + styleUrls: ['./student-register.component.scss'], +}) +export class StudentRegisterComponent implements OnInit { + masterFormGroup!: FormGroup; + zeroFormGroup!: FormGroup; + firstFormGroup!: FormGroup; + secondFormGroup!: FormGroup; + thirdFormGroup!: FormGroup; + isEditable = false; + infoText = + 'Hiermit ermächtige ich die Tanz- und Sportschule Li-Dance, Inh. Lydia Kolepp (nachfolgend Li-\n' + + 'Dance) die Monatsbeiträge i.H.v. oben genannten Betrag, diverse Einmalzahlungen und sonstige\n' + + 'Verbindlichkeiten zu Lasten meines Kontos einzuziehen.\n' + + 'Bitte beachten Sie, dass im Falle einer unberechtigten Rücklastschrift Li-Dance ein\n' + + 'Verwaltungsaufwand entsteht und eine Gebühr i.H.v. derzeit 15,- € berechnet wird. Diese Gebühr\n' + + 'wird zusammen mit dem nachfolgenden Monatsbeitrag vom gleichen Konto abgebucht.'; + + infoTextEinwilligungserklaerung = + 'Ich bin darüber informiert worden, dass meine personenbezogenen Daten aufgrund rechtlicher\n' + + 'Vorgaben mindestens 10 Jahre nach Vertragsende aufbewahrt werden müssen.\n' + + 'Ich bin damit einverstanden, dass die oben genannten personenbezogenen Daten zu den oben\n' + + 'genannten Zwecken erhoben, gespeichert, verarbeitet, genutzt und ggfs. weitergegeben werden.\n' + + 'Ich bin darauf hingewiesen worden, dass die im Rahmen der vorstehend genannten Zwecke\n' + + 'erhobenen persönlichen Daten meiner Person unter Beachtung der EU-\n' + + 'Datenschutzgrundverordnung (DSGVO) erhoben, gespeichert, genutzt und übermittelt werden.\n' + + 'Ich bin zudem darauf hingewiesen worden, dass die Erhebung, Speicherung, Verarbeitung und\n' + + 'Nutzung meiner Daten auf freiwilliger Basis erfolgt.\n' + + 'Ich bin darüber informiert worden, dass diese Einverständniserklärung jederzeit mit sofortiger\n' + + 'Wirkung verweigert, bzw. jederzeit mit Wirkung für die Zukunft widerrufen werden kann. Meine\n' + + 'Widerrufserklärung werde ich richten an die unten angegebene Adresse'; + + infoTextEinverstaendniserklaerung = + 'Ich bin damit einverstanden, dass Bilder und Videos der Tanzschule und der Veranstaltungen,\n' + + 'die durch die Tanzschule organisiert sind und/oder an denen die Tanzschule teilnimmt, auf\n' + + 'denen ich selbst, mein Sohn oder meine Tochter zu sehen ist auf den Webseiten von Li-Dance und in anderen Online- und/oder Printmedien veröffentlicht\n' + + 'werden und für Werbezwecke von Li-Dance genutzt werden dürfen. Rechtsgrundlage: Das Recht am eigenen Bild ist ein Teil des vom Gesetz geschützten\n' + + 'allgemeinen Persönlichkeitsrechts (§22 Kunsturheberrechtsgesetz). Es gilt der Grundsatz, dass\n' + + 'Bild- und Video-Material nur mit Einwilligung des Abgebildeten verbreitet oder veröffentlicht\n' + + 'werden kann. Es handelt sich hierbei um eine rechtsgeschäftliche Willenserklärung. Deshalb\n' + + 'kann bei Minderjährigen eine Einwilligung nur durch den gesetzlichen Vertreter erfolgen.\n' + + 'Diese Einverständniserklärung kann mit sofortiger Wirkung verweigert, bzw. jederzeit mit\n' + + 'Wirkung für die Zukunft widerrufen werden. Ein Widerruf kann einen Ausschluss aus der\n' + + 'jeweiligen Veranstaltung zur Folge haben.\n' + + 'Ein späterer rückwirkender Widerruf für aktuell stattfindende bzw. bereits stattgefundene\n' + + 'Veranstaltungen ist ausgeschlossen.'; + + constructor(private _formBuilder: FormBuilder) {} + + ngOnInit() { + //TODO Test to group all forms into one master form + // this.masterFormGroup = this._formBuilder.group({ + // zeroFormGroup: this._formBuilder.group({ + // danceTraining: [''], + // karateTraining: [''], + // freeTraining: [''], + // }, { + // validators: [this.requireAtLeastOne] + // }), + // firstFormGroup: this._formBuilder.group({ + // firstName: ['', Validators.required], + // lastName: ['', Validators.required], + // birthdate: ['', Validators.required], + // postalCode: ['', [Validators.required, Validators.pattern(/^\d+$/)]], + // address: ['', Validators.required], + // phone: ['', [Validators.required, Validators.pattern(/^\d+$/)]], + // email: ['', [Validators.required, Validators.email]], + // }), + // secondFormGroup: this._formBuilder.group({ + // accountHolder: ['', Validators.required], + // iban: ['', [Validators.required, Validators.pattern(/^\d+$/)]], + // bic: ['', Validators.required], + // nameOfFinancialInstitute: ['', Validators.required], + // amount: ['', [Validators.required, Validators.pattern(/^\d+$/)]], + // einzugsermaechtigung: ['', Validators.required], + // }), + // thirdFormGroup: this._formBuilder.group({ + // einwilligung: ['', Validators.required], + // einverstaendniserklaerung: ['', Validators.required], + // }), + // }); + // } + + this.zeroFormGroup = this._formBuilder.group( + { + danceTraining: [''], + karateTraining: [''], + freeTraining: [''], + }, + { + validators: [this.requireAtLeastOne], + } + ); + this.firstFormGroup = this._formBuilder.group({ + firstName: ['', Validators.required], + lastName: ['', Validators.required], + birthdate: ['', Validators.required], + postalCode: ['', [Validators.required, Validators.pattern(/^\d+$/)]], //^: Asserts the start of the string. \d+: Matches one or more digits.$: Asserts the end of the string. + address: ['', Validators.required], + phone: ['', [Validators.required, Validators.pattern(/^\d+$/)]], + email: ['', [Validators.required, Validators.email]], + }); + + this.secondFormGroup = this._formBuilder.group({ + accountHolder: ['', Validators.required], + iban: ['', [Validators.required, Validators.pattern(/^\d+$/)]], + bic: ['', Validators.required], + nameOfFinancialInstitute: ['', Validators.required], + amount: ['', [Validators.required, Validators.pattern(/^\d+$/)]], + einzugsermaechtigung: ['', Validators.required], + }); + + this.thirdFormGroup = this._formBuilder.group({ + einwilligung: ['', Validators.required], + einverstaendniserklaerung: ['', Validators.required], + }); + } + + // Custom validator function + requireAtLeastOne(formGroup: FormGroup) { + const danceTraining = formGroup.get('danceTraining')?.value; + const karateTraining = formGroup.get('karateTraining')?.value; + const freeTraining = formGroup.get('freeTraining')?.value; + + if (!danceTraining && !karateTraining && !freeTraining) { + return { requiredAtLeastOne: true }; + } + + return null; + } +} diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss index 7082521..1a2401a 100644 --- a/frontend/src/styles.scss +++ b/frontend/src/styles.scss @@ -1,18 +1,49 @@ /* Provide sufficient contrast against white background */ +@use "@angular/material" as mat; @import '~bootstrap-icons/font/bootstrap-icons.css'; -a { - color: #0366d6; -} +@include mat.core(); -code { - color: #e01a76; -} +$my-primary: mat.define-palette(mat.$blue-palette); +$my-accent: mat.define-palette(mat.$gray-palette); -.btn-primary { - color: #fff; - background-color: #1b6ec2; - border-color: #1861ac; + +$my-theme: mat.define-light-theme(( + color: ( + primary: $my-primary, + accent: $my-accent, + ), + typography: mat.define-typography-config(), + density: 0, +)); + +// Emit theme-dependent styles for common features used across multiple components. +@include mat.core-theme($my-theme); + +// Emit styles for MatButton based on `$my-theme`. Because the configuration +// passed to `define-light-theme` omits typography, `button-theme` will not +// emit any typography styles. +@include mat.all-component-colors($my-theme); +@include mat.button-color($my-theme); +// Include the theme mixins for other components you use here. +//a { +// color: #0366d6; +//} +// +//code { +// color: #e01a76; +//} +// +//.btn-primary { +// color: #fff; +// background-color: #1b6ec2; +// border-color: #1861ac; +//} + +body { + margin: 0; + font-family: Roboto, 'Helvetica Neue', sans-serif; + background-color: rgb(245, 126, 32); } html, @@ -20,48 +51,75 @@ body { height: 100%; } -body { - background-color: rgb(245, 126, 32); - margin: 0; - font-family: Roboto, 'Helvetica Neue', sans-serif; -} -.mat-calendar-body-cell-content { - background-color: #1861ac; - color: #fff; - font-weight: bold; - font-size: 2em; -} +//.mat-form-field-subscript-wrapper { +// font-size: 95%; +//} +// +//.mat-checkbox-checked.mat-accent .mat-checkbox-background { +// background-color: #0366d6 !important; +//} +//mat-checkbox-checked { +// border-color: white; +//} +//mat-checkbox { +// color: white; +// margin-bottom: 16px; +//} +//.mat-step-header .mat-step-icon-selected, +//.mat-step-icon-state-edit { +// background-color: #0366d6 !important; +//} -.mat-calendar-table { - border: 1px solid white; -} +// customize datepicker +//.mat-datepicker-content-container { +// background:transparent; +//} +//.mat-datepicker-content { +// color: black; +//} +//.mat-calendar-body-cell-content, .mat-calendar-body-label{ +// color: black; +// } -.mat-calendar-table-header { - tr { - th { - padding-top: 2em; - } - - th.mat-calendar-table-header-divider { - padding: 0; - } - } -} - -.mat-calendar-controls, -.mat-calendar-table-header, -.mat-calendar-body-label { - color: #fff; - font-size: 2em; -} - -.mat-calendar-previous-button, -.mat-calendar-next-button, -.mat-calendar-period-button { - font-size: 2em; - height: 2em; - color: #fff; - background-color: #1b6ec2; - border-color: #1861ac; -} +// +//.mat-calendar-body-cell-content { +// background-color: #1861ac; +// color: #fff; +// font-weight: bold; +// font-size: 2em; +//} +// +//.mat-calendar-table { +// border: 1px solid white; +//} +// +//.mat-calendar-table-header { +// +// tr { +// th { +// padding-top: 2em; +// } +// +// th.mat-calendar-table-header-divider { +// padding: 0; +// } +// } +//} +// +//.mat-calendar-controls, +//.mat-calendar-table-header, +//.mat-calendar-body-label { +// color: #fff; +// font-size: 2em; +//} +// +//.mat-calendar-previous-button, +//.mat-calendar-next-button, +//.mat-calendar-period-button { +// font-size: 2em; +// height: 2em; +// color: #fff; +// background-color: #1b6ec2; +// border-color: #1861ac; +//}