Initial commit
This commit is contained in:
parent
dfc1b8411c
commit
66ef196617
|
@ -0,0 +1,15 @@
|
|||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
|
||||
# For the full list of supported browsers by the Angular framework, please see:
|
||||
# https://angular.io/guide/browser-support
|
||||
|
||||
# You can see what browsers were selected by your queries by running:
|
||||
# npx browserslist
|
||||
|
||||
last 1 Chrome version
|
||||
last 1 Firefox version
|
||||
last 2 Edge major versions
|
||||
last 2 iOS major versions
|
||||
Firefox ESR
|
|
@ -0,0 +1,16 @@
|
|||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.ts]
|
||||
quote_type = single
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
|
@ -0,0 +1,43 @@
|
|||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# Compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
/bazel-out
|
||||
|
||||
# Node
|
||||
/node_modules
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# IDEs and editors
|
||||
.idea/
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
.android/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# Miscellaneous
|
||||
/.angular/cache
|
||||
.sass-cache/
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
Thumbs.db
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
|
||||
"recommendations": ["angular.ng-template"]
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "ng serve",
|
||||
"type": "pwa-chrome",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "npm: start",
|
||||
"url": "http://localhost:4200/"
|
||||
},
|
||||
{
|
||||
"name": "ng test",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "npm: test",
|
||||
"url": "http://localhost:9876/debug.html"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "start",
|
||||
"isBackground": true,
|
||||
"problemMatcher": {
|
||||
"owner": "typescript",
|
||||
"pattern": "$tsc",
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": {
|
||||
"regexp": "(.*?)"
|
||||
},
|
||||
"endsPattern": {
|
||||
"regexp": "bundle generation complete"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "test",
|
||||
"isBackground": true,
|
||||
"problemMatcher": {
|
||||
"owner": "typescript",
|
||||
"pattern": "$tsc",
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": {
|
||||
"regexp": "(.*?)"
|
||||
},
|
||||
"endsPattern": {
|
||||
"regexp": "bundle generation complete"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
27
README.md
27
README.md
|
@ -1,2 +1,27 @@
|
|||
# li-dance-backoffice
|
||||
# LiDanceBackoffice
|
||||
|
||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 13.1.3.
|
||||
|
||||
## Development server
|
||||
|
||||
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Running end-to-end tests
|
||||
|
||||
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"li-dance-backoffice": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"style": "scss"
|
||||
},
|
||||
"@schematics/angular:application": {
|
||||
"strict": true
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "li",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "dist/li-dance-backoffice",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"inlineStyleLanguage": "scss",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": []
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500kb",
|
||||
"maximumError": "1mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "2kb",
|
||||
"maximumError": "4kb"
|
||||
}
|
||||
],
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"development": {
|
||||
"buildOptimizer": false,
|
||||
"optimization": false,
|
||||
"vendorChunk": true,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true,
|
||||
"namedChunks": true
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "li-dance-backoffice:build:production"
|
||||
},
|
||||
"development": {
|
||||
"browserTarget": "li-dance-backoffice:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "li-dance-backoffice:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"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": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultProject": "li-dance-backoffice"
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage'),
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
],
|
||||
client: {
|
||||
jasmine: {
|
||||
// you can add configuration options for Jasmine here
|
||||
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
|
||||
// for example, you can disable the random execution with `random: false`
|
||||
// or set a specific seed with `seed: 4321`
|
||||
},
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
jasmineHtmlReporter: {
|
||||
suppressAll: true // removes the duplicated traces
|
||||
},
|
||||
coverageReporter: {
|
||||
dir: require('path').join(__dirname, './coverage/li-dance-backoffice'),
|
||||
subdir: '.',
|
||||
reporters: [
|
||||
{ type: 'html' },
|
||||
{ type: 'text-summary' }
|
||||
]
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true
|
||||
});
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"name": "li-dance-backoffice",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"watch": "ng build --watch --configuration development",
|
||||
"test": "ng test"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "~13.1.0",
|
||||
"@angular/common": "~13.1.0",
|
||||
"@angular/compiler": "~13.1.0",
|
||||
"@angular/core": "~13.1.0",
|
||||
"@angular/forms": "~13.1.0",
|
||||
"@angular/material": "^13.1.0",
|
||||
"@angular/platform-browser": "~13.1.0",
|
||||
"@angular/platform-browser-dynamic": "~13.1.0",
|
||||
"@angular/router": "~13.1.0",
|
||||
"bootstrap": "^5.2.2",
|
||||
"bootstrap-icons": "^1.9.1",
|
||||
"rxjs": "~7.4.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.11.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~13.1.3",
|
||||
"@angular/cli": "~13.1.3",
|
||||
"@angular/compiler-cli": "~13.1.0",
|
||||
"@types/jasmine": "~3.10.0",
|
||||
"@types/node": "^12.11.1",
|
||||
"jasmine-core": "~3.10.0",
|
||||
"karma": "~6.3.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage": "~2.1.0",
|
||||
"karma-jasmine": "~4.0.0",
|
||||
"karma-jasmine-html-reporter": "~1.7.0",
|
||||
"typescript": "~4.5.2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { StudentListComponent } from './components/students/student-list/student-list.component';
|
||||
import { VisitsComponent } from './components/visits/visits.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: 'students', component: StudentListComponent },
|
||||
{ path: 'visits', component: VisitsComponent },
|
||||
{ path: '**', redirectTo: 'students' }
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule { }
|
|
@ -0,0 +1,5 @@
|
|||
<body>
|
||||
<div class="container">
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</body>
|
|
@ -0,0 +1,35 @@
|
|||
import { TestBed } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
RouterTestingModule
|
||||
],
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
it('should create the app', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`should have as title 'li-dance-backoffice'`, () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app.title).toEqual('li-dance-backoffice');
|
||||
});
|
||||
|
||||
it('should render title', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.nativeElement as HTMLElement;
|
||||
expect(compiled.querySelector('.content span')?.textContent).toContain('li-dance-backoffice app is running!');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,10 @@
|
|||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'li-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss']
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'li-dance-backoffice';
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { StudentListComponent } from './components/students/student-list/student-list.component';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { GenderPipe } from './pipes/gender.pipe';
|
||||
import { AddressPipe } from './pipes/address.pipe';
|
||||
import { NamePipe } from './pipes/name.pipe';
|
||||
import { EnrollPipe } from './pipes/enroll.pipe';
|
||||
import { VisitsComponent } from './components/visits/visits.component';
|
||||
import { StudentEditComponent } from './components/students/student-edit/student-edit.component';
|
||||
import { StudentEnrollComponent } from './components/students/student-enroll/student-enroll.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
StudentListComponent,
|
||||
StudentEditComponent,
|
||||
GenderPipe,
|
||||
AddressPipe,
|
||||
NamePipe,
|
||||
EnrollPipe,
|
||||
VisitsComponent,
|
||||
StudentEnrollComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
BrowserAnimationsModule,
|
||||
FormsModule,
|
||||
AppRoutingModule,
|
||||
HttpClientModule,
|
||||
MatTableModule,
|
||||
MatPaginatorModule,
|
||||
MatProgressSpinnerModule,
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule { }
|
|
@ -0,0 +1,44 @@
|
|||
<div id="student-edit-overlay" [class]="visibility()">
|
||||
<div id="student-edit-container">
|
||||
<form id="student-edit-form">
|
||||
<label id="student-edit-firstname-label" for="student-edit-firstname-input">Vorname</label>
|
||||
<input id="student-edit-firstname-input" name="student-edit-firstname-input" type="text" [(ngModel)]="model.firstname" />
|
||||
|
||||
<label id="student-edit-lastname-label" for="student-edit-lastname-input">Nachname</label>
|
||||
<input id="student-edit-lastname-input" name="student-edit-lastname-input" type="text" [(ngModel)]="model.lastname" />
|
||||
|
||||
<label id="student-edit-birthday-label" for="student-edit-birthday-input">Geburtsdatum</label>
|
||||
<input id="student-edit-birthday-input" name="student-edit-birthday-input" type="date" [(ngModel)]="model.birthday" />
|
||||
|
||||
<label id="student-edit-gender-label" for="student-edit-gender-input">Geschlecht</label>
|
||||
<select id="student-edit-gender-input" name="student-edit-gender-input" [(ngModel)]="model.gender" >
|
||||
<option [value]="gender.id" *ngFor="let gender of genders">{{gender.text}}</option>
|
||||
</select>
|
||||
|
||||
<label id="student-edit-street-label" for="student-edit-street-input">Strasse</label>
|
||||
<input id="student-edit-street-input" name="student-edit-street-input" type="text" [(ngModel)]="model.street" />
|
||||
|
||||
<label id="student-edit-house-label" for="student-edit-house-input">Hausnummer</label>
|
||||
<input id="student-edit-house-input" name="student-edit-house-input" type="text" [(ngModel)]="model.house" />
|
||||
|
||||
<label id="student-edit-housesuffix-label" for="student-edit-housesuffix-input">Suffix</label>
|
||||
<input id="student-edit-housesuffix-input" name="student-edit-housesuffix-input" type="text" [(ngModel)]="model.house_suffix" />
|
||||
|
||||
<label id="student-edit-zip-label" for="student-edit-zip-input">PLZ</label>
|
||||
<input id="student-edit-zip-input" name="student-edit-zip-input" type="text" [(ngModel)]="model.zip" />
|
||||
|
||||
<label id="student-edit-city-label" for="student-edit-city-input">Stadt</label>
|
||||
<input id="student-edit-city-input" name="student-edit-city-input" type="text" [(ngModel)]="model.city" />
|
||||
|
||||
<label id="student-edit-phone-label" for="student-edit-phone-input">Telefon</label>
|
||||
<input id="student-edit-phone-input" name="student-edit-phone-input" type="text" [(ngModel)]="model.phone" />
|
||||
|
||||
<label id="student-edit-email-label" for="student-edit-email-input">E-Mail</label>
|
||||
<input id="student-edit-email-input" name="student-edit-email-input" type="text" [(ngModel)]="model.email" />
|
||||
</form>
|
||||
<div style="text-align:center;">
|
||||
<button class="button-save" (click)="save()">Save</button>
|
||||
<button class="button-close" (click)="close()">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,75 @@
|
|||
.hidden{
|
||||
display:none;
|
||||
}
|
||||
.show{
|
||||
display:grid;
|
||||
}
|
||||
|
||||
#student-edit-container {
|
||||
grid-column: 2 / 3;
|
||||
|
||||
background: rgb(245, 126, 32);
|
||||
color: #ffffff;
|
||||
box-shadow: 0px 0px 2px 1px black;
|
||||
z-index: 100;
|
||||
padding: 2em;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
#student-edit-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
|
||||
z-index: 99;
|
||||
background: rgba(0,0,0,0.8);
|
||||
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
}
|
||||
|
||||
@media (min-width: 51em) and (max-width: 100em) {
|
||||
#student-edit-overlay {
|
||||
grid-template-columns: 1fr 3fr 1fr;
|
||||
}
|
||||
#student-edit-container {
|
||||
grid-column: 2 / 3;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 50em) {
|
||||
#student-edit-overlay {
|
||||
grid-template-columns: 1fr 3fr 1fr;
|
||||
}
|
||||
#student-edit-container {
|
||||
grid-column: 1 / 4;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
color: white;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
button.button-save {
|
||||
background-color: #411ccc;
|
||||
}
|
||||
|
||||
button.button-close {
|
||||
margin-left: 1em;
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
label {
|
||||
display:block;
|
||||
width:100%;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
select,
|
||||
input[type=date],
|
||||
input[type=text]{
|
||||
display:block;
|
||||
width:100%;
|
||||
height: 2em;
|
||||
margin-bottom: 1.5em;
|
||||
font-size: 1em;
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { Student } from 'src/app/models/student';
|
||||
import { StudentsService } from 'src/app/services/students/students.service';
|
||||
|
||||
@Component({
|
||||
selector: 'li-student-edit',
|
||||
templateUrl: './student-edit.component.html',
|
||||
styleUrls: ['./student-edit.component.scss']
|
||||
})
|
||||
export class StudentEditComponent implements OnInit {
|
||||
|
||||
@Input()
|
||||
public student?: Student = undefined;
|
||||
|
||||
@Output()
|
||||
public closing = new EventEmitter();
|
||||
|
||||
public genders = [
|
||||
{id: 0, text: 'Männlich'},
|
||||
{id: 1, text: 'Weiblich'},
|
||||
{id: 2, text: 'Divers'},
|
||||
];
|
||||
|
||||
public get model(): Student {
|
||||
return this.student || <Student>{};
|
||||
}
|
||||
|
||||
constructor(private studentsService: StudentsService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
this.closing.emit();
|
||||
}
|
||||
|
||||
public save(): void {
|
||||
if(this.student){
|
||||
this.studentsService.set(this.model).subscribe(_ => this.closing.emit());
|
||||
} else {
|
||||
this.closing.emit();
|
||||
}
|
||||
}
|
||||
|
||||
public visibility(): string {
|
||||
return this.student ? 'show' : 'hidden';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<div id="student-enroll-overlay" [class]="visibility()">
|
||||
<div id="student-enroll-container">
|
||||
<h1>{{model | name}}</h1>
|
||||
|
||||
<form id="student-enroll-form">
|
||||
<div style="display: grid; grid-template-columns: auto auto auto auto;">
|
||||
<ng-container *ngFor="let enrollment of student?.enrollments; index as i">
|
||||
<div class="enrollment-name">{{ enrollment.name }}</div>
|
||||
<div>
|
||||
<input *ngIf="enrollment.begin" id="student-enroll-begin-input{{i}}" name="student-enroll-begin-input{{i}}" type="date" [(ngModel)]="enrollment.begin" />
|
||||
<button *ngIf="!enrollment.begin" class="button-add" (click)="enroll(enrollment)">+</button>
|
||||
</div>
|
||||
<div>
|
||||
<input *ngIf="enrollment.end" id="student-enroll-end-input{{i}}" name="student-enroll-end-input{{i}}" type="date" [(ngModel)]="enrollment.end" />
|
||||
</div>
|
||||
<div>
|
||||
<button *ngIf="enrollment.begin" class="button-remove" (click)="deroll(enrollment)">X</button>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div style="text-align:center;">
|
||||
<button class="button-save" (click)="save()">Save</button>
|
||||
<button class="button-close" (click)="close()">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,65 @@
|
|||
.hidden{
|
||||
display:none;
|
||||
}
|
||||
.show{
|
||||
display:grid;
|
||||
}
|
||||
|
||||
#student-enroll-container {
|
||||
grid-column: 2 / 3;
|
||||
|
||||
background: rgb(245, 126, 32);
|
||||
color: #ffffff;
|
||||
box-shadow: 0px 0px 2px 1px black;
|
||||
z-index: 100;
|
||||
padding: 2em;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
#student-enroll-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
|
||||
z-index: 99;
|
||||
background: rgba(0,0,0,0.8);
|
||||
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
}
|
||||
|
||||
@media (min-width: 51em) and (max-width: 100em) {
|
||||
#student-enroll-overlay {
|
||||
grid-template-columns: 1fr 3fr 1fr;
|
||||
}
|
||||
#student-enroll-container {
|
||||
grid-column: 2 / 3;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 50em) {
|
||||
#student-enroll-overlay {
|
||||
grid-template-columns: 1fr 3fr 1fr;
|
||||
}
|
||||
#student-enroll-container {
|
||||
grid-column: 1 / 4;
|
||||
}
|
||||
}
|
||||
|
||||
.enrollment-name {
|
||||
line-height: 3em;
|
||||
}
|
||||
|
||||
button {
|
||||
color: white;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
button.button-add,
|
||||
button.button-save {
|
||||
background-color: #411ccc;
|
||||
}
|
||||
|
||||
button.button-remove,
|
||||
button.button-close {
|
||||
margin-left: 1em;
|
||||
background-color: red;
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
import { formatDate } from '@angular/common';
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { Student } from 'src/app/models/student';
|
||||
import { StudentEnrollment } from 'src/app/models/student-enrollment';
|
||||
import { EnrollService } from 'src/app/services/enroll/enroll.service';
|
||||
|
||||
@Component({
|
||||
selector: 'li-student-enroll',
|
||||
templateUrl: './student-enroll.component.html',
|
||||
styleUrls: ['./student-enroll.component.scss']
|
||||
})
|
||||
export class StudentEnrollComponent implements OnInit {
|
||||
|
||||
@Input()
|
||||
public student?: Student;
|
||||
|
||||
@Output()
|
||||
public closing = new EventEmitter();
|
||||
|
||||
|
||||
public get model(): Student {
|
||||
return this.student || <Student>{};
|
||||
}
|
||||
|
||||
public constructor(private enrollService: EnrollService) { }
|
||||
|
||||
public ngOnInit() {
|
||||
}
|
||||
|
||||
public enroll(enrollment: StudentEnrollment) {
|
||||
enrollment.begin = formatDate(new Date(), 'yyyy-MM-dd', 'en-US');
|
||||
enrollment.end = formatDate(new Date('2100-01-01'), 'yyyy-MM-dd', 'en-US');
|
||||
}
|
||||
|
||||
public deroll(enrollment: StudentEnrollment) {
|
||||
enrollment.begin = '';
|
||||
enrollment.end = '';
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
this.closing.emit();
|
||||
}
|
||||
|
||||
public save(): void {
|
||||
if (!this.student) {
|
||||
this.closing.emit();
|
||||
return;
|
||||
}
|
||||
|
||||
this.enrollService.set(
|
||||
this.student.sid,
|
||||
this.student.enrollments.filter(e => e.begin))
|
||||
.subscribe(_ => this.closing.emit());
|
||||
}
|
||||
|
||||
public visibility(): string {
|
||||
return this.student ? 'show' : 'hidden';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
<div id="content">
|
||||
<div class="grid-item">
|
||||
<button class="button-add" (click)="add()">Hinzufügen</button>
|
||||
</div>
|
||||
<div class="grid-item">
|
||||
</div>
|
||||
<div class="grid-item-full">
|
||||
<div *ngIf="loading;else table">
|
||||
<mat-spinner class="center"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<ng-template #table>
|
||||
<mat-table [dataSource]="dataSource">
|
||||
<ng-container matColumnDef="Name">
|
||||
<mat-header-cell *matHeaderCellDef> Name </mat-header-cell>
|
||||
<mat-cell *matCellDef="let element" (click)="edit(element)">{{element | name}}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="Enroll">
|
||||
<mat-header-cell *matHeaderCellDef> Gruppen </mat-header-cell>
|
||||
<mat-cell *matCellDef="let element" (click)="enroll(element)">{{element | enroll}}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="Birthday">
|
||||
<mat-header-cell *matHeaderCellDef> Geburtsdatum </mat-header-cell>
|
||||
<mat-cell *matCellDef="let element" (click)="edit(element)">{{element.birthday | date: 'dd.MM.yyyy'}}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="Gender">
|
||||
<mat-header-cell *matHeaderCellDef> Geschlecht </mat-header-cell>
|
||||
<mat-cell *matCellDef="let element" (click)="edit(element)">{{element | gender}}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="Address">
|
||||
<mat-header-cell *matHeaderCellDef> Adresse </mat-header-cell>
|
||||
<mat-cell *matCellDef="let element" (click)="edit(element)">{{element | address}}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="Phone">
|
||||
<mat-header-cell *matHeaderCellDef> Telefon </mat-header-cell>
|
||||
<mat-cell *matCellDef="let element" (click)="edit(element)">{{element.phone}}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="E-Mail">
|
||||
<mat-header-cell *matHeaderCellDef> E-Mail </mat-header-cell>
|
||||
<mat-cell *matCellDef="let element" (click)="edit(element)">{{element.email}}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="Actions">
|
||||
<mat-header-cell *matHeaderCellDef></mat-header-cell>
|
||||
<mat-cell *matCellDef="let element" class="actions"><a (click)="delete(element)"><i class="bi bi-trash"></i></a></mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<mat-header-row *matHeaderRowDef="[ 'Name', 'Enroll', 'Birthday', 'Gender', 'Address', 'Phone', 'E-Mail', 'Actions' ] "></mat-header-row>
|
||||
<mat-row *matRowDef="let row; columns: [ 'Name', 'Enroll', 'Birthday', 'Gender', 'Address', 'Phone', 'E-Mail', 'Actions' ] "></mat-row>
|
||||
</mat-table>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="grid-item">
|
||||
<input matInput id="pipelineFilter" (keyup)="applyFilter($event)" placeholder="Filtern">
|
||||
</div>
|
||||
<div class="grid-item">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<li-student-edit [student]="selectedStudent" (closing)="commit()"></li-student-edit>
|
||||
<li-student-enroll [student]="enrollingStudent" (closing)="commit()"></li-student-enroll>
|
|
@ -0,0 +1,74 @@
|
|||
#content {
|
||||
margin-top: 24px;
|
||||
background-color: rgb(245, 126, 32);
|
||||
color: #ffffff;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: auto auto;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
#pipelineFilter {
|
||||
margin: 12px;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
button {
|
||||
color: white;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
button.button-add {
|
||||
background-color: #411ccc;
|
||||
}
|
||||
|
||||
.center {
|
||||
width: 50%;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
a:link,
|
||||
a:visited,
|
||||
a:active {
|
||||
text-decoration: none;
|
||||
text-transform: none;
|
||||
color: white;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #411ccc;
|
||||
}
|
||||
|
||||
.grid-item-full {
|
||||
grid-column-start: 1;
|
||||
grid-column-end: 3;
|
||||
}
|
||||
|
||||
.bi {
|
||||
color: #411ccc;
|
||||
font-size: 2em;
|
||||
margin-right:0.4em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mat-row:hover .mat-cell {
|
||||
background-color: #411ccc;
|
||||
}
|
||||
|
||||
.mat-row:hover .mat-cell.actions .bi {
|
||||
color: white;
|
||||
}
|
||||
|
||||
#pipelineFilter{
|
||||
display:block;
|
||||
height: 2em;
|
||||
margin-bottom: 1.5em;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.actions a {
|
||||
margin-left: 3em;
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { Course } from 'src/app/models/course';
|
||||
import { Student } from 'src/app/models/student';
|
||||
import { StudentEnrollment } from 'src/app/models/student-enrollment';
|
||||
import { CoursesService } from 'src/app/services/courses/courses.service';
|
||||
import { StudentsService } from 'src/app/services/students/students.service';
|
||||
|
||||
@Component({
|
||||
selector: 'li-student-list',
|
||||
templateUrl: './student-list.component.html',
|
||||
styleUrls: ['./student-list.component.scss']
|
||||
})
|
||||
export class StudentListComponent implements OnInit {
|
||||
|
||||
public loading: boolean = true;
|
||||
public students: Student[] = new Array<Student>();
|
||||
public courses: Course[] = new Array<Course>();
|
||||
public selectedStudent?: Student;
|
||||
public enrollingStudent?: Student;
|
||||
public enrollments: StudentEnrollment[] = new Array<StudentEnrollment>();
|
||||
public dataSource = new MatTableDataSource<Student>();
|
||||
|
||||
public constructor(
|
||||
private studentsService: StudentsService,
|
||||
private coursesService: CoursesService) { }
|
||||
|
||||
public ngOnInit() {
|
||||
this.dataSource.filterPredicate = function (record: any, filter: any) {
|
||||
if(filter.length < 3) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const searchIn = (record.firstname?.toLocaleLowerCase()
|
||||
+ record.lastname?.toLocaleLowerCase()
|
||||
+ record.street?.toLocaleLowerCase()
|
||||
+ record.email?.toLocaleLowerCase()) || '';
|
||||
const searchFor = filter.toLocaleLowerCase() || '';
|
||||
|
||||
return searchIn.indexOf(searchFor) >= 0;
|
||||
}
|
||||
|
||||
this.getData();
|
||||
}
|
||||
|
||||
public applyFilter(event: Event) {
|
||||
const filterValue = (event.target as HTMLInputElement).value;
|
||||
this.dataSource.filter = filterValue.trim().toLowerCase();
|
||||
}
|
||||
|
||||
public add(): void {
|
||||
this.selectedStudent = <Student>{};
|
||||
}
|
||||
|
||||
public delete(student: Student): void {
|
||||
this.studentsService.del(student).subscribe(_ => this.getData());
|
||||
}
|
||||
|
||||
public edit(student: Student): void {
|
||||
this.selectedStudent = student;
|
||||
}
|
||||
|
||||
public enroll(student: Student): void {
|
||||
const enrollingStudent = Object.assign({}, student);
|
||||
|
||||
enrollingStudent.enrollments = this.courses.map(c => <StudentEnrollment> {
|
||||
cid: c.cid,
|
||||
name: c.name,
|
||||
diffname: c.diffname,
|
||||
begin: student?.enrollments.find(e => e.cid === c.cid)?.begin || undefined,
|
||||
end: student?.enrollments.find(e => e.cid === c.cid)?.end || undefined,
|
||||
});
|
||||
|
||||
this.enrollingStudent = enrollingStudent;
|
||||
}
|
||||
|
||||
public commit(): void {
|
||||
this.selectedStudent = undefined;
|
||||
this.enrollingStudent = undefined;
|
||||
this.getData();
|
||||
}
|
||||
|
||||
private getData() {
|
||||
this.studentsService.get()
|
||||
.subscribe({
|
||||
next: students => {
|
||||
this.loading = false;
|
||||
this.students = students;
|
||||
this.dataSource.data = this.students;
|
||||
}
|
||||
});
|
||||
|
||||
this.coursesService.get()
|
||||
.subscribe(c => this.courses = c);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<div *ngIf="!courseVisit" id="content">
|
||||
<h1>Zur Zeit kein Kurs!</h1>
|
||||
</div>
|
||||
|
||||
<div *ngIf="courseVisit" id="content">
|
||||
<div class="grid-item">
|
||||
<h1>{{courseVisit?.name}}</h1>
|
||||
<h2>{{courseVisit?.date | date: 'dd.MM.yyyy'}}, {{courseVisit?.begin}} - {{courseVisit?.end}}</h2>
|
||||
</div>
|
||||
<div class="grid-item">
|
||||
</div>
|
||||
<div class="grid-item-full">
|
||||
<div *ngIf="loading;else table">
|
||||
<mat-spinner class="center"></mat-spinner>
|
||||
</div>
|
||||
|
||||
<ng-template #table>
|
||||
<table mat-table [dataSource]="dataSource">
|
||||
<ng-container matColumnDef="Name">
|
||||
<th mat-header-cell *matHeaderCellDef> Name </th>
|
||||
<td mat-cell *matCellDef="let element">{{element | name}}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="Visited">
|
||||
<th mat-header-cell *matHeaderCellDef> Anwesend </th>
|
||||
<td mat-cell *matCellDef="let element"><input type="checkbox" [checked]="element.visited > 0" (change)="visit(element)"/></ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="[ 'Name', 'Visited' ] "></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: [ 'Name', 'Visited' ] "></tr>
|
||||
</table>
|
||||
</ng-template>
|
||||
</div>
|
||||
<div class="grid-item">
|
||||
</div>
|
||||
<div class="grid-item">
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,44 @@
|
|||
#content {
|
||||
margin-top: 24px;
|
||||
background-color: rgb(245, 126, 32);
|
||||
color: #ffffff;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: auto auto;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
#pipelineFilter {
|
||||
margin: 12px;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.center {
|
||||
width: 50%;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
a:link,
|
||||
a:visited,
|
||||
a:active {
|
||||
text-decoration: none;
|
||||
text-transform: none;
|
||||
color: white;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: aqua;
|
||||
}
|
||||
|
||||
input[type=checkbox] {
|
||||
width:2em;
|
||||
height:2em;
|
||||
}
|
||||
|
||||
.grid-item-full {
|
||||
grid-column-start: 1;
|
||||
grid-column-end: 3;
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { CourseVisit } from 'src/app/models/course-visit';
|
||||
import { StudentVisit } from 'src/app/models/student-visit';
|
||||
import { Visit } from 'src/app/models/visit';
|
||||
import { VisitsService } from 'src/app/services/visits/visits.service';
|
||||
|
||||
@Component({
|
||||
selector: 'li-visits',
|
||||
templateUrl: './visits.component.html',
|
||||
styleUrls: ['./visits.component.scss']
|
||||
})
|
||||
export class VisitsComponent implements OnInit {
|
||||
|
||||
public loading: boolean = true;
|
||||
public courseVisit?: CourseVisit;
|
||||
public dataSource = new MatTableDataSource<StudentVisit>();
|
||||
|
||||
public constructor(private visitsService: VisitsService) { }
|
||||
|
||||
public ngOnInit() {
|
||||
this.getData();
|
||||
}
|
||||
|
||||
public visit(studentVisit: StudentVisit): void {
|
||||
const visit = <Visit> {
|
||||
cid: this.courseVisit?.cid,
|
||||
sid: studentVisit.sid,
|
||||
date: this.courseVisit?.date
|
||||
}
|
||||
|
||||
if(studentVisit.visited > 0) {
|
||||
this.visitsService.del(visit).subscribe(_ => this.getData());
|
||||
} else {
|
||||
this.visitsService.set(visit).subscribe(_ => this.getData());
|
||||
}
|
||||
}
|
||||
|
||||
private getData(): void {
|
||||
//const now = new Date('2022-11-03 19:17:00');
|
||||
const now = new Date();
|
||||
|
||||
this.visitsService.get(now)
|
||||
.subscribe({
|
||||
next: courseVisit => {
|
||||
this.loading = false;
|
||||
this.courseVisit = courseVisit;
|
||||
if(this.courseVisit) {
|
||||
this.dataSource.data = this.courseVisit.students;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import { StudentVisit } from "./student-visit";
|
||||
|
||||
export interface CourseVisit {
|
||||
cid: number;
|
||||
name: string;
|
||||
date: Date;
|
||||
begin: string;
|
||||
end: string;
|
||||
students: StudentVisit[];
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
export interface Course {
|
||||
cid: number;
|
||||
name: string;
|
||||
diffname: string;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
export interface Enrollment {
|
||||
sid: number;
|
||||
cid: number;
|
||||
begin: Date;
|
||||
end: Date;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
export interface StudentEnrollment {
|
||||
cid: number;
|
||||
name: string;
|
||||
diffname: string;
|
||||
begin: string;
|
||||
end: string;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
export interface StudentVisit {
|
||||
sid: number;
|
||||
firstname: string;
|
||||
lastname: string;
|
||||
visited: number;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
import { StudentEnrollment } from "./student-enrollment";
|
||||
|
||||
export interface Student {
|
||||
sid: number;
|
||||
firstname: string;
|
||||
lastname: string;
|
||||
birthday: Date;
|
||||
gender: number;
|
||||
street: string;
|
||||
house: number;
|
||||
house_suffix: string;
|
||||
zip: string;
|
||||
city: string;
|
||||
phone: string;
|
||||
email: string;
|
||||
enrollments: StudentEnrollment[];
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
export interface Visit {
|
||||
cid: number;
|
||||
sid: number;
|
||||
date: Date;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { Student } from '../models/student';
|
||||
|
||||
@Pipe({name: 'address'})
|
||||
export class AddressPipe implements PipeTransform {
|
||||
transform(student: Student): string {
|
||||
let result = `${student.street} ${student.house}${student.house_suffix}, ${student.zip} ${student.city}`
|
||||
return result.trim() === '0,' ? '' : result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { Student } from '../models/student';
|
||||
|
||||
@Pipe({
|
||||
name: 'enroll'
|
||||
})
|
||||
export class EnrollPipe implements PipeTransform {
|
||||
|
||||
transform(student: Student): string {
|
||||
return student.enrollments?.length > 0 ? student.enrollments?.map(e => e.name).join(', ') : '+';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { Student } from '../models/student';
|
||||
|
||||
@Pipe({name: 'gender'})
|
||||
export class GenderPipe implements PipeTransform {
|
||||
transform(student: Student): string {
|
||||
switch(Number(student.gender)) {
|
||||
case 0: return 'M';
|
||||
case 1: return 'W';
|
||||
default: return '?';
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { Student } from '../models/student';
|
||||
import { StudentVisit } from '../models/student-visit';
|
||||
|
||||
@Pipe({name: 'name'})
|
||||
export class NamePipe implements PipeTransform {
|
||||
transform(student: Student | StudentVisit): string {
|
||||
return `${student.firstname} ${student.lastname}`;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Course } from 'src/app/models/course';
|
||||
import { environment } from 'src/environments/environment';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class CoursesService {
|
||||
private readonly serviceName = "courses";
|
||||
|
||||
constructor(private http: HttpClient) { }
|
||||
|
||||
public get(): Observable<Course[]> {
|
||||
return this.http.get<Course[]>(`${environment.apiUrl}${this.serviceName}/get.php`);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
import { formatDate } from '@angular/common';
|
||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Enrollment } from 'src/app/models/enrollment';
|
||||
import { Student } from 'src/app/models/student';
|
||||
import { StudentEnrollment } from 'src/app/models/student-enrollment';
|
||||
import { environment } from 'src/environments/environment';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class EnrollService {
|
||||
private readonly serviceName = "enroll";
|
||||
|
||||
constructor(private http: HttpClient) { }
|
||||
|
||||
public get(cid: number, date: Date): Observable<Enrollment> {
|
||||
let params = new HttpParams().set('cid', cid).set('date', formatDate(date, 'yyyy-MM-dd', '' ));
|
||||
return this.http.get<Enrollment>(`${environment.apiUrl}${this.serviceName}/get.php`, { params: params });
|
||||
}
|
||||
|
||||
public set(sid: number, enrollments: StudentEnrollment[]): Observable<boolean> {
|
||||
const payload = `sid=${sid}&enrollments=${JSON.stringify(enrollments)}`;
|
||||
|
||||
return this.http.post<boolean>(`${environment.apiUrl}${this.serviceName}/set.php`, payload);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Student } from 'src/app/models/student';
|
||||
import { environment } from 'src/environments/environment';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class StudentsService {
|
||||
private readonly serviceName = "students";
|
||||
|
||||
constructor(private http: HttpClient) { }
|
||||
|
||||
public get(): Observable<Student[]> {
|
||||
return this.http.get<Student[]>(`${environment.apiUrl}${this.serviceName}/get.php`);
|
||||
}
|
||||
|
||||
public set(student: Student): Observable<void> {
|
||||
const payload = `sid=${student.sid}&firstname=${student.firstname}&lastname=${student.lastname}
|
||||
&birthday=${student.birthday}&gender=${student.gender}&street=${student.street}
|
||||
&house=${student.house}&house_suffix=${student.house_suffix}&zip=${student.zip}
|
||||
&city=${student.city}&phone=${student.phone}&email=${student.email}`;
|
||||
|
||||
return this.http.post<void>(`${environment.apiUrl}${this.serviceName}/set.php`, payload);
|
||||
}
|
||||
|
||||
public del(student: Student): Observable<void> {
|
||||
const payload = `sid=${student.sid}`;
|
||||
|
||||
return this.http.post<void>(`${environment.apiUrl}${this.serviceName}/del.php`, payload);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
import { DatePipe, formatDate } from '@angular/common';
|
||||
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { CourseVisit } from 'src/app/models/course-visit';
|
||||
import { Visit } from 'src/app/models/visit';
|
||||
import { environment } from 'src/environments/environment';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class VisitsService {
|
||||
private readonly serviceName = "visits";
|
||||
|
||||
constructor(private http: HttpClient) { }
|
||||
|
||||
public get(date: Date): Observable<CourseVisit> {
|
||||
const payload = `date=${formatDate(date, 'yyyy-MM-dd', 'en-US' )}&time=${formatDate(date, 'HH:mm', 'en-US' )}`;
|
||||
|
||||
// Not easy to pass "time" over GET
|
||||
return this.http.post<CourseVisit>(`${environment.apiUrl}${this.serviceName}/get.php`, payload);
|
||||
}
|
||||
|
||||
public set(visit: Visit): Observable<boolean> {
|
||||
const payload = `cid=${visit.cid}&sid=${visit.sid}&date=${formatDate(visit.date, 'yyyy-MM-dd', 'en-US' )}`
|
||||
|
||||
return this.http.post<boolean>(`${environment.apiUrl}${this.serviceName}/set.php`, payload);
|
||||
}
|
||||
|
||||
public del(visit: Visit): Observable<boolean> {
|
||||
// Delete accepts no payload
|
||||
const payload = `cid=${visit.cid}&sid=${visit.sid}&date=${formatDate(visit.date, 'yyyy-MM-dd', 'en-US' )}`
|
||||
|
||||
return this.http.post<boolean>(`${environment.apiUrl}${this.serviceName}/del.php`, payload);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
export const environment = {
|
||||
production: true,
|
||||
apiUrl: 'https://li-dance.de/plan/api/'
|
||||
};
|
|
@ -0,0 +1,17 @@
|
|||
// This file can be replaced during build by using the `fileReplacements` array.
|
||||
// `ng build` replaces `environment.ts` with `environment.prod.ts`.
|
||||
// The list of file replacements can be found in `angular.json`.
|
||||
|
||||
export const environment = {
|
||||
production: false,
|
||||
apiUrl: 'https://li-dance.de/plan/api/'
|
||||
};
|
||||
|
||||
/*
|
||||
* For easier debugging in development mode, you can import the following file
|
||||
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
|
||||
*
|
||||
* This import should be commented out in production mode because it will have a negative impact
|
||||
* on performance if an error is thrown.
|
||||
*/
|
||||
// import 'zone.js/plugins/zone-error'; // Included with Angular CLI.
|
Binary file not shown.
After Width: | Height: | Size: 948 B |
|
@ -0,0 +1,16 @@
|
|||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Li-Dance Backoffice</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
</head>
|
||||
<body class="mat-typography">
|
||||
<li-root></li-root>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,12 @@
|
|||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
import { environment } from './environments/environment';
|
||||
|
||||
if (environment.production) {
|
||||
enableProdMode();
|
||||
}
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||
.catch(err => console.error(err));
|
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* This file includes polyfills needed by Angular and is loaded before the app.
|
||||
* You can add your own extra polyfills to this file.
|
||||
*
|
||||
* This file is divided into 2 sections:
|
||||
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
|
||||
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
|
||||
* file.
|
||||
*
|
||||
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
|
||||
* automatically update themselves. This includes recent versions of Safari, Chrome (including
|
||||
* Opera), Edge on the desktop, and iOS and Chrome on mobile.
|
||||
*
|
||||
* Learn more in https://angular.io/guide/browser-support
|
||||
*/
|
||||
|
||||
/***************************************************************************************************
|
||||
* BROWSER POLYFILLS
|
||||
*/
|
||||
|
||||
/**
|
||||
* By default, zone.js will patch all possible macroTask and DomEvents
|
||||
* user can disable parts of macroTask/DomEvents patch by setting following flags
|
||||
* because those flags need to be set before `zone.js` being loaded, and webpack
|
||||
* will put import in the top of bundle, so user need to create a separate file
|
||||
* in this directory (for example: zone-flags.ts), and put the following flags
|
||||
* into that file, and then add the following code before importing zone.js.
|
||||
* import './zone-flags';
|
||||
*
|
||||
* The flags allowed in zone-flags.ts are listed here.
|
||||
*
|
||||
* The following flags will work for all browsers.
|
||||
*
|
||||
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
|
||||
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
|
||||
* (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
|
||||
*
|
||||
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
|
||||
* with the following flag, it will bypass `zone.js` patch for IE/Edge
|
||||
*
|
||||
* (window as any).__Zone_enable_cross_context_check = true;
|
||||
*
|
||||
*/
|
||||
|
||||
/***************************************************************************************************
|
||||
* Zone JS is required by default for Angular itself.
|
||||
*/
|
||||
import 'zone.js'; // Included with Angular CLI.
|
||||
|
||||
|
||||
/***************************************************************************************************
|
||||
* APPLICATION IMPORTS
|
||||
*/
|
|
@ -0,0 +1,27 @@
|
|||
/* Provide sufficient contrast against white background */
|
||||
|
||||
@import "~bootstrap-icons/font/bootstrap-icons.css";
|
||||
a {
|
||||
color: #0366d6;
|
||||
}
|
||||
|
||||
code {
|
||||
color: #e01a76;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
color: #fff;
|
||||
background-color: #1b6ec2;
|
||||
border-color: #1861ac;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: rgb(245, 126, 32);
|
||||
margin: 0;
|
||||
font-family: Roboto, "Helvetica Neue", sans-serif;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||
|
||||
import 'zone.js/testing';
|
||||
import { getTestBed } from '@angular/core/testing';
|
||||
import {
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting
|
||||
} from '@angular/platform-browser-dynamic/testing';
|
||||
|
||||
declare const require: {
|
||||
context(path: string, deep?: boolean, filter?: RegExp): {
|
||||
<T>(id: string): T;
|
||||
keys(): string[];
|
||||
};
|
||||
};
|
||||
|
||||
// First, initialize the Angular testing environment.
|
||||
getTestBed().initTestEnvironment(
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting(),
|
||||
);
|
||||
|
||||
// Then we find all the tests.
|
||||
const context = require.context('./', true, /\.spec\.ts$/);
|
||||
// And load the modules.
|
||||
context.keys().map(context);
|
|
@ -0,0 +1,15 @@
|
|||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/app",
|
||||
"types": []
|
||||
},
|
||||
"files": [
|
||||
"src/main.ts",
|
||||
"src/polyfills.ts"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"outDir": "./dist/out-tsc",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"sourceMap": true,
|
||||
"declaration": false,
|
||||
"downlevelIteration": true,
|
||||
"experimentalDecorators": true,
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"target": "es2017",
|
||||
"module": "es2020",
|
||||
"lib": [
|
||||
"es2020",
|
||||
"dom"
|
||||
]
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"strictTemplates": true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"src/test.ts",
|
||||
"src/polyfills.ts"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue