Dependency Day + Basic Setup #3

Closed
igorpropisnov wants to merge 7 commits from feature/dependency-day into main
8 changed files with 559 additions and 36 deletions

View File

@ -26,8 +26,14 @@
"polyfills": "src/polyfills.ts", "polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json", "tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss", "inlineStyleLanguage": "scss",
"assets": ["src/favicon.ico", "src/assets"], "assets": [
"styles": ["src/styles.scss"], "src/favicon.ico",
"src/assets"
],
"styles": [
"./node_modules/@angular/material/prebuilt-themes/pink-bluegrey.css",
"src/styles.scss"
],
"scripts": [] "scripts": []
}, },
"configurations": { "configurations": {
@ -84,7 +90,20 @@
"lint": { "lint": {
"builder": "@angular-eslint/builder:lint", "builder": "@angular-eslint/builder:lint",
"options": { "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": []
} }
} }
} }

View File

@ -3,11 +3,13 @@ import { RouterModule, Routes } from '@angular/router';
import { StudentListComponent } from './components/students/student-list/student-list.component'; import { StudentListComponent } from './components/students/student-list/student-list.component';
import { VisitsComponent } from './components/visits/visits.component'; import { VisitsComponent } from './components/visits/visits.component';
import { VisitsDatetimeComponent } from './components/visits/visits-datetime/visits-datetime.component'; import { VisitsDatetimeComponent } from './components/visits/visits-datetime/visits-datetime.component';
import { StudentRegisterComponent } from './components/students/student-register/student-register.component';
const routes: Routes = [ const routes: Routes = [
{ path: 'students', component: StudentListComponent }, { path: 'students', component: StudentListComponent },
{ path: 'visits', component: VisitsComponent }, { path: 'visits', component: VisitsComponent },
{ path: 'select', component: VisitsDatetimeComponent }, { path: 'select', component: VisitsDatetimeComponent },
{ path: 'register', component: StudentRegisterComponent },
{ path: 'visits/:date/:time', component: VisitsComponent }, { path: 'visits/:date/:time', component: VisitsComponent },
{ path: '**', redirectTo: 'students' }, { path: '**', redirectTo: 'students' },
]; ];

View File

@ -1,7 +1,7 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 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 { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { StudentListComponent } from './components/students/student-list/student-list.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 { MatDatepickerModule } from '@angular/material/datepicker';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { MatNativeDateModule, MatRippleModule } from '@angular/material/core'; 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({ @NgModule({
declarations: [ declarations: [
@ -33,6 +42,7 @@ import { MatNativeDateModule, MatRippleModule } from '@angular/material/core';
VisitsComponent, VisitsComponent,
StudentEnrollComponent, StudentEnrollComponent,
VisitsDatetimeComponent, VisitsDatetimeComponent,
StudentRegisterComponent,
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
@ -47,6 +57,15 @@ import { MatNativeDateModule, MatRippleModule } from '@angular/material/core';
MatFormFieldModule, MatFormFieldModule,
MatRippleModule, MatRippleModule,
MatNativeDateModule, MatNativeDateModule,
MatStepperModule,
ReactiveFormsModule,
MatInputModule,
MatButtonModule,
MatRadioModule,
MatSelectModule,
MatCheckboxModule,
MatTooltipModule,
MatIconModule,
], ],
providers: [], providers: [],
bootstrap: [AppComponent], bootstrap: [AppComponent],

View File

@ -0,0 +1,213 @@
<!--<button mat-raised-button (click)="isEditable = !isEditable">-->
<!-- {{!isEditable ? 'Enable edit mode' : 'Disable edit mode'}}-->
<!--</button>-->
<mat-stepper linear #stepper>
<!-- Step 0-->
<mat-step [stepControl]="zeroFormGroup" class="firstStep">
<form [formGroup]="zeroFormGroup">
<ng-template matStepLabel>Registration</ng-template>
<div class="flexContainerRow width20">
<mat-checkbox formControlName="danceTraining"
>Für den Tanzunterricht (Tanz-Flatrate)</mat-checkbox
>
</div>
<div class="flexContainerRow width20">
<mat-checkbox formControlName="karateTraining"
>Für den Karateunterricht</mat-checkbox
>
</div>
<div class="flexContainerRow width20">
<mat-checkbox formControlName="freeTraining"
>Für freies Training (unterschriebenes Beiblatt muss der Anmeldung
beiliegen)</mat-checkbox
>
</div>
</form>
<div>
<button
mat-button
matStepperNext
[disabled]="zeroFormGroup.status == 'INVALID'"
(click)="onClick()">
Next
</button>
</div>
</mat-step>
<!-- Step 1-->
<mat-step [stepControl]="firstFormGroup" class="firstStep">
<form [formGroup]="firstFormGroup" class="flexContainerRow width50">
<ng-template matStepLabel>Personal Information</ng-template>
<div class="leftContainer">
<mat-form-field appearance="fill">
<mat-label>Name</mat-label>
<input
matInput
formControlName="firstName"
placeholder="First name"
required />
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Surname</mat-label>
<input
matInput
formControlName="lastName"
placeholder="Last name"
required />
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Birthdate</mat-label>
<input
matInput
[matDatepicker]="birthdatePicker"
formControlName="birthdate"
required />
<mat-hint>MM/DD/YYYY</mat-hint>
<mat-datepicker-toggle
matSuffix
[for]="birthdatePicker"></mat-datepicker-toggle>
<mat-datepicker
#birthdatePicker
startView="multi-year"></mat-datepicker>
</mat-form-field>
<mat-form-field>
<mat-label>Gender</mat-label>
<mat-select>
<mat-option value="male">Male</mat-option>
<mat-option value="female">Female</mat-option>
<mat-option value="divers">Divers</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="rightContainer">
<mat-form-field appearance="fill">
<mat-label>Address</mat-label>
<input matInput formControlName="address" required />
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Postal Code</mat-label>
<input matInput formControlName="postalCode" required />
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Phone</mat-label>
<input matInput formControlName="phone" required />
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>E-Mail</mat-label>
<input matInput formControlName="email" required />
<mat-error>Invalid E-Mail Address</mat-error>
</mat-form-field>
</div>
</form>
<div class="buttonsContainer">
<button mat-button matStepperPrevious class="backButton">Back</button>
<button
mat-button
matStepperNext
[disabled]="firstFormGroup.status == 'INVALID'">
Next
</button>
</div>
</mat-step>
<!-- Step 2-->
<mat-step [stepControl]="secondFormGroup" [editable]="isEditable">
<form [formGroup]="secondFormGroup" class="flexContainerColumn width30">
<ng-template matStepLabel>Banking Information</ng-template>
<mat-form-field appearance="fill">
<mat-label>Account Holder</mat-label>
<input matInput formControlName="accountHolder" required />
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>IBAN</mat-label>
<input matInput formControlName="iban" required />
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>BIC</mat-label>
<input matInput formControlName="bic" required />
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Name of Financial Institute</mat-label>
<input matInput formControlName="nameOfFinancialInstitute" required />
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Amount in €/Month</mat-label>
<input matInput formControlName="amount" required />
</mat-form-field>
<div class="flexContainerRow width20">
<mat-checkbox formControlName="einzugsermaechtigung" required
>Einzugsermächtigung</mat-checkbox
>
<mat-icon
aria-hidden="false"
matTooltip="{{ infoText }}"
matTooltipPosition="right"
>info</mat-icon
>
</div>
</form>
<div class="buttonsContainer">
<button mat-button matStepperPrevious class="backButton">Back</button>
<button
mat-button
matStepperNext
[disabled]="secondFormGroup.status == 'INVALID'">
Next
</button>
</div>
</mat-step>
<!-- Step 3-->
<mat-step>
<form [formGroup]="thirdFormGroup" class="flexContainerColumn width30">
<ng-template matStepLabel>Consent</ng-template>
<div class="flexContainerRow width20">
<mat-checkbox formControlName="einwilligung" required
>Einwilligung zur Erhebung, Speicherung, Verarbeitung und Nutzung von
personenbezogenen Daten
</mat-checkbox>
<mat-icon
aria-hidden="false"
matTooltip="{{ infoTextEinwilligungserklaerung }}"
matTooltipPosition="right"
>info</mat-icon
>
</div>
<div class="flexContainerRow width20">
<mat-checkbox formControlName="einverstaendniserklaerung" required
>Einverständniserklärung zur Nutzung von Bild- und Videomaterial
</mat-checkbox>
<mat-icon
aria-hidden="false"
matTooltip="{{ infoTextEinverstaendniserklaerung }}"
matTooltipPosition="right"
>info</mat-icon
>
</div>
</form>
<div class="buttonsContainer">
<button mat-button matStepperPrevious class="backButton">Back</button>
<button
mat-button
matStepperNext
[disabled]="thirdFormGroup.status == 'INVALID'"
(click)="onClick()">
Submit
</button>
</div>
</mat-step>
</mat-stepper>

View File

@ -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%;
}

View File

@ -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<StudentRegisterComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [StudentRegisterComponent],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(StudentRegisterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,140 @@
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],
});
}
onClick() {
console.log(this.zeroFormGroup);
}
// 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;
}
}

View File

@ -1,6 +1,7 @@
/* Provide sufficient contrast against white background */ /* Provide sufficient contrast against white background */
@import '~bootstrap-icons/font/bootstrap-icons.css'; @import '~bootstrap-icons/font/bootstrap-icons.css';
a { a {
color: #0366d6; color: #0366d6;
} }
@ -26,42 +27,82 @@ body {
font-family: Roboto, 'Helvetica Neue', sans-serif; font-family: Roboto, 'Helvetica Neue', sans-serif;
} }
.mat-calendar-body-cell-content { .mat-form-field-subscript-wrapper {
background-color: #1861ac; font-size: 95%;
color: #fff;
font-weight: bold;
font-size: 2em;
} }
.mat-calendar-table { .mat-checkbox-checked.mat-accent .mat-checkbox-background {
border: 1px solid white; 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-header { // customize datepicker
tr { //.mat-datepicker-content-container {
th { // background:transparent;
padding-top: 2em; //}
} //.mat-datepicker-content {
// color: black;
//}
//.mat-calendar-body-cell-content, .mat-calendar-body-label{
// color: black;
// }
th.mat-calendar-table-header-divider { //
padding: 0; //.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;
//}
html,
body {
height: 100%;
} }
body {
.mat-calendar-controls, margin: 0;
.mat-calendar-table-header, font-family: Roboto, 'Helvetica Neue', sans-serif;
.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;
} }