First Alpha release candidate
All checks were successful
Jenkins Production Deployment

This commit is contained in:
Patrick Müller 2021-10-01 19:25:39 +02:00
parent 8e1c071645
commit 59682c3091
11 changed files with 365 additions and 48 deletions

View File

@ -13,6 +13,7 @@
"@angular/compiler": "~12.2.0", "@angular/compiler": "~12.2.0",
"@angular/core": "~12.2.0", "@angular/core": "~12.2.0",
"@angular/forms": "~12.2.0", "@angular/forms": "~12.2.0",
"@angular/material": "^12.2.8",
"@angular/platform-browser": "~12.2.0", "@angular/platform-browser": "~12.2.0",
"@angular/platform-browser-dynamic": "~12.2.0", "@angular/platform-browser-dynamic": "~12.2.0",
"@angular/router": "~12.2.0", "@angular/router": "~12.2.0",
@ -288,6 +289,30 @@
"@angular/core": "12.2.6" "@angular/core": "12.2.6"
} }
}, },
"node_modules/@angular/cdk": {
"version": "12.2.8",
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-12.2.8.tgz",
"integrity": "sha512-M0Y61o0yEVLMg+DSNsaDgiOifAV6OdumTgt2/kNoSuauPRWS0bkZJE58k3LR+cPi1Cho3UXELMKMOXZN9AhofA==",
"peer": true,
"dependencies": {
"tslib": "^2.2.0"
},
"optionalDependencies": {
"parse5": "^5.0.0"
},
"peerDependencies": {
"@angular/common": "^12.0.0 || ^13.0.0-0",
"@angular/core": "^12.0.0 || ^13.0.0-0",
"rxjs": "^6.5.3 || ^7.0.0"
}
},
"node_modules/@angular/cdk/node_modules/parse5": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
"integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
"optional": true,
"peer": true
},
"node_modules/@angular/cli": { "node_modules/@angular/cli": {
"version": "12.2.6", "version": "12.2.6",
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-12.2.6.tgz", "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-12.2.6.tgz",
@ -495,6 +520,22 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/@angular/material": {
"version": "12.2.8",
"resolved": "https://registry.npmjs.org/@angular/material/-/material-12.2.8.tgz",
"integrity": "sha512-wRTaTZIGC9+2e8aft44V9Qqwp3PsR9AG0FeJ0spl8mdOlYEqMMyoRXjvMiWIjo2ywxHLoQgLXXsWn3ip2xnnVg==",
"dependencies": {
"tslib": "^2.2.0"
},
"peerDependencies": {
"@angular/animations": "^12.0.0 || ^13.0.0-0",
"@angular/cdk": "12.2.8",
"@angular/common": "^12.0.0 || ^13.0.0-0",
"@angular/core": "^12.0.0 || ^13.0.0-0",
"@angular/forms": "^12.0.0 || ^13.0.0-0",
"rxjs": "^6.5.3 || ^7.0.0"
}
},
"node_modules/@angular/platform-browser": { "node_modules/@angular/platform-browser": {
"version": "12.2.6", "version": "12.2.6",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-12.2.6.tgz", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-12.2.6.tgz",
@ -16251,6 +16292,25 @@
"tslib": "^2.2.0" "tslib": "^2.2.0"
} }
}, },
"@angular/cdk": {
"version": "12.2.8",
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-12.2.8.tgz",
"integrity": "sha512-M0Y61o0yEVLMg+DSNsaDgiOifAV6OdumTgt2/kNoSuauPRWS0bkZJE58k3LR+cPi1Cho3UXELMKMOXZN9AhofA==",
"peer": true,
"requires": {
"parse5": "^5.0.0",
"tslib": "^2.2.0"
},
"dependencies": {
"parse5": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
"integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==",
"optional": true,
"peer": true
}
}
},
"@angular/cli": { "@angular/cli": {
"version": "12.2.6", "version": "12.2.6",
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-12.2.6.tgz", "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-12.2.6.tgz",
@ -16386,6 +16446,14 @@
} }
} }
}, },
"@angular/material": {
"version": "12.2.8",
"resolved": "https://registry.npmjs.org/@angular/material/-/material-12.2.8.tgz",
"integrity": "sha512-wRTaTZIGC9+2e8aft44V9Qqwp3PsR9AG0FeJ0spl8mdOlYEqMMyoRXjvMiWIjo2ywxHLoQgLXXsWn3ip2xnnVg==",
"requires": {
"tslib": "^2.2.0"
}
},
"@angular/platform-browser": { "@angular/platform-browser": {
"version": "12.2.6", "version": "12.2.6",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-12.2.6.tgz", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-12.2.6.tgz",

View File

@ -1,41 +1,42 @@
{ {
"name": "com.p4ddy.rapla", "name": "com.p4ddy.rapla",
"version": "1.0.0", "version": "1.0.0",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "ng serve", "start": "ng serve",
"build": "ng build", "build": "ng build",
"watch": "ng build --watch --configuration development", "watch": "ng build --watch --configuration development",
"test": "ng test" "test": "ng test"
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "~12.2.0", "@angular/animations": "~12.2.0",
"@angular/common": "~12.2.0", "@angular/common": "~12.2.0",
"@angular/compiler": "~12.2.0", "@angular/compiler": "~12.2.0",
"@angular/core": "~12.2.0", "@angular/core": "~12.2.0",
"@angular/forms": "~12.2.0", "@angular/forms": "~12.2.0",
"@angular/platform-browser": "~12.2.0", "@angular/material": "^12.2.8",
"@angular/platform-browser-dynamic": "~12.2.0", "@angular/platform-browser": "~12.2.0",
"@angular/router": "~12.2.0", "@angular/platform-browser-dynamic": "~12.2.0",
"@ng-bootstrap/ng-bootstrap": "^10.0.0", "@angular/router": "~12.2.0",
"bootstrap": "^5.1.1", "@ng-bootstrap/ng-bootstrap": "^10.0.0",
"rxjs": "~6.6.0", "bootstrap": "^5.1.1",
"tslib": "^2.3.0", "rxjs": "~6.6.0",
"zone.js": "~0.11.4" "tslib": "^2.3.0",
}, "zone.js": "~0.11.4"
"devDependencies": { },
"@angular-devkit/build-angular": "~12.2.6", "devDependencies": {
"@angular/cli": "~12.2.6", "@angular-devkit/build-angular": "~12.2.6",
"@angular/compiler-cli": "~12.2.0", "@angular/cli": "~12.2.6",
"@types/jasmine": "~3.8.0", "@angular/compiler-cli": "~12.2.0",
"@types/node": "^12.20.27", "@types/jasmine": "~3.8.0",
"jasmine-core": "~3.8.0", "@types/node": "^12.20.27",
"karma": "~6.3.0", "jasmine-core": "~3.8.0",
"karma-chrome-launcher": "~3.1.0", "karma": "~6.3.0",
"karma-coverage": "~2.0.3", "karma-chrome-launcher": "~3.1.0",
"karma-jasmine": "~4.0.0", "karma-coverage": "~2.0.3",
"karma-jasmine-html-reporter": "~1.7.0", "karma-jasmine": "~4.0.0",
"typescript": "~4.3.5" "karma-jasmine-html-reporter": "~1.7.0",
} "typescript": "~4.3.5"
}
} }

View File

@ -15,6 +15,8 @@ import {EventDetailComponent} from './components/event-detail/event-detail.compo
import {HttpClientModule} from '@angular/common/http'; import {HttpClientModule} from '@angular/common/http';
import {FormsModule} from '@angular/forms'; import {FormsModule} from '@angular/forms';
import {ApiService} from './services/api/api.service'; import {ApiService} from './services/api/api.service';
import {MatDialogModule} from '@angular/material/dialog';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
@NgModule({ @NgModule({
declarations: [ declarations: [

View File

@ -1,10 +1,12 @@
import {NgModule} from '@angular/core'; import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router'; import {RouterModule, Routes} from '@angular/router';
import { EventDetailComponent } from './components/event-detail/event-detail.component';
import {LandingpageComponent} from './pages/landingpage/landingpage.component'; import {LandingpageComponent} from './pages/landingpage/landingpage.component';
import {PageNotFoundComponent} from './pages/page-not-found/page-not-found.component'; import {PageNotFoundComponent} from './pages/page-not-found/page-not-found.component';
const routes: Routes = [ const routes: Routes = [
{path: '', component: LandingpageComponent, pathMatch: 'full'}, {path: '', component: LandingpageComponent, pathMatch: 'full'},
{path: 'eventDetails/:id', component: EventDetailComponent},
{path: '**', component: PageNotFoundComponent} {path: '**', component: PageNotFoundComponent}
]; ];

View File

@ -0,0 +1,41 @@
.title {
text-align: center;
font-size: 2em;
font-weight: bold;
}
footer {
text-align: center;
}
.backBtn {
background-color: dimgrey; /* Green */
border: none;
color: #E0E5E9;
padding: .15em .5em;
text-align: center;
display: inline-block;
border-radius: .5em;
margin: .5em;
}
.info-table {
padding: .5em;
}
.event-current-status, .event-changes {
display: flex;
justify-content: center;
}
.changes-table th, .changes-table td {
padding-inline: .5em;
}
.change-old-version {
color: red;
}
.change-new-version {
color: green;
}

View File

@ -1 +1,69 @@
<p>Event details popover</p> <header>
<button (click)="closeTab()" class="backBtn">&lt;&lt; Back</button>
<p class="title">Event: {{event.latest_event_summary}}</p>
</header>
<div class="event-current-status">
<table class="info-table">
<tr>
<th>Most recent details</th>
</tr>
<tr>
<td>Description:</td>
<td>&nbsp;{{latestFullChange.new_description}}</td>
</tr>
<tr>
<td>Created:</td>
<td>&nbsp;{{latestFullChange.new_created}}</td>
</tr>
<tr>
<td>Start:</td>
<td>&nbsp;{{latestFullChange.new_start}}</td>
</tr>
<tr>
<td>End:</td>
<td>&nbsp;{{latestFullChange.new_end}}</td>
</tr>
<tr>
<td>Last modified:</td>
<td>&nbsp;{{latestFullChange.new_last_modified}}</td>
</tr>
<tr>
<td>Location:</td>
<td>&nbsp;{{latestFullChange.new_location}}</td>
</tr>
<tr>
<td>Organizer:</td>
<td>&nbsp;{{latestFullChange.new_organizer}}</td>
</tr>
<tr>
<td>Type:</td>
<td>&nbsp;{{latestFullChange.new_categories}}</td>
</tr>
<tr>
<td>Is deleted:</td>
<td>&nbsp;{{isDeleted}}</td>
</tr>
</table>
</div><br>
<div class="event-changes">
<table class="changes-table">
<tr>
<th>Changes</th>
</tr>
<tbody *ngFor="let change of changeDetails">
<tr>
<td class="change-timestamp">{{change.timestamp}}</td>
<td class="change-summary">{{change.summary}}</td>
</tr>
<tr *ngIf="change.hasDetails">
<td class="change-old-version">{{change.oldVersion}}</td>
<td>--></td>
<td class="change-new-version">{{change.newVersion}}</td>
</tr>
</tbody>
</table>
</div>
<footer class="fixed-bottom start-50 translate-middle">
Copyright &#169; 2021 - Patrick Müller
</footer>

View File

@ -1,4 +1,8 @@
import {Component, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {ActivatedRoute} from '@angular/router';
import {Event} from '../../models/event';
import {ApiService} from '../../services/api/api.service';
import {Change} from '../../models/change';
@Component({ @Component({
selector: 'app-event-detail', selector: 'app-event-detail',
@ -6,11 +10,124 @@ import {Component, OnInit} from '@angular/core';
styleUrls: ['./event-detail.component.css'] styleUrls: ['./event-detail.component.css']
}) })
export class EventDetailComponent implements OnInit { export class EventDetailComponent implements OnInit {
id: string = '';
event: Event = {} as Event;
latestFullChange: Change = {} as Change;
isDeleted: boolean = false;
changeDetails: ChangeDetail[] = [];
constructor() { constructor(
private route: ActivatedRoute,
private apiService: ApiService
) {
} }
ngOnInit(): void { ngOnInit(): void {
this.id = this.route.snapshot.paramMap.get('id') as string;
console.log(this.id);
this.apiService.getEventById(this.id).subscribe(event => {
this.event = event;
// Set latest full change
this.latestFullChange = this.event.changes[this.event.changes.length - 1];
if (this.latestFullChange.is_deleted) {
this.isDeleted = true;
this.latestFullChange = this.event.changes[this.event.changes.length - 2];
}
this.generateChangeDetails();
});
} }
generateChangeDetails() {
let changes = this.event.changes;
if (changes.length < 1) {
return;
}
let firstChange = changes.shift();
this.changeDetails.push({
timestamp: firstChange!.change_timestamp,
summary: 'Created',
hasDetails: false
});
let lastChange = firstChange!;
for (let change of changes) {
let cTs = change.change_timestamp;
let cSummary = '';
let cOld = '';
let cNew = '';
let cDetails = false;
let appendChange = true;
if (change.is_deleted) {
cSummary = 'Deleted';
} else if (change.new_summary !== lastChange.new_summary) {
cSummary = 'Name changed';
cOld = lastChange.new_summary;
cNew = change.new_summary;
cDetails = true;
} else if (change.new_description !== lastChange.new_description) {
cSummary = 'Description changed';
cOld = lastChange.new_description;
cNew = change.new_description;
cDetails = true;
} else if (change.new_start !== lastChange.new_start) {
cSummary = 'Start changed';
cOld = lastChange.new_start.toString();
cNew = change.new_start.toString();
cDetails = true;
} else if (change.new_end !== lastChange.new_end) {
cSummary = 'End changed';
cOld = lastChange.new_end.toString();
cNew = change.new_end.toString();
cDetails = true;
} else if (change.new_location !== lastChange.new_location) {
cSummary = 'Location changed';
cOld = lastChange.new_location;
cNew = change.new_location;
cDetails = true;
} else if (change.new_organizer !== lastChange.new_organizer) {
cSummary = 'Organizer changed';
cOld = lastChange.new_organizer;
cNew = change.new_organizer;
cDetails = true;
} else if (change.new_categories !== lastChange.new_categories) {
cSummary = 'Type changed';
cOld = lastChange.new_categories;
cNew = change.new_categories;
cDetails = true;
} else {
appendChange = false;
}
if(appendChange) {
this.changeDetails.push({
timestamp: cTs,
summary: cSummary,
oldVersion: cOld,
newVersion: cNew,
hasDetails: cDetails
});
}
lastChange = change;
}
}
closeTab(): void {
window.close();
}
}
export interface ChangeDetail {
timestamp: Date;
summary: string;
hasDetails: boolean;
oldVersion?: string;
newVersion?: string;
} }

View File

@ -9,7 +9,7 @@
} }
.event-card-exam { .event-card-exam {
background-color: orangered; background-color: darkred;
} }
.event-card-blocker { .event-card-blocker {

View File

@ -1,4 +1,4 @@
<div class="event-card {{eventTypeClass}}"> <div class="event-card {{eventTypeClass}}" (click)="openEventDetails()">
Titel: {{event.latest_event_summary}} Titel: {{event.latest_event_summary}}
Start: {{event.latest_start_date}} Start: {{event.latest_start_date}}
Gelöscht: {{event.changes[event.changes.length-1].is_deleted}} Gelöscht: {{event.changes[event.changes.length-1].is_deleted}}

View File

@ -1,5 +1,7 @@
import {Component, Input, OnInit} from '@angular/core'; import {Component, Input, OnInit} from '@angular/core';
import {Event} from '../../models/event'; import {Event} from '../../models/event';
import {Change} from '../../models/change';
import {Router} from '@angular/router';
@Component({ @Component({
selector: 'app-event', selector: 'app-event',
@ -10,16 +12,20 @@ export class EventComponent implements OnInit {
@Input() event: Event = {} as Event; @Input() event: Event = {} as Event;
eventTypeClass: string = ''; eventTypeClass: string = '';
constructor() { latestFullChange: Change = {} as Change;
constructor(
private router: Router
) {
} }
ngOnInit(): void { ngOnInit(): void {
let latestFullChange = this.event.changes[this.event.changes.length - 1]; this.latestFullChange = this.event.changes[this.event.changes.length - 1];
if(latestFullChange.is_deleted) { if (this.latestFullChange.is_deleted) {
latestFullChange = this.event.changes[this.event.changes.length - 2]; this.latestFullChange = this.event.changes[this.event.changes.length - 2];
} }
switch(latestFullChange.new_categories) { switch (this.latestFullChange.new_categories) {
case 'Prüfung': case 'Prüfung':
this.eventTypeClass = 'event-card-exam'; this.eventTypeClass = 'event-card-exam';
break; break;
@ -34,4 +40,7 @@ export class EventComponent implements OnInit {
} }
} }
openEventDetails() {
this.router.navigate([]).then(result => { window.open('/eventDetails/'+this.event.event_uid, '_blank'); });
}
} }

View File

@ -33,4 +33,13 @@ export class ApiService {
} }
return new Observable<Event[]>(); return new Observable<Event[]>();
} }
getEventById(id: string): Observable<Event> {
try {
return this.http.get<Event>((this.apiUrl + '/' + id));
} catch (exception) {
console.log('Error fetching events from API');
}
return new Observable<Event>();
}
} }