Add ability to move events between calendars
Some checks failed
Jenkins Production Deployment

This commit is contained in:
Patrick Müller 2024-06-04 16:34:49 +02:00
parent 42021b50bc
commit a0f6d85e1b
Signed by: Paddy
GPG Key ID: D10B5E2CFD8E7C6D
9 changed files with 10598 additions and 6230 deletions

16685
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "nachklang-calendar", "name": "nachklang-calendar",
"version": "0.1.0", "version": "1.1.0",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "ng serve", "start": "ng serve",
@ -10,29 +10,31 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "^15.0.4", "@angular/animations": "^18.0.1",
"@angular/common": "^15.0.4", "@angular/common": "^18.0.1",
"@angular/compiler": "^15.0.4", "@angular/compiler": "^18.0.1",
"@angular/core": "^15.0.4", "@angular/core": "^18.0.1",
"@angular/forms": "^15.0.4", "@angular/forms": "^18.0.1",
"@angular/platform-browser": "^15.0.4", "@angular/material": "^18.0.1",
"@angular/platform-browser-dynamic": "^15.0.4", "@angular/platform-browser": "^18.0.1",
"@angular/router": "^15.0.4", "@angular/platform-browser-dynamic": "^18.0.1",
"rxjs": "~7.5.0", "@angular/router": "^18.0.1",
"tslib": "^2.3.0", "@material/dialog": "^15.0.0-canary.7f224ddd4.0",
"zone.js": "~0.11.4" "rxjs": "~7.8.1",
"tslib": "^2.6.2",
"zone.js": "~0.14.6"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^15.0.4", "@angular-devkit/build-angular": "^18.0.2",
"@angular/cli": "~15.0.4", "@angular/cli": "~18.0.2",
"@angular/compiler-cli": "^15.0.4", "@angular/compiler-cli": "^18.0.1",
"@types/jasmine": "~4.0.0", "@types/jasmine": "~5.1.4",
"jasmine-core": "~4.3.0", "jasmine-core": "~5.1.2",
"karma": "~6.4.0", "karma": "~6.4.3",
"karma-chrome-launcher": "~3.1.0", "karma-chrome-launcher": "~3.2.0",
"karma-coverage": "~2.2.0", "karma-coverage": "~2.2.1",
"karma-jasmine": "~5.1.0", "karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.0.0", "karma-jasmine-html-reporter": "~2.1.0",
"typescript": "~4.8.2" "typescript": "~5.4.5"
} }
} }

View File

@ -12,6 +12,11 @@ import {ApiService} from './services/api.service';
import {HttpClientModule} from '@angular/common/http'; import {HttpClientModule} from '@angular/common/http';
import {FormsModule} from '@angular/forms'; import {FormsModule} from '@angular/forms';
import {EventsTableComponent} from './components/events-table/events-table.component'; import {EventsTableComponent} from './components/events-table/events-table.component';
import { EventMovePopupComponent } from './components/event-move-popup/event-move-popup.component';
import {MAT_DIALOG_DEFAULT_OPTIONS, MatDialogModule} from "@angular/material/dialog";
import {MatFormField} from "@angular/material/form-field";
import {MatInput} from "@angular/material/input";
import {MatButton} from "@angular/material/button";
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -21,15 +26,23 @@ import {EventsTableComponent} from './components/events-table/events-table.compo
AdminComponent, AdminComponent,
LandingpageComponent, LandingpageComponent,
NotfoundComponent, NotfoundComponent,
EventsTableComponent EventsTableComponent,
EventMovePopupComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
HttpClientModule, HttpClientModule,
AppRouting, AppRouting,
FormsModule FormsModule,
MatDialogModule,
MatFormField,
MatInput,
MatButton
],
providers: [
ApiService,
{provide: MAT_DIALOG_DEFAULT_OPTIONS, useValue: {hasBackdrop: false}}
], ],
providers: [ApiService],
bootstrap: [AppComponent] bootstrap: [AppComponent]
}) })
export class AppModule { export class AppModule {

View File

@ -41,6 +41,9 @@
<p class="has-error" *ngIf="showCreateError">Error creating event. Please try again.</p> <p class="has-error" *ngIf="showCreateError">Error creating event. Please try again.</p>
<p class="has-error" *ngIf="invalidUrlError">Invalid URL, please only provide valid ones!</p> <p class="has-error" *ngIf="invalidUrlError">Invalid URL, please only provide valid ones!</p>
</td> </td>
<td>
<button (click)="triggerMove()" *ngIf="event!.eventId !== undefined">Move</button>
</td>
<td> <td>
<button (click)="triggerDelete()" *ngIf="event!.eventId !== undefined">Delete</button> <button (click)="triggerDelete()" *ngIf="event!.eventId !== undefined">Delete</button>
</td> </td>
@ -81,6 +84,9 @@
<td> <td>
<button (click)="toggleEdit()">Edit</button> <button (click)="toggleEdit()">Edit</button>
</td> </td>
<td>
<button (click)="triggerMove()">Move</button>
</td>
<td> <td>
<button (click)="triggerDelete()">Delete</button> <button (click)="triggerDelete()">Delete</button>
</td> </td>

View File

@ -1,6 +1,8 @@
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {MatDialog, MatDialogRef} from '@angular/material/dialog';
import {Event} from '../../models/event'; import {Event} from '../../models/event';
import {ApiService} from '../../services/api.service'; import {ApiService} from '../../services/api.service';
import {EventMovePopupComponent} from "../event-move-popup/event-move-popup.component";
@Component({ @Component({
selector: '[app-event]', selector: '[app-event]',
@ -22,7 +24,8 @@ export class EventComponent implements OnInit {
@Output() deleteEvent = new EventEmitter<number>(); @Output() deleteEvent = new EventEmitter<number>();
constructor( constructor(
private api: ApiService private api: ApiService,
public dialog: MatDialog
) { ) {
} }
@ -163,6 +166,31 @@ export class EventComponent implements OnInit {
} }
} }
triggerMove() {
if(this.editActive) {
window.alert('Please save your changes before moving the event to a different calendar.');
return;
}
let movePopup = this.dialog.open(EventMovePopupComponent, {
data: {
event: this.event
}
});
movePopup.afterClosed().subscribe(result => {
// If popup is dismissed, undefined will be returned
if(result) {
this.api.moveEvent(result).subscribe((res: any) => {
console.log(res);
// Uses the same interface as delete as from the calendar table perspective it is the same action
// as a delete
this.deleteEvent.next(result.eventId);
});
}
});
}
checkEventValidUrl(): boolean { checkEventValidUrl(): boolean {
if(this.isNullOrBlank(this.event!.url)) { if(this.isNullOrBlank(this.event!.url)) {
return true; return true;

View File

@ -11,6 +11,7 @@
<th>URL</th> <th>URL</th>
<th>Status</th> <th>Status</th>
<th>Edit</th> <th>Edit</th>
<th>Move</th>
<th>Delete</th> <th>Delete</th>
</tr> </tr>
<tr app-event *ngFor="let event of events" [event]="event" [editActive]="event.eventId === undefined" (deleteEvent)="deleteEvent($event)"></tr> <tr app-event *ngFor="let event of events" [event]="event" [editActive]="event.eventId === undefined" (deleteEvent)="deleteEvent($event)"></tr>

View File

@ -1,6 +1,6 @@
<div *ngIf="!isLoggedIn" class="form-container"> <div *ngIf="!isLoggedIn" class="form-container">
<p>Please log in:</p> <p>Please log in:</p>
<label for="email">Your @nachklang.art email: </label> <label for="email">Your &#64;nachklang.art email: </label>
<input id="email" type="text" aria-label="Your Email" [(ngModel)]="email"><br> <input id="email" type="text" aria-label="Your Email" [(ngModel)]="email"><br>
<label for="password">Password: </label> <label for="password">Password: </label>
<input id="password" type="password" aria-label="Password" (keyup.enter)="login()" [(ngModel)]="password"><br> <input id="password" type="password" aria-label="Password" (keyup.enter)="login()" [(ngModel)]="password"><br>
@ -9,7 +9,7 @@
<p>If you dont' have an account yet, please use the following form to register:</p> <p>If you dont' have an account yet, please use the following form to register:</p>
<label for="name">Your full name: </label> <label for="name">Your full name: </label>
<input id="name" type="text" aria-label="Your Name" [(ngModel)]="name"><br> <input id="name" type="text" aria-label="Your Name" [(ngModel)]="name"><br>
<label for="registerEmail">Your @nachklang.art email: </label> <label for="registerEmail">Your &#64;nachklang.art email: </label>
<input id="registerEmail" type="text" aria-label="Your Email" [(ngModel)]="registerEmail"><br> <input id="registerEmail" type="text" aria-label="Your Email" [(ngModel)]="registerEmail"><br>
<label for="registerPassword">Password: </label> <label for="registerPassword">Password: </label>
<input id="registerPassword" type="password" aria-label="Password" [(ngModel)]="registerPassword"><br> <input id="registerPassword" type="password" aria-label="Password" [(ngModel)]="registerPassword"><br>

View File

@ -127,4 +127,21 @@ export class ApiService {
} }
return new Observable<any>(); return new Observable<any>();
} }
moveEvent(event: Event): Observable<any> {
try {
let session = UtilsService.getSessionInfoFromLocalStorage();
let params = new HttpParams();
params = params.append('sessionId', session.sessionId);
params = params.append('sessionKey', session.sessionKey);
let updateEvent: any = event;
return this.http.put(this.apiUrl + 'move/' + updateEvent.eventId, updateEvent, {params});
} catch (exception) {
console.log('Error updating event');
}
return new Observable<any>();
}
} }

View File

@ -1 +1,21 @@
/* You can add global styles to this file, and also import other style files */ /* You can add global styles to this file, and also import other style files */
html, body {
height: 100%;
display:flex;
margin:0;
flex-direction:column;
font-family: sans-serif;
}
app-root {
z-index: 100;
position: fixed;
width: 100%;
box-sizing: border-box;
padding: 20px;
}
div.cdk-overlay-container {
z-index: 1000;
margin: auto;
}