From 558bdec1b365df050a2546ab6b13c37e626b4416 Mon Sep 17 00:00:00 2001 From: Felix Engelmann Date: Mon, 10 Feb 2025 21:59:22 +0100 Subject: [PATCH 01/18] Fix leveled grade distribution color bug --- .../leveled-grade-distribution.component.html | 2 +- server/.idea/runConfigurations/pytest_all.xml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.html b/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.html index 6335ffe1..be567ded 100644 --- a/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.html +++ b/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.html @@ -62,7 +62,7 @@ + -
+
+ + +
+
+ + +
-
-

{{ t("gradeBracketsLabel") }}

+ +
+ +

{{ t("gradeBracketsLabel") }} - {{ t('stackedChartBrackets') }}

- {{ t("gradeBracketsDescription", { min: 2, max: 8 }) }} + {{ t("gradeBracketsDescriptionStackedChart", {min: 2, max: 8}) }}
+
+
+ + +
+
+ + + +
+ + +
+ +
+ +

{{ t("gradeBracketsLabel") }} - {{ t('barChartBrackets') }}

+
+ {{ t("gradeBracketsDescriptionBarChart", {min: 2, max: 12}) }} +
+
@@ -169,65 +252,96 @@

{{ t("gradeBracketsLabel") }}

- - -
- + + + + + + +
-
-
- - - -
+ + + + + + + + diff --git a/client/src/app/modules/scale/scale-form/scale-form.component.ts b/client/src/app/modules/scale/scale-form/scale-form.component.ts index 6769049e..96d0d7aa 100644 --- a/client/src/app/modules/scale/scale-form/scale-form.component.ts +++ b/client/src/app/modules/scale/scale-form/scale-form.component.ts @@ -8,11 +8,14 @@ import { } from '@jsverse/transloco'; import { CardModule } from 'primeng/card'; import { + AbstractControl, FormArray, FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, + ValidationErrors, + ValidatorFn, Validators, } from '@angular/forms'; import { FormDirective } from '../../shared/forms/form.directive'; @@ -114,33 +117,71 @@ export class ScaleFormComponent implements OnInit { } buildForm() { + const allNamesFilled: ValidatorFn = ( + control: AbstractControl, + ): ValidationErrors | null => { + const formArray = control as FormArray; + return formArray.value.every( + (item: any) => item.name && item.name.trim() !== '', + ) + ? null + : { names_not_filled: true }; + }; + + const notUniqueValidator: ValidatorFn = ( + ctl: AbstractControl, + ): ValidationErrors | null => { + const formArray = ctl as FormArray; + return formArray.value.length === + new Set(formArray.value.map((v: any) => v.value)).size + ? null + : { not_unique: true }; + }; + + const semanticBracketErrorValidator: ValidatorFn = ( + ctl: AbstractControl, + ): ValidationErrors | null => { + const formArray = ctl as FormArray; + return formArray.value.length >= 2 && + formArray.value.reduce( + (p: boolean, c: any) => p && c.value > 0, + true, + ) && + formArray.value.at(-2).value + 1 === formArray.value.at(-1).value + ? null + : { semantic_error: true }; + }; + + const invalidLengthValidator = (min: number, max: number): ValidatorFn => { + return (ctl: AbstractControl): ValidationErrors | null => { + const formArray = ctl as FormArray; + return formArray.value.length >= min && formArray.value.length <= max + ? null + : { invalid_length: true }; + }; + }; + this.scaleForm = this.fb.group({ lineType: this.editMode ? undefined : [LineType.BOULDER, [Validators.required]], name: this.editMode ? undefined : ['', Validators.required], - grades: this.fb.array( + grades: this.fb.array([], [notUniqueValidator, allNamesFilled]), + stackedChartBrackets: this.fb.array( [], [ - (ctl) => - ctl.value.length === new Set(ctl.value.map((v) => v.value)).size - ? null - : { not_unique: true }, + notUniqueValidator, + semanticBracketErrorValidator, + invalidLengthValidator(2, 8), ], ), - gradeBrackets: this.fb.array( + barChartBrackets: this.fb.array( [], [ - (ctl) => - ctl.value.length >= 2 && - ctl.value.reduce((p, c) => p && c.value > 0, true) && - ctl.value.at(-2).value + 1 === ctl.value.at(-1).value - ? null - : { semantic_error: true }, - (ctl) => - ctl.value.length >= 2 && ctl.value.length <= 8 - ? null - : { invalid_length: true }, + notUniqueValidator, + semanticBracketErrorValidator, + invalidLengthValidator(2, 8), + allNamesFilled, ], ), }); @@ -156,13 +197,21 @@ export class ScaleFormComponent implements OnInit { }), ) .forEach((ctl) => this.gradeControls().push(ctl)); - this.scale.gradeBrackets + this.scale.gradeBrackets.stackedChartBrackets .map((value) => this.fb.group({ value: [value], }), ) - .forEach((ctl) => this.gradeBracketsControls().push(ctl)); + .forEach((ctl) => this.stackedChartBracketsControls().push(ctl)); + this.scale.gradeBrackets.barChartBrackets + .map((bracket) => + this.fb.group({ + value: [bracket.value], + name: [bracket.name], + }), + ) + .forEach((ctl) => this.barChartBracketsControls().push(ctl)); } else { this.gradeControls().push( this.fb.group({ name: marker('CLOSED_PROJECT'), value: -2 }), @@ -173,8 +222,14 @@ export class ScaleFormComponent implements OnInit { this.gradeControls().push( this.fb.group({ name: marker('UNGRADED'), value: 0 }), ); - this.gradeBracketsControls().push(this.fb.group({ value: 1 })); - this.gradeBracketsControls().push(this.fb.group({ value: 2 })); + this.stackedChartBracketsControls().push(this.fb.group({ value: 1 })); + this.stackedChartBracketsControls().push(this.fb.group({ value: 2 })); + this.barChartBracketsControls().push( + this.fb.group({ value: 1, name: marker('FIRST_BAR_CHART_BRACKET') }), + ); + this.barChartBracketsControls().push( + this.fb.group({ value: 2, name: marker('SECOND_BAR_CHART_BRACKET') }), + ); this.addGrade(); } this.scaleForm.enable(); @@ -184,23 +239,45 @@ export class ScaleFormComponent implements OnInit { return this.scaleForm.controls.grades as FormArray; } - gradeBracketsControls() { - return this.scaleForm.controls.gradeBrackets as FormArray; + stackedChartBracketsControls() { + return this.scaleForm.controls.stackedChartBrackets as FormArray; + } + + barChartBracketsControls() { + return this.scaleForm.controls.barChartBrackets as FormArray; } - reorderByValue() { - const data = this.gradeControls().value; + private reorderControlsByValue( + controls: FormArray, + includeName: boolean = true, + ) { + const data = controls.value; data.sort((a, b) => a.value - b.value); - this.gradeControls().clear(); + controls.clear(); data .filter((g) => Number.isInteger(g.value)) - .map((g) => - this.fb.group({ - name: [g.name], + .map((g) => { + const controls = { value: [g.value], - }), - ) - .forEach((ctl) => this.gradeControls().push(ctl)); + }; + if (includeName) { + controls['name'] = [g.name]; + } + return this.fb.group(controls); + }) + .forEach((ctl) => controls.push(ctl)); + } + + reorderGradesByValue() { + this.reorderControlsByValue(this.gradeControls()); + } + + reorderBarChartBracketsByValue() { + this.reorderControlsByValue(this.barChartBracketsControls()); + } + + reorderStackedChartBracketsByValue() { + this.reorderControlsByValue(this.stackedChartBracketsControls(), false); } addGrade() { @@ -215,42 +292,68 @@ export class ScaleFormComponent implements OnInit { this.gradeControls().removeAt(index); } - addBracket() { - const max = this.gradeBracketsControls().value.reduce( + private addBracket(controls: FormArray, includeName: boolean = true) { + const max = controls.value.reduce( (acc, v) => (v.value > acc ? v.value : acc), 0, ); - this.gradeBracketsControls().push( - this.fb.group({ name: [], value: [max + 1] }), - ); + const group = { + value: [max + 1], + }; + if (includeName) { + group['name'] = []; + } + controls.push(this.fb.group(group)); + } + + addStackedChartBracket() { + this.addBracket(this.stackedChartBracketsControls(), false); } - deleteBracket(index: number) { - this.gradeBracketsControls().removeAt(index); + addBarChartBracket() { + this.addBracket(this.barChartBracketsControls()); + } + + deleteStackedChartBracket(index: number) { + this.stackedChartBracketsControls().removeAt(index); + } + + deleteBarChartBracket(index: number) { + this.barChartBracketsControls().removeAt(index); } saveScale() { if (this.scaleForm.valid) { this.loadingState = LoadingState.LOADING; + this.scaleForm.disable(); if (this.editMode) { this.scale.grades = this.gradeControls().value; - this.scale.gradeBrackets = this.gradeBracketsControls().value.map( - (gb) => gb.value, - ); - + this.scale.gradeBrackets = { + stackedChartBrackets: this.stackedChartBracketsControls().value.map( + (gb) => gb.value, + ), + barChartBrackets: this.barChartBracketsControls().value.map((gb) => { + return { + value: gb.value, + name: gb.name, + }; + }), + }; this.scalesService.updateScale(this.scale).subscribe({ next: () => { this.store.dispatch( toastNotification(NotificationIdentifier.SCALE_UPDATED), ); this.loadingState = LoadingState.DEFAULT; + this.scaleForm.enable(); }, error: () => { this.store.dispatch( toastNotification(NotificationIdentifier.SCALE_UPDATED_ERROR), ); this.loadingState = LoadingState.DEFAULT; + this.scaleForm.enable(); }, }); } else { @@ -258,7 +361,7 @@ export class ScaleFormComponent implements OnInit { scale.lineType = this.scaleForm.get('lineType').value.value; scale.name = this.scaleForm.get('name').value; scale.grades = this.gradeControls().value; - scale.gradeBrackets = this.gradeBracketsControls().value.map( + scale.gradeBrackets = this.stackedChartBracketsControls().value.map( (gb) => gb.value, ); this.scalesService.createScale(scale).subscribe({ @@ -267,6 +370,7 @@ export class ScaleFormComponent implements OnInit { toastNotification(NotificationIdentifier.SCALE_CREATED), ); this.loadingState = LoadingState.DEFAULT; + this.scaleForm.enable(); this.router.navigate(['/scales']); }, error: () => { @@ -274,6 +378,7 @@ export class ScaleFormComponent implements OnInit { toastNotification(NotificationIdentifier.SCALE_CREATED_ERROR), ); this.loadingState = LoadingState.DEFAULT; + this.scaleForm.enable(); }, }); } diff --git a/client/src/app/modules/shared/components/grade-distribution-bar-chart/grade-distribution-bar-chart.component.ts b/client/src/app/modules/shared/components/grade-distribution-bar-chart/grade-distribution-bar-chart.component.ts index d6a7532d..78beb036 100644 --- a/client/src/app/modules/shared/components/grade-distribution-bar-chart/grade-distribution-bar-chart.component.ts +++ b/client/src/app/modules/shared/components/grade-distribution-bar-chart/grade-distribution-bar-chart.component.ts @@ -158,9 +158,22 @@ export class GradeDistributionBarChartComponent implements OnChanges, OnInit { ]).pipe( map(([barChartColor, scale]) => { // Condensed scale is needed if screen is too small to host all grades - let gradesInUsedScale = scale.grades.sort( + const usedScale = condensed + ? scale.gradeBrackets.barChartBrackets + : scale.grades; + + // Because of the way the condensed scale is built, we need to replace the last grade value + // with the maximum grade value of the full scale (if not condensed, this has no effect) + usedScale[usedScale.length - 1].value = Math.max( + ...scale.grades.map((grade) => grade.value), + ); + + // Sort grades in ascending order + let gradesInUsedScale = usedScale.sort( (a, b) => a.value - b.value, ); + + // Filter out projects gradesInUsedScale = gradesInUsedScale.filter( (grade) => grade.value > 0, ); @@ -184,6 +197,24 @@ export class GradeDistributionBarChartComponent implements OnChanges, OnInit { } }); + // Apply condensedSortingMap to gradeDistribution + const mappedGradeDistribution = {}; + for (const gradeValue in this.gradeDistribution[lineType][ + gradeScale + ]) { + if (Number(gradeValue) <= 0) { + continue; + } + const condensedGradeValue = condensedSortingMap[gradeValue]; + if (condensedGradeValue) { + if (!mappedGradeDistribution[condensedGradeValue]) { + mappedGradeDistribution[condensedGradeValue] = 0; + } + mappedGradeDistribution[condensedGradeValue] += + this.gradeDistribution[lineType][gradeScale][gradeValue]; + } + } + // Build chart data const labels = gradesInUsedScale.map((grade) => grade.value > 0 @@ -191,8 +222,7 @@ export class GradeDistributionBarChartComponent implements OnChanges, OnInit { : this.translocoService.translate(grade.name), ); const counts = gradeValues.map( - (gradeValue) => - this.gradeDistribution[lineType][gradeScale][gradeValue] ?? 0, + (gradeValue) => mappedGradeDistribution[gradeValue] ?? 0, ); const maxCount = Math.max(...counts); const backgroundColors = counts.map((count) => { diff --git a/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.ts b/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.ts index cbe9b27e..5f669130 100644 --- a/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.ts +++ b/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.ts @@ -61,7 +61,9 @@ export class LeveledGradeDistributionComponent implements OnInit { ), ]).pipe( map(([scale, gradeNameByValueMap]) => { - const labels = Array(scale.gradeBrackets.length).fill(''); + const labels = Array( + scale.gradeBrackets.stackedChartBrackets.length, + ).fill(''); const nextGradeName = Object.fromEntries( scale.grades @@ -73,16 +75,23 @@ export class LeveledGradeDistributionComponent implements OnInit { }), ); - for (let i = 0; i < scale.gradeBrackets.length; i++) { + for ( + let i = 0; + i < scale.gradeBrackets.stackedChartBrackets.length; + i++ + ) { if (i == 0) { labels[i] = - `${this.translocoService.translate(marker('leveledGradeDistributionUntil'))} ${gradeNameByValueMap[scale.gradeBrackets[i]]}`; - } else if (i == scale.gradeBrackets.length - 1) { + `${this.translocoService.translate(marker('leveledGradeDistributionUntil'))} ${gradeNameByValueMap[scale.gradeBrackets.stackedChartBrackets[i]]}`; + } else if ( + i == + scale.gradeBrackets.stackedChartBrackets.length - 1 + ) { labels[i] = - `${this.translocoService.translate(marker('leveledGradeDistributionFrom'))} ${gradeNameByValueMap[scale.gradeBrackets[i]]}`; + `${this.translocoService.translate(marker('leveledGradeDistributionFrom'))} ${gradeNameByValueMap[scale.gradeBrackets.stackedChartBrackets[i]]}`; } else { labels[i] = - `${nextGradeName[scale.gradeBrackets[i - 1]]} - ${gradeNameByValueMap[scale.gradeBrackets[i]]}`; + `${nextGradeName[scale.gradeBrackets.stackedChartBrackets[i - 1]]} - ${gradeNameByValueMap[scale.gradeBrackets.stackedChartBrackets[i]]}`; } } @@ -90,7 +99,9 @@ export class LeveledGradeDistributionComponent implements OnInit { lineType: lineType as LineType, gradeScale, projects: 0, - brackets: Array(scale.gradeBrackets.length).fill(0), + brackets: Array( + scale.gradeBrackets.stackedChartBrackets.length, + ).fill(0), bracketLabels: labels, total: 0, }; @@ -103,9 +114,16 @@ export class LeveledGradeDistributionComponent implements OnInit { if (gradeValue <= 0) { data.projects += count; } else { - for (let i = 0; i < scale.gradeBrackets.length; i++) { - const bracket = scale.gradeBrackets[i]; - if (i == scale.gradeBrackets.length - 1) { + for ( + let i = 0; + i < scale.gradeBrackets.stackedChartBrackets.length; + i++ + ) { + const bracket = scale.gradeBrackets.stackedChartBrackets[i]; + if ( + i == + scale.gradeBrackets.stackedChartBrackets.length - 1 + ) { data.brackets[i] += count; } else if (gradeValue <= bracket) { data.brackets[i] += count; @@ -126,6 +144,7 @@ export class LeveledGradeDistributionComponent implements OnInit { } else { forkJoin(observables).subscribe(() => { this.stackChartData = stackChartData.sort((a, b) => a.total - b.total); + console.log(this.stackChartData); this.gradeDistributionEmpty = false; }); } diff --git a/client/src/assets/i18n/area/de.json b/client/src/assets/i18n/area/de.json index 82eb4f05..6ff60779 100644 --- a/client/src/assets/i18n/area/de.json +++ b/client/src/assets/i18n/area/de.json @@ -38,4 +38,4 @@ "areaForm.lngLabel": "Längengrad", "areaForm.lngPlaceholder": "z.B. 7.26605", "areaForm.invalidLng": "Ungültiger Längengrad" -} +} \ No newline at end of file diff --git a/client/src/assets/i18n/crag/de.json b/client/src/assets/i18n/crag/de.json index e15f67cb..8901075c 100644 --- a/client/src/assets/i18n/crag/de.json +++ b/client/src/assets/i18n/crag/de.json @@ -39,4 +39,4 @@ "cragForm.invalidLng": "Ungültiger Längengrad", "crag.cragRules": "Gebietsregeln", "cragList.cragListTitle": "Gebiete" -} +} \ No newline at end of file diff --git a/client/src/assets/i18n/de.json b/client/src/assets/i18n/de.json index cb3a5808..f4ded6d6 100644 --- a/client/src/assets/i18n/de.json +++ b/client/src/assets/i18n/de.json @@ -12,8 +12,8 @@ "users.list.noUsersFoundEmptyMessage": "Keine Benutzer gefunden.", "users.list.userListTitle": "Benutzer", "user.charts.gradeDistribution": "Gradverteilung", - "user.charts.lineType": "Linienart", "user.charts.completion": "Fortschritt", + "user.charts.lineType": "Linienart", "user.charts.noLinesInThisGradeRange": "Keine Linien in diesem Schwierigkeitsbereich.", "todos.priorityButton.mediumPriority": "Normale Priorität", "todos.priorityButton.lowPriority": "Niedrige Priorität", @@ -65,17 +65,22 @@ "scale.scaleForm.TRAD": "Traditionell", "scale.scaleForm.nameInputLabel": "Skalenname", "scale.scaleForm.required": "Pflichtfeld", - "scale.scaleForm.gradesErrorMsg": "Schwierigkeitswerte dürfen nicht mehrfach auftreten", "scale.scaleForm.gradeNameLabel": "Schwierigkeit (angezeigt)", "scale.scaleForm.gradeValueLabel": "Schwierigkeitswert (numerisch, intern)", + "scale.scaleForm.valuesNotUnique": "Die Schwierigkeitswerte müssen eindeutig sein.", + "scale.scaleForm.namesNotFilled": "Alle Namen müssen ausgefüllt sein.", + "scale.scaleForm.addGrade": "Schwierigkeit hinzufügen", + "scale.scaleForm.reorderGrades": "Felder nach Wert sortieren", "scale.scaleForm.gradeBracketsLabel": "Schwierigkeitsabschnitte", - "scale.scaleForm.gradeBracketsDescription": "Die Übersicht der Abschnitte wird wie folgt gebildet. Angenommen, die Abschnitte sind [5, 10, 15, 16]. Dann werden im ersten Abschnitt alle Grade mit Wert bis 5 landen, im nächsten alle zwischen 6 und 10, dann 11 bis 15 und zuletzt alles ab 16. Es ist erforderlich, dass die letzten beiden Werte aufeinanderfolgen und die Abschnitte sortiert sind. Projekte werden immer getrennt gruppiert. Es dürfen maximal {{max}} Abschnitte gesetzt werden.", + "scale.scaleForm.stackedChartBrackets": "Gestapeltes Balkendiagramm", + "scale.scaleForm.gradeBracketsDescriptionStackedChart": "Hier werden die Grad-Abschnitte für gestapelte Balkendiagramme definiert. Die Übersicht der Abschnitte wird wie folgt gebildet. Angenommen, die Abschnitte sind [5, 10, 15, 16]. Dann werden im ersten Abschnitt alle Grade mit Wert bis 5 landen, im nächsten alle zwischen 6 und 10, dann 11 bis 15 und zuletzt alles ab 16. Es ist erforderlich, dass die letzten beiden Werte aufeinanderfolgen und die Abschnitte sortiert sind. Projekte werden immer getrennt gruppiert. Es dürfen maximal {{max}} Abschnitte gesetzt werden.", + "scale.scaleForm.gradeBracketsInputLabel": "Abschnitt", "scale.scaleForm.gradeBracketsErrorMsg": "Vorgaben zu Abschnitten nicht erfüllt", "scale.scaleForm.gradeBracketsInvalidLength": "Es sind müssen mindestens {{min}} und maximal {{max}} Abschnitte konfiguriert werden.", - "scale.scaleForm.gradeBracketsInputLabel": "Abschnitt", - "scale.scaleForm.addGrade": "Schwierigkeit hinzufügen", "scale.scaleForm.addBracket": "Abschnitt hinzufügen", - "scale.scaleForm.reorderGrades": "Felder nach Wert sortieren", + "scale.scaleForm.barChartBrackets": "Balkendiagramm", + "scale.scaleForm.gradeBracketsDescriptionBarChart": "Hier werden die Grad-Abschnitte für Balkendiagramme definiert. Sie verhalten sich genau wie die Abschnitte der gestapelten Diagramme, haben aber zusätzloich eine frei wählbare Bezeichnung (z.B. \"> 8A\"). Hier dürfen maximal {{max}} Abschnitte gesetzt werden.", + "scale.scaleForm.barChartBracketNameLabel": "Name", "scale.scaleForm.saveScale": "Skala speichern", "scale.scaleForm.deleteScale": "Skala löschen", "scale.scaleForm.cancel": "Abbrechen", @@ -356,6 +361,8 @@ "clipboardSuccessToastDescription": "Der Text wurde erfolgreich in die Zwischenablage kopiert.", "clipboardErrorToastTitle": "Fehler", "clipboardErrorToastDescription": "Der Text konnte nicht in die Zwischenablage kopiert werden.", + "notifications.INSTANCE_SETTINGS_ERROR_MIGRATION_IMPOSSIBLE_TITLE": "Fehler beim Speichern", + "notifications.INSTANCE_SETTINGS_ERROR_MIGRATION_IMPOSSIBLE_MESSAGE": "Existierende Objekte verhindern eine Veränderung der Hierarchieebenen.", "notifications.MAP_MARKER_ADDED_TITLE": "Marker hinzugefügt", "notifications.MAP_MARKER_ADDED_MESSAGE": "Der Marker wurde erfolgreich hinzugefügt.", "notifications.MAP_MARKER_REMOVED_TITLE": "Marker entfernt", @@ -506,6 +513,7 @@ "user.charts": "Statistiken", "user.gallery": "Galerie", "ascents": "Begehungen", + "ALL": "Alle", "sortAscending": "Aufsteigend", "sortDescending": "Absteigend", "topoImage.askReallyWantToDeleteTopoImage": "Bist Du Dir sicher? Dies ist eine extrem destruktive Aktion und kann nicht rückgängig gemacht werden. Das Topo Bild und alle Linienverknüpfungen werden gelöscht. Die eigentlichen Linien samt Begehungen etc. bleiben aber bestehen.", @@ -518,7 +526,6 @@ "reorderLinePathsDialogItemsName": "Linien", "editTopoImageBrowserTitle": "Topo Bild bearbeiten", "addTopoImageBrowserTitle": "Topo Bild hinzufügen", - "ALL": "Alle", "orderByGrade": "Grad", "orderByTimeCreated": "Eintragungsdatum", "orderDescending": "absteigend", @@ -530,6 +537,9 @@ "allCrags": "Alle Gebiete", "allSectors": "Alle Sektoren", "allAreas": "Alle Bereiche", + "CLOSED_PROJECT": "Geschlossenes Projekt", + "OPEN_PROJECT": "Offenes Projekt", + "UNGRADED": "Nicht bewertet", "January": "Januar", "February": "Februar", "March": "März", @@ -542,12 +552,8 @@ "October": "Oktober", "November": "November", "December": "Dezember", - "CLOSED_PROJECT": "Geschlossenes Projekt", - "OPEN_PROJECT": "Offenes Projekt", - "UNGRADED": "Nicht bewertet", "leveledGradeDistributionUntil": "bis", "leveledGradeDistributionFrom": "ab", - "GENERIC_PROJECT": "Proj.", "copyCoordinatesToClipboard": "Koordinaten kopieren", "openCoordinatesInGoogleMaps": "In Google Maps öffnen", "reorderSectorsDialogTitle": "Sektoren ordnen", @@ -564,6 +570,8 @@ "sector.ranking": "Ranking", "sector.gallery": "Galerie", "sector.edit": "Bearbeiten", + "FIRST_BAR_CHART_BRACKET": "Erster Bereich", + "SECOND_BAR_CHART_BRACKET": "Zweiter Bereich", "scale.scaleForm.confirmDeleteMessage": "Skala wirklich unwiderruflich löschen?", "scale.scaleForm.acceptConfirmDelete": "Ja, unwiderruflich löschen", "region.infos": "Infos", @@ -722,10 +730,10 @@ "URL": "URL", "BOTTOM": "Footer", "TOP": "Header", + "scale.scaleForm.gradesErrorMsg": "Schwierigkeitswerte dürfen nicht mehrfach auftreten", + "GENERIC_PROJECT": "Proj.", "instanceSettings.instanceSettingsForm.menuSettings": "Menü Einstellungen", "instanceSettings.instanceSettingsForm.invalidHttpUrl": "Ungültige URL", - "notifications.INSTANCE_SETTINGS_ERROR_MIGRATION_IMPOSSIBLE_TITLE": "Fehler beim Speichern", - "notifications.INSTANCE_SETTINGS_ERROR_MIGRATION_IMPOSSIBLE_MESSAGE": "Existierende Objekte verhindern eine Veränderung der Hierarchieebenen.", "maps.crags": "Gebiete", "maps.sectors": "Sektoren", "maps.areas": "Bereiche", diff --git a/client/src/assets/i18n/line/de.json b/client/src/assets/i18n/line/de.json index 8e3b09a6..f7372ec0 100644 --- a/client/src/assets/i18n/line/de.json +++ b/client/src/assets/i18n/line/de.json @@ -1,6 +1,7 @@ { "lineList.newLineButtonLabel": "Neue Linie", "lineList.orderByLabel": "Sortieren nach:", + "lineList.lineType": "Linientyp", "lineList.hideArchive": "Archiv verstecken", "lineList.showArchive": "Archiv anzeigen", "lineList.noLinesFoundEmptyMessage": "Keine Linien gefunden", @@ -105,7 +106,6 @@ "lineBoolPropList.pockets": "Taschen", "lineBoolPropList.pinches": "Zangen", "lineForm.lineSecretLabel": "Geheim", - "lineList.lineType": "Linientyp", "lineList.ascentCount": "Begehungen", "lineList.videobeta": "Videobeta", "lineForm.lineVideoLabel": "Video", @@ -114,4 +114,4 @@ "lineBoolPropList.sitstart": "Sitzstart", "lineForm.lineSitstartLabel": "Sitzstart", "lineList.lineListTitle": "Linien" -} +} \ No newline at end of file diff --git a/client/src/assets/i18n/linePath/de.json b/client/src/assets/i18n/linePath/de.json index 21f3493b..a5714e90 100644 --- a/client/src/assets/i18n/linePath/de.json +++ b/client/src/assets/i18n/linePath/de.json @@ -9,4 +9,4 @@ "linePathForm.leaveEditorButtonLabel": "Editor verlassen", "linePathForm.addLinePathTitle": "Linie einzeichnen", "linePathForm.cancelButtonLabel": "Abbrechen" -} +} \ No newline at end of file diff --git a/client/src/assets/i18n/maps/de.json b/client/src/assets/i18n/maps/de.json index 545a2704..ad2a8f99 100644 --- a/client/src/assets/i18n/maps/de.json +++ b/client/src/assets/i18n/maps/de.json @@ -30,4 +30,4 @@ "mapMarkerForm.PARKING": "Parkplatz", "mapMarkerForm.ACCESS_POINT": "Zustiegspunkt", "mapMarkerForm.OTHER": "Verschiedenes" -} +} \ No newline at end of file diff --git a/client/src/assets/i18n/sector/de.json b/client/src/assets/i18n/sector/de.json index 412d5ea1..53fb9757 100644 --- a/client/src/assets/i18n/sector/de.json +++ b/client/src/assets/i18n/sector/de.json @@ -38,4 +38,4 @@ "sectorForm.lngLabel": "Längengrad", "sectorForm.lngPlaceholder": "z.B. 7.26605", "sectorForm.invalidLng": "Ungültiger Längengrad" -} +} \ No newline at end of file diff --git a/client/src/assets/i18n/topoImage/de.json b/client/src/assets/i18n/topoImage/de.json index b04f9c4f..3a3a1b76 100644 --- a/client/src/assets/i18n/topoImage/de.json +++ b/client/src/assets/i18n/topoImage/de.json @@ -30,4 +30,4 @@ "topoImageForm.invalidLng": "Ungültiger Längengrad", "topoImageForm.topoImageImageImageLabel": "Bild", "topoImageList.deleteTopoImageButtonLabel": "Löschen" -} +} \ No newline at end of file diff --git a/server/src/marshmallow_schemas/scale_schema.py b/server/src/marshmallow_schemas/scale_schema.py index 8634682a..fc130871 100644 --- a/server/src/marshmallow_schemas/scale_schema.py +++ b/server/src/marshmallow_schemas/scale_schema.py @@ -9,7 +9,7 @@ class ScaleSchema(ma.SQLAlchemySchema): name = fields.String() type = EnumField(LineTypeEnum, by_value=True) grades = fields.List(fields.Dict) - gradeBrackets = fields.List(fields.Integer, attribute="grade_brackets") + gradeBrackets = fields.Dict(attribute="grade_brackets") scale_schema = ScaleSchema() diff --git a/server/src/messages/messages.py b/server/src/messages/messages.py index 2cdbb792..8b659b33 100644 --- a/server/src/messages/messages.py +++ b/server/src/messages/messages.py @@ -34,4 +34,5 @@ class ResponseMessage(Enum): CANNOT_DELETE_SUPERADMIN = "CANNOT_DELETE_SUPERADMIN" MIGRATION_IMPOSSIBLE = "MIGRATION_IMPOSSIBLE" CANNOT_CHANGE_SCALES_CONFLICTING_LINES = "CANNOT_CHANGE_SCALES_CONFLICTING_LINES" - INVALID_SCALES_GRADE_BRACKETS = "INVALID_SCALES_GRADE_BRACKETS" + INVALID_BAR_CHART_BRACKETS = "INVALID_BAR_CHART_BRACKETS" + INVALID_STACKED_CHART_BRACKETS = "INVALID_STACKED_CHART_BRACKETS" diff --git a/server/src/migrations/versions/68dd991d54f5_add_line_chart_grade_brackets.py b/server/src/migrations/versions/68dd991d54f5_add_line_chart_grade_brackets.py new file mode 100644 index 00000000..0625bee7 --- /dev/null +++ b/server/src/migrations/versions/68dd991d54f5_add_line_chart_grade_brackets.py @@ -0,0 +1,68 @@ +"""empty message + +Revision ID: 68dd991d54f5 +Revises: 4da80c2a64d6 +Create Date: 2025-02-12 20:11:35.096135 + +""" + +import sqlalchemy as sa +from alembic import op +from sqlalchemy.dialects import postgresql +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import Session + +# revision identifiers, used by Alembic. +revision = "68dd991d54f5" +down_revision = "4da80c2a64d6" +branch_labels = None +depends_on = None + + +Base = declarative_base() + + +class Scale(Base): + __tablename__ = "scales" + name = sa.Column(sa.String(32), nullable=False, primary_key=True) + type = sa.Column(sa.Enum("BOULDER", "SPORT", name="linetypeenum"), nullable=False, primary_key=True) + grades = sa.Column(postgresql.JSON, nullable=False) + grade_brackets = sa.Column(postgresql.JSON, nullable=False) + + +def upgrade(): + bind = op.get_bind() + session = Session(bind=bind) + + scales = session.query(Scale).all() + for scale in scales: + + bar_chart_brackets = [] + + for i in range(0, len(scale.grade_brackets)): + lower_grade = None + upper_grade = scale.grades[-1]["name"] + last_bracket_value = scale.grade_brackets[i - 1] if i > 0 else 0 + for grade in scale.grades: + if grade["value"] == last_bracket_value + 1: + lower_grade = grade["name"] + continue + if grade["value"] == scale.grade_brackets[i]: + upper_grade = grade["name"] + break + bar_chart_brackets.append({"name": f"{lower_grade} - {upper_grade}", "value": scale.grade_brackets[i]}) + + scale.grade_brackets = {"stackedChartBrackets": scale.grade_brackets, "barChartBrackets": bar_chart_brackets} + session.add(scale) + session.commit() + + +def downgrade(): + bind = op.get_bind() + session = Session(bind=bind) + + scales = session.query(Scale).all() + for scale in scales: + scale.grade_brackets = scale.grade_brackets["stackedChartBrackets"] + session.add(scale) + session.commit() diff --git a/server/src/resources/ranking_resources.py b/server/src/resources/ranking_resources.py index 88cbb9c3..30d0c900 100644 --- a/server/src/resources/ranking_resources.py +++ b/server/src/resources/ranking_resources.py @@ -2,6 +2,7 @@ from flask import copy_current_request_context, jsonify, request from flask.views import MethodView +from sqlalchemy.orm import joinedload from error_handling.http_exceptions.bad_request import BadRequest from error_handling.http_exceptions.unauthorized import Unauthorized @@ -33,6 +34,7 @@ def get(self): raise BadRequest("Can either fetch crag OR sector OR global ranking.") query = db.session.query(Ranking) + query = query.options(joinedload(Ranking.user)) query = query.filter(Ranking.type == line_type) query = query.filter(Ranking.crag_id == crag_id) query = query.filter(Ranking.sector_id == sector_id) diff --git a/server/src/resources/scale_resources.py b/server/src/resources/scale_resources.py index 2a4f8dd1..e26fe9ee 100644 --- a/server/src/resources/scale_resources.py +++ b/server/src/resources/scale_resources.py @@ -74,9 +74,12 @@ def put(self, line_type, name): if line.grade_value not in values: raise Conflict(ResponseMessage.CANNOT_CHANGE_SCALES_CONFLICTING_LINES.value) + # All values used in the grade brackets must be in the grades all_grades = set(g["value"] for g in scale_data["grades"]) - if any(gb not in all_grades for gb in scale_data["gradeBrackets"]): - raise BadRequest(ResponseMessage.INVALID_SCALES_GRADE_BRACKETS) + if any(gb not in all_grades for gb in scale_data["gradeBrackets"]["stackedChartBrackets"]): + raise BadRequest(ResponseMessage.INVALID_STACKED_CHART_BRACKETS) + if any(gb.get("value") not in all_grades for gb in scale_data["gradeBrackets"]["barChartBrackets"]): + raise BadRequest(ResponseMessage.INVALID_BAR_CHART_BRACKETS) scale.name = scale_data["name"] scale.type = scale_data["type"] diff --git a/server/src/webargs_schemas/scale_args.py b/server/src/webargs_schemas/scale_args.py index 433ff4bb..10308438 100644 --- a/server/src/webargs_schemas/scale_args.py +++ b/server/src/webargs_schemas/scale_args.py @@ -7,6 +7,23 @@ "value": fields.Integer(required=True, allow_none=False), } +stacked_chart_bracket_args = { + "name": fields.String(required=True, allow_none=False), + "value": fields.Integer(required=True, allow_none=False), +} + +grade_bracket_args = { + "barChartBrackets": fields.List( + fields.Nested(stacked_chart_bracket_args), + required=True, + allow_none=False, + validate=lambda gbs: all(gb.get("value") > 0 for gb in gbs), + ), + "stackedChartBrackets": fields.List( + fields.Integer(), required=True, allow_none=False, validate=lambda gbs: all(gb > 0 for gb in gbs) + ), +} + scale_args = { "name": fields.String(required=False), "type": fields.Enum(LineTypeEnum, required=True, allow_none=False), @@ -16,7 +33,5 @@ allow_none=False, validate=lambda gs: len(gs) == len(set([g["name"] for g in gs])), # Grade names must be unique ), - "gradeBrackets": fields.List( - fields.Integer(), required=True, allow_none=False, validate=lambda gbs: all(gb > 0 for gb in gbs) - ), + "gradeBrackets": fields.Nested(grade_bracket_args, required=True, allow_none=False), } From 3f82a1be10257038f857d81d960315edf637488c Mon Sep 17 00:00:00 2001 From: Felix Engelmann Date: Fri, 14 Feb 2025 18:43:05 +0100 Subject: [PATCH 05/18] Formatting --- .../scale-form/scale-form.component.html | 56 ++++++++++--------- client/src/assets/i18n/area/de.json | 2 +- client/src/assets/i18n/crag/de.json | 2 +- client/src/assets/i18n/line/de.json | 2 +- client/src/assets/i18n/linePath/de.json | 2 +- client/src/assets/i18n/maps/de.json | 2 +- client/src/assets/i18n/sector/de.json | 2 +- client/src/assets/i18n/topoImage/de.json | 2 +- 8 files changed, 36 insertions(+), 34 deletions(-) diff --git a/client/src/app/modules/scale/scale-form/scale-form.component.html b/client/src/app/modules/scale/scale-form/scale-form.component.html index 5790f64d..c7bac8b4 100644 --- a/client/src/app/modules/scale/scale-form/scale-form.component.html +++ b/client/src/app/modules/scale/scale-form/scale-form.component.html @@ -5,9 +5,8 @@ {{ scale?.lineType | transloco }} {{ scale?.name }} - + >{{ scale?.lineType | transloco }} {{ scale?.name }} +
@@ -44,7 +43,7 @@ {{ t("required") }}
-
+
- - + +
-
+
-

{{ t("gradeBracketsLabel") }} - {{ t('stackedChartBrackets') }}

+

+ {{ t("gradeBracketsLabel") }} - {{ t("stackedChartBrackets") }} +

- {{ t("gradeBracketsDescriptionStackedChart", {min: 2, max: 8}) }} + {{ + t("gradeBracketsDescriptionStackedChart", { min: 2, max: 8 }) + }}
{{ t("gradeBracketsLabel") }} - {{ t('stackedChartBrackets') }}
-
+
-

{{ t("gradeBracketsLabel") }} - {{ t('barChartBrackets') }}

+

{{ t("gradeBracketsLabel") }} - {{ t("barChartBrackets") }}

- {{ t("gradeBracketsDescriptionBarChart", {min: 2, max: 12}) }} + {{ + t("gradeBracketsDescriptionBarChart", { min: 2, max: 12 }) + }}
{{ t("gradeBracketsLabel") }} - {{ t('barChartBrackets') }}
-
diff --git a/client/src/assets/i18n/area/de.json b/client/src/assets/i18n/area/de.json index 6ff60779..82eb4f05 100644 --- a/client/src/assets/i18n/area/de.json +++ b/client/src/assets/i18n/area/de.json @@ -38,4 +38,4 @@ "areaForm.lngLabel": "Längengrad", "areaForm.lngPlaceholder": "z.B. 7.26605", "areaForm.invalidLng": "Ungültiger Längengrad" -} \ No newline at end of file +} diff --git a/client/src/assets/i18n/crag/de.json b/client/src/assets/i18n/crag/de.json index 8901075c..e15f67cb 100644 --- a/client/src/assets/i18n/crag/de.json +++ b/client/src/assets/i18n/crag/de.json @@ -39,4 +39,4 @@ "cragForm.invalidLng": "Ungültiger Längengrad", "crag.cragRules": "Gebietsregeln", "cragList.cragListTitle": "Gebiete" -} \ No newline at end of file +} diff --git a/client/src/assets/i18n/line/de.json b/client/src/assets/i18n/line/de.json index f7372ec0..483a1f46 100644 --- a/client/src/assets/i18n/line/de.json +++ b/client/src/assets/i18n/line/de.json @@ -114,4 +114,4 @@ "lineBoolPropList.sitstart": "Sitzstart", "lineForm.lineSitstartLabel": "Sitzstart", "lineList.lineListTitle": "Linien" -} \ No newline at end of file +} diff --git a/client/src/assets/i18n/linePath/de.json b/client/src/assets/i18n/linePath/de.json index a5714e90..21f3493b 100644 --- a/client/src/assets/i18n/linePath/de.json +++ b/client/src/assets/i18n/linePath/de.json @@ -9,4 +9,4 @@ "linePathForm.leaveEditorButtonLabel": "Editor verlassen", "linePathForm.addLinePathTitle": "Linie einzeichnen", "linePathForm.cancelButtonLabel": "Abbrechen" -} \ No newline at end of file +} diff --git a/client/src/assets/i18n/maps/de.json b/client/src/assets/i18n/maps/de.json index ad2a8f99..545a2704 100644 --- a/client/src/assets/i18n/maps/de.json +++ b/client/src/assets/i18n/maps/de.json @@ -30,4 +30,4 @@ "mapMarkerForm.PARKING": "Parkplatz", "mapMarkerForm.ACCESS_POINT": "Zustiegspunkt", "mapMarkerForm.OTHER": "Verschiedenes" -} \ No newline at end of file +} diff --git a/client/src/assets/i18n/sector/de.json b/client/src/assets/i18n/sector/de.json index 53fb9757..412d5ea1 100644 --- a/client/src/assets/i18n/sector/de.json +++ b/client/src/assets/i18n/sector/de.json @@ -38,4 +38,4 @@ "sectorForm.lngLabel": "Längengrad", "sectorForm.lngPlaceholder": "z.B. 7.26605", "sectorForm.invalidLng": "Ungültiger Längengrad" -} \ No newline at end of file +} diff --git a/client/src/assets/i18n/topoImage/de.json b/client/src/assets/i18n/topoImage/de.json index 3a3a1b76..b04f9c4f 100644 --- a/client/src/assets/i18n/topoImage/de.json +++ b/client/src/assets/i18n/topoImage/de.json @@ -30,4 +30,4 @@ "topoImageForm.invalidLng": "Ungültiger Längengrad", "topoImageForm.topoImageImageImageLabel": "Bild", "topoImageList.deleteTopoImageButtonLabel": "Löschen" -} \ No newline at end of file +} From 4944d6c03a1d7207477589aef9e5972b718269f6 Mon Sep 17 00:00:00 2001 From: Felix Engelmann Date: Fri, 14 Feb 2025 18:58:04 +0100 Subject: [PATCH 06/18] Fix bug in put scales, Fix broken tests --- server/src/models/scale.py | 24 ++++++++++++++++++++++-- server/src/resources/scale_resources.py | 7 +++++-- server/tests/test_scale_resources.py | 21 ++++++++++++++------- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/server/src/models/scale.py b/server/src/models/scale.py index 329d5370..5237cb43 100644 --- a/server/src/models/scale.py +++ b/server/src/models/scale.py @@ -78,8 +78,28 @@ } GRADE_BRACKETS = { - LineTypeEnum.BOULDER: {"FB": [9, 15, 21, 22]}, - LineTypeEnum.SPORT: {"UIAA": [9, 15, 21, 22]}, + LineTypeEnum.BOULDER: { + "FB": { + "barChartBrackets": [ + {"name": "1-4", "value": 9}, + {"name": "5-6", "value": 15}, + {"name": "7-8", "value": 21}, + {"name": "9-12", "value": 22}, + ], + "stackedChartBrackets": [9, 15, 21, 22], + } + }, + LineTypeEnum.SPORT: { + "UIAA": { + "barChartBrackets": [ + {"name": "I-IV", "value": 9}, + {"name": "V-VI", "value": 15}, + {"name": "VII-VIII", "value": 21}, + {"name": "IX-XII", "value": 22}, + ], + "stackedChartBrackets": [9, 15, 21, 22], + } + }, } diff --git a/server/src/resources/scale_resources.py b/server/src/resources/scale_resources.py index e26fe9ee..7b6fbf39 100644 --- a/server/src/resources/scale_resources.py +++ b/server/src/resources/scale_resources.py @@ -37,9 +37,12 @@ class CreateScale(MethodView): def post(self): scale_data = parser.parse(scale_args, request) + # All values used in the grade brackets must be in the grades all_grades = set(g["value"] for g in scale_data["grades"]) - if any(gb not in all_grades for gb in scale_data["gradeBrackets"]): - raise BadRequest(ResponseMessage.INVALID_SCALES_GRADE_BRACKETS) + if any(gb not in all_grades for gb in scale_data["gradeBrackets"]["stackedChartBrackets"]): + raise BadRequest(ResponseMessage.INVALID_STACKED_CHART_BRACKETS) + if any(gb.get("value") not in all_grades for gb in scale_data["gradeBrackets"]["barChartBrackets"]): + raise BadRequest(ResponseMessage.INVALID_BAR_CHART_BRACKETS) scale = Scale() scale.name = scale_data["name"] diff --git a/server/tests/test_scale_resources.py b/server/tests/test_scale_resources.py index af321cea..d7c9d625 100644 --- a/server/tests/test_scale_resources.py +++ b/server/tests/test_scale_resources.py @@ -32,7 +32,14 @@ def test_successful_create_scale(client, admin_token): {"name": "hard", "value": 3}, {"name": "insane", "value": 4}, ], - "gradeBrackets": [1, 3, 4], + "gradeBrackets": { + "barChartBrackets": [ + {"name": "easy", "value": 1}, + {"name": "hard", "value": 3}, + {"name": "insane", "value": 4}, + ], + "stackedChartBrackets": [1, 3, 4], + }, } rv = client.post("/api/scales", token=admin_token, json=scale_data) @@ -52,10 +59,10 @@ def test_successful_update_scale(client, admin_token): "gradeBrackets": GRADE_BRACKETS[LineTypeEnum.BOULDER]["FB"], } - rv = client.put(f"/api/scales/BOULDER/FB", token=admin_token, json=scale_data) + rv = client.put("/api/scales/BOULDER/FB", token=admin_token, json=scale_data) assert rv.status_code == 200 - rv = client.get(f"/api/lines/treppe") + rv = client.get("/api/lines/treppe") assert rv.status_code == 200 res = rv.json assert res["gradeScale"] == "FBnew" @@ -69,7 +76,7 @@ def test_unsuccessful_update_scale_changed_type(client, admin_token): "gradeBrackets": GRADE_BRACKETS[LineTypeEnum.BOULDER]["FB"], } - rv = client.put(f"/api/scales/BOULDER/FB", token=admin_token, json=scale_data) + rv = client.put("/api/scales/BOULDER/FB", token=admin_token, json=scale_data) assert rv.status_code == 409 @@ -81,15 +88,15 @@ def test_unsuccessful_update_scale_missing_values(client, admin_token): "gradeBrackets": GRADE_BRACKETS[LineTypeEnum.BOULDER]["FB"], } - rv = client.put(f"/api/scales/BOULDER/FB", token=admin_token, json=grades_data) + rv = client.put("/api/scales/BOULDER/FB", token=admin_token, json=grades_data) assert rv.status_code == 409 def test_successful_delete_scale(client, admin_token): - rv = client.delete(f"/api/scales/SPORT/UIAA", token=admin_token) + rv = client.delete("/api/scales/SPORT/UIAA", token=admin_token) assert rv.status_code == 204 def test_unsuccessful_delete_scale(client, admin_token): - rv = client.delete(f"/api/scales/BOULDER/FB", token=admin_token) + rv = client.delete("/api/scales/BOULDER/FB", token=admin_token) assert rv.status_code == 409 From 20161467013b962b98c00385e3b8a3df3f8c643c Mon Sep 17 00:00:00 2001 From: Felix Engelmann Date: Fri, 14 Feb 2025 19:06:52 +0100 Subject: [PATCH 07/18] Fix migration for new instances --- .../versions/68dd991d54f5_add_line_chart_grade_brackets.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/src/migrations/versions/68dd991d54f5_add_line_chart_grade_brackets.py b/server/src/migrations/versions/68dd991d54f5_add_line_chart_grade_brackets.py index 0625bee7..2997ab03 100644 --- a/server/src/migrations/versions/68dd991d54f5_add_line_chart_grade_brackets.py +++ b/server/src/migrations/versions/68dd991d54f5_add_line_chart_grade_brackets.py @@ -37,6 +37,11 @@ def upgrade(): scales = session.query(Scale).all() for scale in scales: + # Don't do anything if the grade_brackets are already in the correct format (e.g. for new instances) + if isinstance(scale.grade_brackets, dict): + # They have been a list before, so if they are already a dict, we can assume they are in the correct format + continue + bar_chart_brackets = [] for i in range(0, len(scale.grade_brackets)): From b2edc504ee9d0a53a6c7b418ee464df691f1947c Mon Sep 17 00:00:00 2001 From: Felix Engelmann Date: Fri, 14 Feb 2025 19:37:56 +0100 Subject: [PATCH 08/18] Fix default brackets --- .../scale-form/scale-form.component.html | 4 +-- .../scale/scale-form/scale-form.component.ts | 2 +- .../grade-distribution-bar-chart.component.ts | 6 ++-- server/src/models/scale.py | 31 ++++++++++++++----- 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/client/src/app/modules/scale/scale-form/scale-form.component.html b/client/src/app/modules/scale/scale-form/scale-form.component.html index c7bac8b4..604b9c20 100644 --- a/client/src/app/modules/scale/scale-form/scale-form.component.html +++ b/client/src/app/modules/scale/scale-form/scale-form.component.html @@ -240,7 +240,7 @@

{{ t("gradeBracketsLabel") }} - {{ t("barChartBrackets") }}

{{ - t("gradeBracketsDescriptionBarChart", { min: 2, max: 12 }) + t("gradeBracketsDescriptionBarChart", { min: 2, max: 14 }) }}
@@ -291,7 +291,7 @@

{{ t("gradeBracketsLabel") }} - {{ t("barChartBrackets") }}

8", "value": 22}, ], "stackedChartBrackets": [9, 15, 21, 22], } @@ -92,10 +102,15 @@ LineTypeEnum.SPORT: { "UIAA": { "barChartBrackets": [ - {"name": "I-IV", "value": 9}, - {"name": "V-VI", "value": 15}, - {"name": "VII-VIII", "value": 21}, - {"name": "IX-XII", "value": 22}, + {"name": "I-III", "value": 3}, + {"name": "IV", "value": 6}, + {"name": "V", "value": 9}, + {"name": "VI", "value": 12}, + {"name": "VII", "value": 15}, + {"name": "VIII", "value": 18}, + {"name": "IX", "value": 21}, + {"name": "X", "value": 24}, + {"name": ">= XI", "value": 25}, ], "stackedChartBrackets": [9, 15, 21, 22], } From 127b140d6c4036853ec243e075b48f9bd2783d23 Mon Sep 17 00:00:00 2001 From: Felix Engelmann Date: Fri, 14 Feb 2025 20:02:39 +0100 Subject: [PATCH 09/18] Fix broken rankings --- server/src/util/scripts/build_rankings.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/server/src/util/scripts/build_rankings.py b/server/src/util/scripts/build_rankings.py index e6ebf9eb..5e0a79dc 100644 --- a/server/src/util/scripts/build_rankings.py +++ b/server/src/util/scripts/build_rankings.py @@ -75,7 +75,12 @@ def build_rankings(): page = 1 has_next_page = True while has_next_page: - query = select(Ascent).join(Line).filter(Ascent.created_by_id == user.id, Line.archived == False) + query = ( + select(Ascent) + .join(Line) + .filter(Ascent.created_by_id == user.id, Line.archived.is_(False)) + .distinct(Line.id) + ) paginated_ascents = db.paginate(query, page=page, per_page=50) has_next_page = paginated_ascents.has_next if paginated_ascents.has_next: From c3471e316814562e4e590e0fea4289e77a3bdffa Mon Sep 17 00:00:00 2001 From: Felix Engelmann Date: Sat, 15 Feb 2025 18:22:26 +0100 Subject: [PATCH 10/18] Fix wrong display of grades in history list --- .../history-list/history-list.component.ts | 42 +++++++++++++++---- client/src/assets/i18n/area/de.json | 2 +- client/src/assets/i18n/crag/de.json | 2 +- client/src/assets/i18n/de.json | 2 + client/src/assets/i18n/line/de.json | 2 +- client/src/assets/i18n/linePath/de.json | 2 +- client/src/assets/i18n/maps/de.json | 2 +- client/src/assets/i18n/sector/de.json | 2 +- client/src/assets/i18n/topoImage/de.json | 2 +- 9 files changed, 43 insertions(+), 15 deletions(-) diff --git a/client/src/app/modules/history/history-list/history-list.component.ts b/client/src/app/modules/history/history-list/history-list.component.ts index 864e6ec3..8c2af001 100644 --- a/client/src/app/modules/history/history-list/history-list.component.ts +++ b/client/src/app/modules/history/history-list/history-list.component.ts @@ -25,6 +25,7 @@ import { InfiniteScrollModule } from 'ngx-infinite-scroll'; import { MessageModule } from 'primeng/message'; import { ScalesService } from '../../../services/crud/scales.service'; import { map } from 'rxjs/operators'; +import { TranslateSpecialGradesPipe } from '../../shared/pipes/translate-special-grades.pipe'; @Component({ selector: 'lc-history-list', @@ -61,6 +62,7 @@ export class HistoryListComponent implements OnInit { constructor( private historyService: HistoryService, + private translateSpecialGradesPipe: TranslateSpecialGradesPipe, private router: Router, private store: Store, private transloco: TranslocoService, @@ -127,8 +129,13 @@ export class HistoryListComponent implements OnInit { if (event.type === HistoryItemType.UPDATED) { switch (event.objectType) { case ObjectType.Line: - /** t(history.grading_changed) */ - return this.transloco.translate('history.grading_changed'); + if (Number(event.newValue) >= 0) { + /** t(history.grading_changed) */ + return this.transloco.translate('history.grading_changed'); + } else { + /** t(history.project_status_changed) */ + return this.transloco.translate('history.project_status_changed'); + } default: return ''; } @@ -153,16 +160,33 @@ export class HistoryListComponent implements OnInit { Number(event.newValue), ), ]).pipe( - map((oldGrade, newGrade) => { + map(([oldGrade, newGrade]) => { + return [ + this.translateSpecialGradesPipe.transform(oldGrade), + this.translateSpecialGradesPipe.transform(newGrade), + ]; + }), + map(([oldGrade, newGrade]) => { if ( Number(event.oldValue) < 0 && - Number(event.oldValue) < Number(event.newValue) + Number(event.oldValue) < Number(event.newValue) && + Number(event.newValue) >= 0 ) { /** t(history.projectClimbed) */ return this.transloco.translate('history.projectClimbed', { line: line.name, newGrade, }); + } else if ( + Number(event.oldValue) < 0 && + Number(event.newValue) < 0 + ) { + /** t(history.projectStatusChanged) */ + return this.transloco.translate('history.projectStatusChanged', { + line: line.name, + oldGrade, + newGrade, + }); } else if ( Number(event.oldValue) === 0 && Number(event.oldValue) < Number(event.newValue) @@ -218,10 +242,12 @@ export class HistoryListComponent implements OnInit { return 'pi pi-plus'; } if (event.type === HistoryItemType.UPDATED) { - if (Number(event.oldValue) < Number(event.newValue)) { - return 'pi pi-arrow-up'; - } else if (Number(event.oldValue) > Number(event.newValue)) { - return 'pi pi-arrow-down'; + if (Number(event.newValue) >= 0) { + if (Number(event.oldValue) < Number(event.newValue)) { + return 'pi pi-arrow-up'; + } else if (Number(event.oldValue) > Number(event.newValue)) { + return 'pi pi-arrow-down'; + } } return 'pi pi-cog'; } diff --git a/client/src/assets/i18n/area/de.json b/client/src/assets/i18n/area/de.json index 82eb4f05..6ff60779 100644 --- a/client/src/assets/i18n/area/de.json +++ b/client/src/assets/i18n/area/de.json @@ -38,4 +38,4 @@ "areaForm.lngLabel": "Längengrad", "areaForm.lngPlaceholder": "z.B. 7.26605", "areaForm.invalidLng": "Ungültiger Längengrad" -} +} \ No newline at end of file diff --git a/client/src/assets/i18n/crag/de.json b/client/src/assets/i18n/crag/de.json index e15f67cb..8901075c 100644 --- a/client/src/assets/i18n/crag/de.json +++ b/client/src/assets/i18n/crag/de.json @@ -39,4 +39,4 @@ "cragForm.invalidLng": "Ungültiger Längengrad", "crag.cragRules": "Gebietsregeln", "cragList.cragListTitle": "Gebiete" -} +} \ No newline at end of file diff --git a/client/src/assets/i18n/de.json b/client/src/assets/i18n/de.json index f4ded6d6..e94c1c30 100644 --- a/client/src/assets/i18n/de.json +++ b/client/src/assets/i18n/de.json @@ -640,7 +640,9 @@ "history.created_area": "Neuer Bereich", "history.created_line": "Neue Linie", "history.grading_changed": "Bewertung geändert", + "history.project_status_changed": "Projektstatus geändert", "history.projectClimbed": "Das Projekt {{line}} wurde geklettert und mit {{newGrade}} bewertet.", + "history.projectStatusChanged": "Der Projektstatus der Linie {{line}} hat sich von {{oldGrade}} zu {{newGrade}} geändert.", "history.lineGraded": "Die bisher nicht bewertete Linie {{line}} wurde mit {{newGrade}} bewertet.", "history.upgrade": "{{line}} wurde von {{oldGrade}} auf {{newGrade}} aufgewertet.", "history.downgrade": "{{line}} wurde von {{oldGrade}} auf {{newGrade}} abgewertet.", diff --git a/client/src/assets/i18n/line/de.json b/client/src/assets/i18n/line/de.json index 483a1f46..f7372ec0 100644 --- a/client/src/assets/i18n/line/de.json +++ b/client/src/assets/i18n/line/de.json @@ -114,4 +114,4 @@ "lineBoolPropList.sitstart": "Sitzstart", "lineForm.lineSitstartLabel": "Sitzstart", "lineList.lineListTitle": "Linien" -} +} \ No newline at end of file diff --git a/client/src/assets/i18n/linePath/de.json b/client/src/assets/i18n/linePath/de.json index 21f3493b..a5714e90 100644 --- a/client/src/assets/i18n/linePath/de.json +++ b/client/src/assets/i18n/linePath/de.json @@ -9,4 +9,4 @@ "linePathForm.leaveEditorButtonLabel": "Editor verlassen", "linePathForm.addLinePathTitle": "Linie einzeichnen", "linePathForm.cancelButtonLabel": "Abbrechen" -} +} \ No newline at end of file diff --git a/client/src/assets/i18n/maps/de.json b/client/src/assets/i18n/maps/de.json index 545a2704..ad2a8f99 100644 --- a/client/src/assets/i18n/maps/de.json +++ b/client/src/assets/i18n/maps/de.json @@ -30,4 +30,4 @@ "mapMarkerForm.PARKING": "Parkplatz", "mapMarkerForm.ACCESS_POINT": "Zustiegspunkt", "mapMarkerForm.OTHER": "Verschiedenes" -} +} \ No newline at end of file diff --git a/client/src/assets/i18n/sector/de.json b/client/src/assets/i18n/sector/de.json index 412d5ea1..53fb9757 100644 --- a/client/src/assets/i18n/sector/de.json +++ b/client/src/assets/i18n/sector/de.json @@ -38,4 +38,4 @@ "sectorForm.lngLabel": "Längengrad", "sectorForm.lngPlaceholder": "z.B. 7.26605", "sectorForm.invalidLng": "Ungültiger Längengrad" -} +} \ No newline at end of file diff --git a/client/src/assets/i18n/topoImage/de.json b/client/src/assets/i18n/topoImage/de.json index b04f9c4f..3a3a1b76 100644 --- a/client/src/assets/i18n/topoImage/de.json +++ b/client/src/assets/i18n/topoImage/de.json @@ -30,4 +30,4 @@ "topoImageForm.invalidLng": "Ungültiger Längengrad", "topoImageForm.topoImageImageImageLabel": "Bild", "topoImageList.deleteTopoImageButtonLabel": "Löschen" -} +} \ No newline at end of file From 8c189bd43e97ad3798db60eae9fe3b1050dd465a Mon Sep 17 00:00:00 2001 From: Felix Engelmann Date: Sat, 15 Feb 2025 18:22:56 +0100 Subject: [PATCH 11/18] Formatting --- client/src/assets/i18n/area/de.json | 2 +- client/src/assets/i18n/crag/de.json | 2 +- client/src/assets/i18n/line/de.json | 2 +- client/src/assets/i18n/linePath/de.json | 2 +- client/src/assets/i18n/maps/de.json | 2 +- client/src/assets/i18n/sector/de.json | 2 +- client/src/assets/i18n/topoImage/de.json | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/client/src/assets/i18n/area/de.json b/client/src/assets/i18n/area/de.json index 6ff60779..82eb4f05 100644 --- a/client/src/assets/i18n/area/de.json +++ b/client/src/assets/i18n/area/de.json @@ -38,4 +38,4 @@ "areaForm.lngLabel": "Längengrad", "areaForm.lngPlaceholder": "z.B. 7.26605", "areaForm.invalidLng": "Ungültiger Längengrad" -} \ No newline at end of file +} diff --git a/client/src/assets/i18n/crag/de.json b/client/src/assets/i18n/crag/de.json index 8901075c..e15f67cb 100644 --- a/client/src/assets/i18n/crag/de.json +++ b/client/src/assets/i18n/crag/de.json @@ -39,4 +39,4 @@ "cragForm.invalidLng": "Ungültiger Längengrad", "crag.cragRules": "Gebietsregeln", "cragList.cragListTitle": "Gebiete" -} \ No newline at end of file +} diff --git a/client/src/assets/i18n/line/de.json b/client/src/assets/i18n/line/de.json index f7372ec0..483a1f46 100644 --- a/client/src/assets/i18n/line/de.json +++ b/client/src/assets/i18n/line/de.json @@ -114,4 +114,4 @@ "lineBoolPropList.sitstart": "Sitzstart", "lineForm.lineSitstartLabel": "Sitzstart", "lineList.lineListTitle": "Linien" -} \ No newline at end of file +} diff --git a/client/src/assets/i18n/linePath/de.json b/client/src/assets/i18n/linePath/de.json index a5714e90..21f3493b 100644 --- a/client/src/assets/i18n/linePath/de.json +++ b/client/src/assets/i18n/linePath/de.json @@ -9,4 +9,4 @@ "linePathForm.leaveEditorButtonLabel": "Editor verlassen", "linePathForm.addLinePathTitle": "Linie einzeichnen", "linePathForm.cancelButtonLabel": "Abbrechen" -} \ No newline at end of file +} diff --git a/client/src/assets/i18n/maps/de.json b/client/src/assets/i18n/maps/de.json index ad2a8f99..545a2704 100644 --- a/client/src/assets/i18n/maps/de.json +++ b/client/src/assets/i18n/maps/de.json @@ -30,4 +30,4 @@ "mapMarkerForm.PARKING": "Parkplatz", "mapMarkerForm.ACCESS_POINT": "Zustiegspunkt", "mapMarkerForm.OTHER": "Verschiedenes" -} \ No newline at end of file +} diff --git a/client/src/assets/i18n/sector/de.json b/client/src/assets/i18n/sector/de.json index 53fb9757..412d5ea1 100644 --- a/client/src/assets/i18n/sector/de.json +++ b/client/src/assets/i18n/sector/de.json @@ -38,4 +38,4 @@ "sectorForm.lngLabel": "Längengrad", "sectorForm.lngPlaceholder": "z.B. 7.26605", "sectorForm.invalidLng": "Ungültiger Längengrad" -} \ No newline at end of file +} diff --git a/client/src/assets/i18n/topoImage/de.json b/client/src/assets/i18n/topoImage/de.json index 3a3a1b76..b04f9c4f 100644 --- a/client/src/assets/i18n/topoImage/de.json +++ b/client/src/assets/i18n/topoImage/de.json @@ -30,4 +30,4 @@ "topoImageForm.invalidLng": "Ungültiger Längengrad", "topoImageForm.topoImageImageImageLabel": "Bild", "topoImageList.deleteTopoImageButtonLabel": "Löschen" -} \ No newline at end of file +} From 006340568a3db0519da1bd9aa7c62ec21ed4fbfb Mon Sep 17 00:00:00 2001 From: Felix Engelmann Date: Sat, 15 Feb 2025 23:08:16 +0100 Subject: [PATCH 12/18] Fix leveled grade distribution issues by using meter group --- .../leveled-grade-distribution.component.html | 94 +++++-------------- .../leveled-grade-distribution.component.scss | 40 -------- .../leveled-grade-distribution.component.ts | 45 +++++++-- .../src/app/modules/shared/shared.module.ts | 2 + client/src/app/styles/grades.scss | 21 ----- 5 files changed, 62 insertions(+), 140 deletions(-) diff --git a/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.html b/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.html index 76a86661..49de20dc 100644 --- a/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.html +++ b/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.html @@ -6,80 +6,34 @@ > -
- - - {{ stackChartData.length > 1 ? data.gradeScale : "" }} - {{ t("lines") }}: - - - - - {{ data.bracketLabels[i] }} - - - - - {{ t("leveledGradeDistributionProjects") }} - + + +
    +
  1. + {{ data.total - data.projects }} + {{ stackChartData.length > 1 ? data.gradeScale : "" }} + {{ t("lines") }}: +
  2. +
  3. + + {{ meterValue.value }} {{ meterValue.label }} +
  4. +
  5. + + {{ data.projects }} {{ t('leveledGradeDistributionProjects') }} +
  6. +
+
+
+
+
diff --git a/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.scss b/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.scss index c2d22353..6e2ec66f 100644 --- a/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.scss +++ b/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.scss @@ -1,45 +1,5 @@ :host { width: 100%; - display: block; } -.grade-distribution-tags { - flex-wrap: wrap; - p-tag { - white-space: nowrap; - } -} - -.grade-distribution-stack { - overflow: hidden; - display: flex; - flex-wrap: wrap; - row-gap: 1rem; - padding-left: 6px; - - div { - padding: 0.25rem; - color: #ffffff; - font-size: 0.75rem; - font-weight: 700; - display: inline-flex; - white-space: nowrap; - min-width: fit-content; - justify-content: center; - align-items: center; - &.level-total { - margin-left: -6px; - border-radius: 6px 0 0 6px; - } - - &.level-max { - border-radius: 0 6px 6px 0; - } - - &.level-projects { - border-radius: 6px; - margin-left: 0.5rem; - } - } -} diff --git a/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.ts b/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.ts index 5f669130..1d04c08c 100644 --- a/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.ts +++ b/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.ts @@ -11,9 +11,8 @@ type StackChartData = { lineType: LineType; gradeScale: string; projects: number; - brackets: number[]; - bracketLabels: string[]; total: number; + meterValues: { color: string; value: number; label: string }[]; }; /** @@ -30,6 +29,12 @@ export class LeveledGradeDistributionComponent implements OnInit { public stackChartData = null; public gradeDistribution: GradeDistribution; public gradeDistributionEmpty = true; + value = [ + { label: 'Apps', color: '#34d399', value: 16 }, + { label: 'Messages', color: '#fbbf24', value: 8 }, + { label: 'Media', color: '#60a5fa', value: 24 }, + { label: 'System', color: '#c084fc', value: 10 }, + ]; constructor( private scalesService: ScalesService, @@ -47,6 +52,16 @@ export class LeveledGradeDistributionComponent implements OnInit { * Sorts the grades in buckets and calculates the total count for each bucket. */ buildGradeDistribution() { + const colors = [ + 'var(--yellow-500)', + 'var(--blue-500)', + 'var(--red-500)', + 'var(--green-500)', + 'var(--orange-500)', + 'var(--teal-500)', + 'var(--indigo-500)', + 'var(--bluegray-500)', + ]; const stackChartData: StackChartData[] = []; const observables: Observable[] = []; @@ -99,11 +114,15 @@ export class LeveledGradeDistributionComponent implements OnInit { lineType: lineType as LineType, gradeScale, projects: 0, - brackets: Array( - scale.gradeBrackets.stackedChartBrackets.length, - ).fill(0), - bracketLabels: labels, total: 0, + meterValues: Array.from( + { length: scale.gradeBrackets.stackedChartBrackets.length }, + (_, i) => ({ + color: colors[i % colors.length], + value: 0, + label: null, + }), + ), }; for (const gradeValue of Object.keys( this.gradeDistribution[lineType][gradeScale], @@ -124,15 +143,24 @@ export class LeveledGradeDistributionComponent implements OnInit { i == scale.gradeBrackets.stackedChartBrackets.length - 1 ) { - data.brackets[i] += count; + data.meterValues[i].value += count; } else if (gradeValue <= bracket) { - data.brackets[i] += count; + data.meterValues[i].value += count; break; } } } } + // Add the labels to the data + data.meterValues.forEach((meterValue, i) => { + meterValue.label = labels[i]; + }); + // Drop all meter values that are zero + data.meterValues = data.meterValues.filter( + (meterValue) => meterValue.value > 0, + ); stackChartData.push(data); + console.log(data); }), ), ); @@ -144,7 +172,6 @@ export class LeveledGradeDistributionComponent implements OnInit { } else { forkJoin(observables).subscribe(() => { this.stackChartData = stackChartData.sort((a, b) => a.total - b.total); - console.log(this.stackChartData); this.gradeDistributionEmpty = false; }); } diff --git a/client/src/app/modules/shared/shared.module.ts b/client/src/app/modules/shared/shared.module.ts index 68dfd5c0..00e2315a 100644 --- a/client/src/app/modules/shared/shared.module.ts +++ b/client/src/app/modules/shared/shared.module.ts @@ -27,6 +27,7 @@ import { ChartModule } from 'primeng/chart'; import { ChipModule } from 'primeng/chip'; import { TranslateSpecialGradesPipe } from './pipes/translate-special-grades.pipe'; import { LineGradePipe } from './pipes/line-grade.pipe'; +import { MeterGroupModule } from 'primeng/metergroup'; /** * Module for shared components, pipes etc. @@ -63,6 +64,7 @@ import { LineGradePipe } from './pipes/line-grade.pipe'; SkeletonModule, ChartModule, ChipModule, + MeterGroupModule, ], exports: [ DatePipe, diff --git a/client/src/app/styles/grades.scss b/client/src/app/styles/grades.scss index 1e4b164b..9247dbbd 100644 --- a/client/src/app/styles/grades.scss +++ b/client/src/app/styles/grades.scss @@ -1,24 +1,3 @@ -.level-total { - background-color: var(--gray-500) !important; -} - -$colors: var(--yellow-500), var(--blue-500), var(--red-500), var(--green-500), - var(--orange-500), var(--teal-500), var(--indigo-500), var(--bluegray-500); - -@for $i from 1 to length($colors) { - .level-#{$i} { - background-color: nth($colors, $i) !important; - } -} - -.level-max { - background-color: var(--gray-900) !important; -} - -.level-projects { - background-color: var(--gray-500) !important; -} - .neutral-badge { background-color: var(--gray-50) !important; color: #000 !important; From 8b8c54faf79aafb3d48a0f060afbfeada476c820 Mon Sep 17 00:00:00 2001 From: Felix Engelmann Date: Sat, 15 Feb 2025 23:08:30 +0100 Subject: [PATCH 13/18] Formatting --- .../leveled-grade-distribution.component.html | 43 ++++++++++++------- .../leveled-grade-distribution.component.scss | 2 - 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.html b/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.html index 49de20dc..19df03ac 100644 --- a/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.html +++ b/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.html @@ -6,34 +6,45 @@ > - -
+
-
    -
  1. +
      +
    1. {{ data.total - data.projects }} {{ stackChartData.length > 1 ? data.gradeScale : "" }} {{ t("lines") }}:
    2. -
    3. - - {{ meterValue.value }} {{ meterValue.label }} +
    4. + + {{ meterValue.value }} {{ meterValue.label }}
    5. - - {{ data.projects }} {{ t('leveledGradeDistributionProjects') }} + + {{ data.projects }} + {{ t("leveledGradeDistributionProjects") }}
    -
- diff --git a/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.scss b/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.scss index 6e2ec66f..b9bc65ea 100644 --- a/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.scss +++ b/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.scss @@ -1,5 +1,3 @@ :host { width: 100%; } - - From 12a44790d15178e5769b5ef63840b5e6ffc808fd Mon Sep 17 00:00:00 2001 From: Felix Engelmann Date: Sun, 16 Feb 2025 13:34:04 +0100 Subject: [PATCH 14/18] Change endless scroll components to on push change detection --- .../ascent/ascent-list/ascent-list.component.ts | 5 +++++ .../modules/gallery/gallery/gallery.component.ts | 13 ++++++++++++- .../history/history-list/history-list.component.ts | 11 ++++++++++- .../modules/todo/todo-list/todo-list.component.ts | 6 ++++++ 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/client/src/app/modules/ascent/ascent-list/ascent-list.component.ts b/client/src/app/modules/ascent/ascent-list/ascent-list.component.ts index e3a37ef1..0d571d16 100644 --- a/client/src/app/modules/ascent/ascent-list/ascent-list.component.ts +++ b/client/src/app/modules/ascent/ascent-list/ascent-list.component.ts @@ -1,4 +1,6 @@ import { + ChangeDetectionStrategy, + ChangeDetectorRef, Component, HostListener, Input, @@ -88,6 +90,7 @@ import { RegionService } from '../../../services/crud/region.service'; styleUrl: './ascent-list.component.scss', encapsulation: ViewEncapsulation.None, providers: [DialogService, ConfirmationService], + changeDetection: ChangeDetectionStrategy.OnPush, }) @UntilDestroy() export class AscentListComponent implements OnInit { @@ -133,6 +136,7 @@ export class AscentListComponent implements OnInit { private translocoService: TranslocoService, protected scalesService: ScalesService, private regionService: RegionService, + private cdr: ChangeDetectorRef, ) {} ngOnInit() { @@ -292,6 +296,7 @@ export class AscentListComponent implements OnInit { this.hasNextPage = ascents.hasNext; this.loadingFirstPage = LoadingState.DEFAULT; this.loadingAdditionalPage = LoadingState.DEFAULT; + this.cdr.detectChanges(); }); } } diff --git a/client/src/app/modules/gallery/gallery/gallery.component.ts b/client/src/app/modules/gallery/gallery/gallery.component.ts index ce4256ca..8d259995 100644 --- a/client/src/app/modules/gallery/gallery/gallery.component.ts +++ b/client/src/app/modules/gallery/gallery/gallery.component.ts @@ -1,4 +1,9 @@ -import { Component, OnInit } from '@angular/core'; +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + OnInit, +} from '@angular/core'; import { GalleryService } from '../../../services/crud/gallery.service'; import { GalleryImage } from '../../../models/gallery-image'; import { ObjectType } from '../../../models/tag'; @@ -45,6 +50,7 @@ import { InfiniteScrollModule } from 'ngx-infinite-scroll'; templateUrl: './gallery.component.html', styleUrl: './gallery.component.scss', providers: [DialogService, ConfirmationService], + changeDetection: ChangeDetectionStrategy.OnPush, }) @UntilDestroy() export class GalleryComponent implements OnInit { @@ -66,6 +72,7 @@ export class GalleryComponent implements OnInit { private confirmationService: ConfirmationService, private route: ActivatedRoute, private translocoService: TranslocoService, + private cdr: ChangeDetectorRef, ) {} ngOnInit(): void { @@ -137,6 +144,7 @@ export class GalleryComponent implements OnInit { this.hasNextPage = images.hasNext; this.loadingFirstPage = LoadingState.DEFAULT; this.loadingAdditionalPage = LoadingState.DEFAULT; + this.cdr.detectChanges(); }), ) .subscribe(); @@ -159,6 +167,7 @@ export class GalleryComponent implements OnInit { if (galleryImage) { if (this.images.map((i) => i.id).indexOf(galleryImage.id) === -1) { this.images.unshift(galleryImage); + this.cdr.detectChanges(); } } }); @@ -192,6 +201,7 @@ export class GalleryComponent implements OnInit { this.store.dispatch( toastNotification(NotificationIdentifier.GALLERY_IMAGE_DELETED), ); + this.cdr.detectChanges(); }); } @@ -210,6 +220,7 @@ export class GalleryComponent implements OnInit { this.images = this.images.map((i) => i.id === galleryImage.id ? galleryImage : i, ); + this.cdr.detectChanges(); } }); } diff --git a/client/src/app/modules/history/history-list/history-list.component.ts b/client/src/app/modules/history/history-list/history-list.component.ts index 8c2af001..f6f451f2 100644 --- a/client/src/app/modules/history/history-list/history-list.component.ts +++ b/client/src/app/modules/history/history-list/history-list.component.ts @@ -1,4 +1,10 @@ -import { Component, OnInit, ViewEncapsulation } from '@angular/core'; +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + OnInit, + ViewEncapsulation, +} from '@angular/core'; import { BreadcrumbModule } from 'primeng/breadcrumb'; import { CardModule } from 'primeng/card'; import { AsyncPipe, NgClass, NgIf } from '@angular/common'; @@ -49,6 +55,7 @@ import { TranslateSpecialGradesPipe } from '../../shared/pipes/translate-special templateUrl: './history-list.component.html', styleUrl: './history-list.component.scss', encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush, }) export class HistoryListComponent implements OnInit { public loadingStates = LoadingState; @@ -67,6 +74,7 @@ export class HistoryListComponent implements OnInit { private store: Store, private transloco: TranslocoService, private scalesService: ScalesService, + private cdr: ChangeDetectorRef, ) {} loadFirstPage() { @@ -98,6 +106,7 @@ export class HistoryListComponent implements OnInit { this.hasNextPage = historyItems.hasNext; this.loadingFirstPage = LoadingState.DEFAULT; this.loadingAdditionalPage = LoadingState.DEFAULT; + this.cdr.detectChanges(); }); } } diff --git a/client/src/app/modules/todo/todo-list/todo-list.component.ts b/client/src/app/modules/todo/todo-list/todo-list.component.ts index f5f2639f..283a0acd 100644 --- a/client/src/app/modules/todo/todo-list/todo-list.component.ts +++ b/client/src/app/modules/todo/todo-list/todo-list.component.ts @@ -1,4 +1,6 @@ import { + ChangeDetectionStrategy, + ChangeDetectorRef, Component, HostListener, OnInit, @@ -72,6 +74,7 @@ import { RegionService } from '../../../services/crud/region.service'; templateUrl: './todo-list.component.html', styleUrl: './todo-list.component.scss', encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush, }) @UntilDestroy() export class TodoListComponent implements OnInit { @@ -116,6 +119,7 @@ export class TodoListComponent implements OnInit { private translocoService: TranslocoService, private regionService: RegionService, protected scalesService: ScalesService, + private cdr: ChangeDetectorRef, ) {} ngOnInit() { @@ -263,6 +267,7 @@ export class TodoListComponent implements OnInit { toastNotification(NotificationIdentifier.TODO_DELETED), ); this.todos = this.todos.filter((t) => t.id !== todo.id); + this.cdr.detectChanges(); }); } @@ -331,6 +336,7 @@ export class TodoListComponent implements OnInit { this.hasNextPage = todos.hasNext; this.loadingFirstPage = LoadingState.DEFAULT; this.loadingAdditionalPage = LoadingState.DEFAULT; + this.cdr.detectChanges(); }); } } From d96b2646a9007e2dce9f24afc7299462e3bd4091 Mon Sep 17 00:00:00 2001 From: Felix Engelmann Date: Sun, 16 Feb 2025 16:22:19 +0100 Subject: [PATCH 15/18] Refactor notifications --- .../archive-button.component.ts | 11 +- .../area/area-form/area-form.component.ts | 13 +- .../ascent-form/ascent-form.component.ts | 9 +- .../ascent-list/ascent-list.component.ts | 5 +- .../project-climbed-form.component.ts | 5 +- .../blog/post-form/post-form.component.ts | 13 +- .../account-form/account-form.component.ts | 5 +- .../change-password.component.ts | 5 +- .../instance-settings-form.component.ts | 13 +- .../core/register/register.component.ts | 5 +- .../reset-password.component.ts | 5 +- .../crag/crag-form/crag-form.component.ts | 13 +- .../gallery-form/gallery-form.component.ts | 9 +- .../gallery/gallery/gallery.component.ts | 5 +- .../line-path-form.component.ts | 5 +- .../line/line-form/line-form.component.ts | 13 +- .../map-marker-form-array.component.ts | 9 +- .../menu-items-form.component.ts | 13 +- .../menu-pages-form.component.ts | 13 +- .../region-form/region-form.component.ts | 5 +- .../scale/scale-form/scale-form.component.ts | 25 +- .../sector-form/sector-form.component.ts | 13 +- .../leveled-grade-distribution.component.ts | 2 +- .../todo/todo-button/todo-button.component.ts | 9 +- .../todo/todo-list/todo-list.component.ts | 5 +- .../todo-priority-button.component.ts | 5 +- .../topo-image-form.component.ts | 9 +- .../topo-image-list.component.ts | 9 +- .../user/user-list/user-list.component.ts | 13 +- .../app/ngrx/actions/notifications.actions.ts | 6 +- .../ngrx/effects/app-level-alerts.effects.ts | 7 +- client/src/app/ngrx/effects/auth.effects.ts | 45 +- .../app/ngrx/effects/notifications.effects.ts | 2 +- .../core/app-notifications.service.ts | 392 +----------------- .../services/core/error-handler.service.ts | 15 +- client/src/app/utility/notifications.ts | 388 +++++++++++++++++ .../notification-identifier.enum.ts | 74 ---- .../notifications/notification-type.enum.ts | 9 - 38 files changed, 481 insertions(+), 721 deletions(-) create mode 100644 client/src/app/utility/notifications.ts delete mode 100644 client/src/app/utility/notifications/notification-identifier.enum.ts delete mode 100644 client/src/app/utility/notifications/notification-type.enum.ts diff --git a/client/src/app/modules/archive/archive-button/archive-button.component.ts b/client/src/app/modules/archive/archive-button/archive-button.component.ts index a5ba08df..c416a5b4 100644 --- a/client/src/app/modules/archive/archive-button/archive-button.component.ts +++ b/client/src/app/modules/archive/archive-button/archive-button.component.ts @@ -5,7 +5,6 @@ import { SharedModule } from 'primeng/api'; import { Store } from '@ngrx/store'; import { TranslocoDirective, TranslocoService } from '@jsverse/transloco'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; import { Line } from '../../../models/line'; import { Crag } from '../../../models/crag'; import { Sector } from '../../../models/sector'; @@ -57,19 +56,13 @@ export class ArchiveButtonComponent { const resultHandler = { next: (_) => { this.store.dispatch( - toastNotification( - this.getCurrentState() - ? NotificationIdentifier.ARCHIVED - : NotificationIdentifier.UNARCHIVED, - ), + toastNotification(this.getCurrentState() ? 'ARCHIVED' : 'UNARCHIVED'), ); }, error: () => { this.store.dispatch( toastNotification( - this.getCurrentState() - ? NotificationIdentifier.ARCHIVED_ERROR - : NotificationIdentifier.UNARCHIVED_ERROR, + this.getCurrentState() ? 'ARCHIVED_ERROR' : 'UNARCHIVED_ERROR', ), ); }, diff --git a/client/src/app/modules/area/area-form/area-form.component.ts b/client/src/app/modules/area/area-form/area-form.component.ts index 1ea490d8..a7672fcf 100644 --- a/client/src/app/modules/area/area-form/area-form.component.ts +++ b/client/src/app/modules/area/area-form/area-form.component.ts @@ -16,7 +16,6 @@ import { ConfirmationService, SelectItem } from 'primeng/api'; import { catchError, map } from 'rxjs/operators'; import { forkJoin, of } from 'rxjs'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; import { environment } from '../../../../environments/environment'; import { marker } from '@jsverse/transloco-keys-manager/marker'; import { Area } from '../../../models/area'; @@ -227,9 +226,7 @@ export class AreaFormComponent implements OnInit { if (this.area) { area.slug = this.area.slug; this.areasService.updateArea(area).subscribe((area) => { - this.store.dispatch( - toastNotification(NotificationIdentifier.AREA_UPDATED), - ); + this.store.dispatch(toastNotification('AREA_UPDATED')); this.router.navigate([ '/topo', this.cragSlug, @@ -240,9 +237,7 @@ export class AreaFormComponent implements OnInit { }); } else { this.areasService.createArea(area, this.sectorSlug).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.AREA_CREATED), - ); + this.store.dispatch(toastNotification('AREA_CREATED')); this.router.navigate([ '/topo', this.cragSlug, @@ -286,9 +281,7 @@ export class AreaFormComponent implements OnInit { */ public deleteArea() { this.areasService.deleteArea(this.area).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.AREA_DELETED), - ); + this.store.dispatch(toastNotification('AREA_DELETED')); this.router.navigate(['/topo', this.cragSlug, this.sectorSlug, 'areas']); this.loadingState = LoadingState.DEFAULT; }); diff --git a/client/src/app/modules/ascent/ascent-form/ascent-form.component.ts b/client/src/app/modules/ascent/ascent-form/ascent-form.component.ts index 576848a4..9e730369 100644 --- a/client/src/app/modules/ascent/ascent-form/ascent-form.component.ts +++ b/client/src/app/modules/ascent/ascent-form/ascent-form.component.ts @@ -22,7 +22,6 @@ import { Line } from '../../../models/line'; import { Store } from '@ngrx/store'; import { yearOfDateNotInFutureValidator } from '../../../utility/validators/year-not-in-future.validator'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; import { Ascent } from '../../../models/ascent'; import { AscentsService } from '../../../services/crud/ascents.service'; import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; @@ -223,9 +222,7 @@ export class AscentFormComponent implements OnInit { ascent.line = this.line; if (!this.editMode) { this.ascentsService.createAscent(ascent).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.ASCENT_ADDED), - ); + this.store.dispatch(toastNotification('ASCENT_ADDED')); this.loadingState = LoadingState.DEFAULT; this.store.dispatch( reloadAfterAscent({ ascendedLineId: this.line.id }), @@ -235,9 +232,7 @@ export class AscentFormComponent implements OnInit { } else { ascent.id = this.ascent.id; this.ascentsService.updateAscent(ascent).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.ASCENT_UPDATED), - ); + this.store.dispatch(toastNotification('ASCENT_UPDATED')); this.loadingState = LoadingState.DEFAULT; this.store.dispatch( reloadAfterAscent({ ascendedLineId: this.line.id }), diff --git a/client/src/app/modules/ascent/ascent-list/ascent-list.component.ts b/client/src/app/modules/ascent/ascent-list/ascent-list.component.ts index 0d571d16..ffe82c90 100644 --- a/client/src/app/modules/ascent/ascent-list/ascent-list.component.ts +++ b/client/src/app/modules/ascent/ascent-list/ascent-list.component.ts @@ -41,7 +41,6 @@ import { AscentFormComponent } from '../ascent-form/ascent-form.component'; import { AscentFormTitleComponent } from '../ascent-form-title/ascent-form-title.component'; import { environment } from '../../../../environments/environment'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; import { reloadAfterAscent } from '../../../ngrx/actions/ascent.actions'; import { Actions, ofType } from '@ngrx/effects'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; @@ -337,9 +336,7 @@ export class AscentListComponent implements OnInit { public deleteAscent(ascent: Ascent) { this.ascentsService.deleteAscent(ascent).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.ASCENT_DELETED), - ); + this.store.dispatch(toastNotification('ASCENT_DELETED')); this.store.dispatch( reloadAfterAscent({ ascendedLineId: ascent.line.id }), ); diff --git a/client/src/app/modules/ascent/project-climbed-form/project-climbed-form.component.ts b/client/src/app/modules/ascent/project-climbed-form/project-climbed-form.component.ts index e4917157..3a6400fa 100644 --- a/client/src/app/modules/ascent/project-climbed-form/project-climbed-form.component.ts +++ b/client/src/app/modules/ascent/project-climbed-form/project-climbed-form.component.ts @@ -18,7 +18,6 @@ import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; import { Store } from '@ngrx/store'; import { AscentsService } from '../../../services/crud/ascents.service'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; import { TranslocoDirective } from '@jsverse/transloco'; @Component({ @@ -75,9 +74,7 @@ export class ProjectClimbedFormComponent implements OnInit { .sendProjectClimbedMessage(message, this.line.id) .subscribe(() => { this.store.dispatch( - toastNotification( - NotificationIdentifier.PROJECT_CLIMBED_MESSAGE_SENT, - ), + toastNotification('PROJECT_CLIMBED_MESSAGE_SENT'), ); this.loadingState = LoadingState.DEFAULT; this.ref.close(); diff --git a/client/src/app/modules/blog/post-form/post-form.component.ts b/client/src/app/modules/blog/post-form/post-form.component.ts index 7e51d92c..38514dfc 100644 --- a/client/src/app/modules/blog/post-form/post-form.component.ts +++ b/client/src/app/modules/blog/post-form/post-form.component.ts @@ -17,7 +17,6 @@ import { of } from 'rxjs'; import { marker } from '@jsverse/transloco-keys-manager/marker'; import { environment } from '../../../../environments/environment'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; import { Post } from '../../../models/post'; import { PostsService } from '../../../services/crud/posts.service'; import { ButtonModule } from 'primeng/button'; @@ -158,17 +157,13 @@ export class PostFormComponent implements OnInit { if (this.post) { post.slug = this.post.slug; this.postsService.updatePost(post).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.POST_UPDATED), - ); + this.store.dispatch(toastNotification('POST_UPDATED')); this.router.navigate(['/news']); this.loadingState = LoadingState.DEFAULT; }); } else { this.postsService.createPost(post).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.POST_CREATED), - ); + this.store.dispatch(toastNotification('POST_CREATED')); this.router.navigate(['/news']); this.loadingState = LoadingState.DEFAULT; }); @@ -207,9 +202,7 @@ export class PostFormComponent implements OnInit { */ public deletePost() { this.postsService.deletePost(this.post).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.POST_DELETED), - ); + this.store.dispatch(toastNotification('POST_DELETED')); this.router.navigate(['/news']); this.loadingState = LoadingState.DEFAULT; }); diff --git a/client/src/app/modules/core/account-form/account-form.component.ts b/client/src/app/modules/core/account-form/account-form.component.ts index 3f064b81..998ab201 100644 --- a/client/src/app/modules/core/account-form/account-form.component.ts +++ b/client/src/app/modules/core/account-form/account-form.component.ts @@ -19,7 +19,6 @@ import { selectInstanceName } from '../../../ngrx/selectors/instance-settings.se import { marker } from '@jsverse/transloco-keys-manager/marker'; import { User } from '../../../models/user'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; import { selectCurrentUser } from '../../../ngrx/selectors/auth.selectors'; import { take } from 'rxjs/operators'; import { AvatarUploadComponent } from '../../shared/forms/controls/avatar-upload/avatar-upload.component'; @@ -94,9 +93,7 @@ export class AccountFormComponent implements OnInit { user.avatar = this.accountForm.get('avatar').value; this.usersService.updateAccount(user).subscribe((updatedUser) => { this.store.dispatch(updateAccountSettings({ user: updatedUser })); - this.store.dispatch( - toastNotification(NotificationIdentifier.ACCOUNT_SETTINGS_UPDATED), - ); + this.store.dispatch(toastNotification('ACCOUNT_SETTINGS_UPDATED')); this.loadingState = LoadingState.DEFAULT; this.emailChangedPostSave = emailChanged; }); diff --git a/client/src/app/modules/core/change-password/change-password.component.ts b/client/src/app/modules/core/change-password/change-password.component.ts index 600ca756..d90e2c0e 100644 --- a/client/src/app/modules/core/change-password/change-password.component.ts +++ b/client/src/app/modules/core/change-password/change-password.component.ts @@ -10,7 +10,6 @@ import { Store } from '@ngrx/store'; import { AppState } from '../../../ngrx/reducers'; import { AuthCrudService } from '../../../services/crud/auth-crud.service'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; import { passwordsValidator } from '../../../utility/validators/passwords.validator'; import { FormDirective } from '../../shared/forms/form.directive'; import { Router } from '@angular/router'; @@ -73,9 +72,7 @@ export class ChangePasswordComponent implements OnInit { .subscribe( () => { this.loading = false; - this.store.dispatch( - toastNotification(NotificationIdentifier.CHANGE_PASSWORD_SUCCESS), - ); + this.store.dispatch(toastNotification('CHANGE_PASSWORD_SUCCESS')); this.router.navigate(['']); }, () => { diff --git a/client/src/app/modules/core/instance-settings-form/instance-settings-form.component.ts b/client/src/app/modules/core/instance-settings-form/instance-settings-form.component.ts index 34318e04..034ac278 100644 --- a/client/src/app/modules/core/instance-settings-form/instance-settings-form.component.ts +++ b/client/src/app/modules/core/instance-settings-form/instance-settings-form.component.ts @@ -14,7 +14,6 @@ import { TranslocoDirective } from '@jsverse/transloco'; import { catchError } from 'rxjs/operators'; import { of } from 'rxjs'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; import { InstanceSettings } from '../../../models/instance-settings'; import { InstanceSettingsService } from '../../../services/crud/instance-settings.service'; import { ButtonModule } from 'primeng/button'; @@ -183,11 +182,7 @@ export class InstanceSettingsFormComponent implements OnInit { .updateInstanceSettings(instanceSettings) .subscribe({ next: (instanceSettings) => { - this.store.dispatch( - toastNotification( - NotificationIdentifier.INSTANCE_SETTINGS_UPDATED, - ), - ); + this.store.dispatch(toastNotification('INSTANCE_SETTINGS_UPDATED')); this.loadingState = LoadingState.DEFAULT; this.store.dispatch( updateInstanceSettings({ settings: instanceSettings }), @@ -198,13 +193,11 @@ export class InstanceSettingsFormComponent implements OnInit { if (e.error?.message == 'MIGRATION_IMPOSSIBLE') { this.store.dispatch( toastNotification( - NotificationIdentifier.INSTANCE_SETTINGS_ERROR_MIGRATION_IMPOSSIBLE, + 'INSTANCE_SETTINGS_ERROR_MIGRATION_IMPOSSIBLE', ), ); } else { - this.store.dispatch( - toastNotification(NotificationIdentifier.UNKNOWN_ERROR), - ); + this.store.dispatch(toastNotification('UNKNOWN_ERROR')); } }, }); diff --git a/client/src/app/modules/core/register/register.component.ts b/client/src/app/modules/core/register/register.component.ts index e0a06c53..62537d81 100644 --- a/client/src/app/modules/core/register/register.component.ts +++ b/client/src/app/modules/core/register/register.component.ts @@ -18,7 +18,6 @@ import { Store } from '@ngrx/store'; import { AppState } from '../../../ngrx/reducers'; import { User } from '../../../models/user'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; import { UsersService } from '../../../services/crud/users.service'; import { selectInstanceName } from '../../../ngrx/selectors/instance-settings.selectors'; import { marker } from '@jsverse/transloco-keys-manager/marker'; @@ -86,9 +85,7 @@ export class RegisterComponent implements OnInit { this.registrationForm.get('emails.email').value as string ).toLowerCase(); this.usersService.registerUser(user).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.USER_REGISTERED), - ); + this.store.dispatch(toastNotification('USER_REGISTERED')); this.router.navigate(['/register-check-mailbox']); this.loadingState = LoadingState.DEFAULT; }); diff --git a/client/src/app/modules/core/reset-password/reset-password.component.ts b/client/src/app/modules/core/reset-password/reset-password.component.ts index efca6cd0..0a96f1d4 100644 --- a/client/src/app/modules/core/reset-password/reset-password.component.ts +++ b/client/src/app/modules/core/reset-password/reset-password.component.ts @@ -18,7 +18,6 @@ import { } from '../../../ngrx/selectors/auth.selectors'; import { take } from 'rxjs/operators'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; import { resetPassword } from 'src/app/ngrx/actions/auth.actions'; import { passwordsValidator } from '../../../utility/validators/passwords.validator'; import { Title } from '@angular/platform-browser'; @@ -64,9 +63,7 @@ export class ResetPasswordComponent implements OnInit, OnDestroy { if (isLoggedIn) { this.router.navigate(['/']); this.store.dispatch( - toastNotification( - NotificationIdentifier.LOG_OUT_TO_USE_THIS_FUNCTION, - ), + toastNotification('LOG_OUT_TO_USE_THIS_FUNCTION'), ); } }); diff --git a/client/src/app/modules/crag/crag-form/crag-form.component.ts b/client/src/app/modules/crag/crag-form/crag-form.component.ts index 0d3fbcc8..cf7e77ce 100644 --- a/client/src/app/modules/crag/crag-form/crag-form.component.ts +++ b/client/src/app/modules/crag/crag-form/crag-form.component.ts @@ -13,7 +13,6 @@ import { Crag } from '../../../models/crag'; import { environment } from '../../../../environments/environment'; import { Store } from '@ngrx/store'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; import { ActivatedRoute, Router } from '@angular/router'; import { forkJoin, of } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; @@ -211,17 +210,13 @@ export class CragFormComponent implements OnInit { if (this.crag) { crag.slug = this.crag.slug; this.cragsService.updateCrag(crag).subscribe((crag) => { - this.store.dispatch( - toastNotification(NotificationIdentifier.CRAG_UPDATED), - ); + this.store.dispatch(toastNotification('CRAG_UPDATED')); this.router.navigate(['/topo', crag.slug]); this.loadingState = LoadingState.DEFAULT; }); } else { this.cragsService.createCrag(crag).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.CRAG_CREATED), - ); + this.store.dispatch(toastNotification('CRAG_CREATED')); this.router.navigate(['/topo']); this.loadingState = LoadingState.DEFAULT; }); @@ -260,9 +255,7 @@ export class CragFormComponent implements OnInit { */ public deleteCrag() { this.cragsService.deleteCrag(this.crag).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.CRAG_DELETED), - ); + this.store.dispatch(toastNotification('CRAG_DELETED')); this.router.navigate(['/topo']); this.loadingState = LoadingState.DEFAULT; }); diff --git a/client/src/app/modules/gallery/gallery-form/gallery-form.component.ts b/client/src/app/modules/gallery/gallery-form/gallery-form.component.ts index 9a4eef06..8792ae56 100644 --- a/client/src/app/modules/gallery/gallery-form/gallery-form.component.ts +++ b/client/src/app/modules/gallery/gallery-form/gallery-form.component.ts @@ -24,7 +24,6 @@ import { SearchService } from '../../../services/crud/search.service'; import { ObjectType, Tag } from '../../../models/tag'; import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; import { Store } from '@ngrx/store'; import { EMPTY, Observable } from 'rxjs'; import { LinesService } from '../../../services/crud/lines.service'; @@ -172,9 +171,7 @@ export class GalleryFormComponent implements OnInit { .subscribe((galleryImage) => { this.loadingState = LoadingState.DEFAULT; this.ref.close(galleryImage); - this.store.dispatch( - toastNotification(NotificationIdentifier.GALLERY_IMAGE_UPDATED), - ); + this.store.dispatch(toastNotification('GALLERY_IMAGE_UPDATED')); }); } else { this.galleryService @@ -182,9 +179,7 @@ export class GalleryFormComponent implements OnInit { .subscribe((galleryImage) => { this.loadingState = LoadingState.DEFAULT; this.ref.close(galleryImage); - this.store.dispatch( - toastNotification(NotificationIdentifier.GALLERY_IMAGE_CREATED), - ); + this.store.dispatch(toastNotification('GALLERY_IMAGE_CREATED')); }); } } else { diff --git a/client/src/app/modules/gallery/gallery/gallery.component.ts b/client/src/app/modules/gallery/gallery/gallery.component.ts index 8d259995..e5de327c 100644 --- a/client/src/app/modules/gallery/gallery/gallery.component.ts +++ b/client/src/app/modules/gallery/gallery/gallery.component.ts @@ -22,7 +22,6 @@ import { HasPermissionDirective } from '../../shared/directives/has-permission.d import { ConfirmPopupModule } from 'primeng/confirmpopup'; import { environment } from '../../../../environments/environment'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; import { Store } from '@ngrx/store'; import { ConfirmationService } from 'primeng/api'; import { GalleryImageSkeletonComponent } from '../gallery-image-skeleton/gallery-image-skeleton.component'; @@ -198,9 +197,7 @@ export class GalleryComponent implements OnInit { deleteImage(image: GalleryImage) { this.galleryService.deleteGalleryImage(image.id).subscribe(() => { this.images = this.images.filter((i) => i.id !== image.id); - this.store.dispatch( - toastNotification(NotificationIdentifier.GALLERY_IMAGE_DELETED), - ); + this.store.dispatch(toastNotification('GALLERY_IMAGE_DELETED')); this.cdr.detectChanges(); }); } diff --git a/client/src/app/modules/line-path-editor/line-path-form/line-path-form.component.ts b/client/src/app/modules/line-path-editor/line-path-form/line-path-form.component.ts index 6da183de..517d1317 100644 --- a/client/src/app/modules/line-path-editor/line-path-form/line-path-form.component.ts +++ b/client/src/app/modules/line-path-editor/line-path-form/line-path-form.component.ts @@ -5,7 +5,6 @@ import { LoadingState } from '../../../enums/loading-state'; import { Store } from '@ngrx/store'; import { ActivatedRoute, Router } from '@angular/router'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; import { LinePath } from '../../../models/line-path'; import { LinePathsService } from '../../../services/crud/line-paths.service'; import { LinesService } from '../../../services/crud/lines.service'; @@ -128,9 +127,7 @@ export class LinePathFormComponent implements OnInit { this.linePathsService .addLinePath(linePath, this.topoImageId) .subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.LINE_PATH_ADDED), - ); + this.store.dispatch(toastNotification('LINE_PATH_ADDED')); // this.router.navigate(['/topo', this.cragSlug, this.sectorSlug, this.areaSlug, 'topo-images']); this.loadingState = LoadingState.DEFAULT; this.refreshData(); diff --git a/client/src/app/modules/line/line-form/line-form.component.ts b/client/src/app/modules/line/line-form/line-form.component.ts index 95f8d584..bf7727a5 100644 --- a/client/src/app/modules/line/line-form/line-form.component.ts +++ b/client/src/app/modules/line/line-form/line-form.component.ts @@ -9,7 +9,6 @@ import { ConfirmationService } from 'primeng/api'; import { catchError } from 'rxjs/operators'; import { forkJoin, of } from 'rxjs'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; import { environment } from '../../../../environments/environment'; import { marker } from '@jsverse/transloco-keys-manager/marker'; import { Line } from '../../../models/line'; @@ -468,9 +467,7 @@ export class LineFormComponent implements OnInit { if (this.line) { line.slug = this.line.slug; this.linesService.updateLine(line).subscribe((line) => { - this.store.dispatch( - toastNotification(NotificationIdentifier.LINE_UPDATED), - ); + this.store.dispatch(toastNotification('LINE_UPDATED')); this.router.navigate([ '/topo', this.cragSlug, @@ -482,9 +479,7 @@ export class LineFormComponent implements OnInit { }); } else { this.linesService.createLine(line, this.areaSlug).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.LINE_CREATED), - ); + this.store.dispatch(toastNotification('LINE_CREATED')); this.router.navigate([ '/topo', this.cragSlug, @@ -529,9 +524,7 @@ export class LineFormComponent implements OnInit { */ public deleteLine() { this.linesService.deleteLine(this.line).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.LINE_DELETED), - ); + this.store.dispatch(toastNotification('LINE_DELETED')); this.router.navigate([ '/topo', this.cragSlug, diff --git a/client/src/app/modules/maps/map-marker-form-array/map-marker-form-array.component.ts b/client/src/app/modules/maps/map-marker-form-array/map-marker-form-array.component.ts index c4abc6d9..dec86c85 100644 --- a/client/src/app/modules/maps/map-marker-form-array/map-marker-form-array.component.ts +++ b/client/src/app/modules/maps/map-marker-form-array/map-marker-form-array.component.ts @@ -16,7 +16,6 @@ import { marker as translocoMarker } from '@jsverse/transloco-keys-manager/marke import { MapMarkerType } from '../../../enums/map-marker-type'; import { Store } from '@ngrx/store'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; @Component({ selector: 'lc-map-marker-form-array', @@ -75,17 +74,13 @@ export class MapMarkerFormArrayComponent implements ControlValueAccessor { addMarker(marker: MapMarker) { this.markers.push(marker); this.onChange(); - this.store.dispatch( - toastNotification(NotificationIdentifier.MAP_MARKER_ADDED), - ); + this.store.dispatch(toastNotification('MAP_MARKER_ADDED')); } removeMarker(marker: MapMarker) { this.markers.splice(this.markers.indexOf(marker), 1); this.onChange(); - this.store.dispatch( - toastNotification(NotificationIdentifier.MAP_MARKER_REMOVED), - ); + this.store.dispatch(toastNotification('MAP_MARKER_REMOVED')); } setDisabledState(isDisabled: boolean) { diff --git a/client/src/app/modules/menu-pages/menu-items-form/menu-items-form.component.ts b/client/src/app/modules/menu-pages/menu-items-form/menu-items-form.component.ts index 3884e262..d0390a94 100644 --- a/client/src/app/modules/menu-pages/menu-items-form/menu-items-form.component.ts +++ b/client/src/app/modules/menu-pages/menu-items-form/menu-items-form.component.ts @@ -23,7 +23,6 @@ import { environment } from '../../../../environments/environment'; import { catchError } from 'rxjs/operators'; import { forkJoin, Observable, of } from 'rxjs'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; import { MenuItem } from '../../../models/menu-item'; import { MenuItemsService } from '../../../services/crud/menu-items.service'; import { MenuItemType } from '../../../enums/menu-item-type'; @@ -266,18 +265,14 @@ export class MenuItemsFormComponent implements OnInit { if (this.menuItem) { menuItem.id = this.menuItem.id; this.menuItemsService.updateMenuItem(menuItem).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.MENU_ITEM_UPDATED), - ); + this.store.dispatch(toastNotification('MENU_ITEM_UPDATED')); this.router.navigate(['/menu-items']); this.loadingState = LoadingState.DEFAULT; this.store.dispatch(reloadMenus()); }); } else { this.menuItemsService.createMenuItem(menuItem).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.MENU_ITEM_CREATED), - ); + this.store.dispatch(toastNotification('MENU_ITEM_CREATED')); this.router.navigate(['/menu-items']); this.loadingState = LoadingState.DEFAULT; this.store.dispatch(reloadMenus()); @@ -319,9 +314,7 @@ export class MenuItemsFormComponent implements OnInit { */ public deleteMenuItem() { this.menuItemsService.deleteMenuItem(this.menuItem).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.MENU_ITEM_DELETED), - ); + this.store.dispatch(toastNotification('MENU_ITEM_DELETED')); this.store.dispatch(reloadMenus()); this.router.navigate(['/menu-items']); this.loadingState = LoadingState.DEFAULT; diff --git a/client/src/app/modules/menu-pages/menu-pages-form/menu-pages-form.component.ts b/client/src/app/modules/menu-pages/menu-pages-form/menu-pages-form.component.ts index 703cd2e0..19b6aeb1 100644 --- a/client/src/app/modules/menu-pages/menu-pages-form/menu-pages-form.component.ts +++ b/client/src/app/modules/menu-pages/menu-pages-form/menu-pages-form.component.ts @@ -19,7 +19,6 @@ import { environment } from '../../../../environments/environment'; import { catchError } from 'rxjs/operators'; import { of } from 'rxjs'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; import { MenuPage } from '../../../models/menu-page'; import { MenuPagesService } from '../../../services/crud/menu-pages.service'; import { ButtonModule } from 'primeng/button'; @@ -158,18 +157,14 @@ export class MenuPagesFormComponent implements OnInit { if (this.menuPage) { menuPage.slug = this.menuPage.slug; this.menuPagesService.updateMenuPage(menuPage).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.MENU_PAGE_UPDATED), - ); + this.store.dispatch(toastNotification('MENU_PAGE_UPDATED')); this.router.navigate(['/pages']); this.loadingState = LoadingState.DEFAULT; this.store.dispatch(reloadMenus()); }); } else { this.menuPagesService.createMenuPage(menuPage).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.MENU_PAGE_CREATED), - ); + this.store.dispatch(toastNotification('MENU_PAGE_CREATED')); this.router.navigate(['/pages']); this.loadingState = LoadingState.DEFAULT; this.store.dispatch(reloadMenus()); @@ -211,9 +206,7 @@ export class MenuPagesFormComponent implements OnInit { */ public deleteMenuPage() { this.menuPagesService.deleteMenuPage(this.menuPage).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.MENU_PAGE_DELETED), - ); + this.store.dispatch(toastNotification('MENU_PAGE_DELETED')); this.store.dispatch(reloadMenus()); this.router.navigate(['/pages']); this.loadingState = LoadingState.DEFAULT; diff --git a/client/src/app/modules/region/region-form/region-form.component.ts b/client/src/app/modules/region/region-form/region-form.component.ts index 57a31cf9..879b1993 100644 --- a/client/src/app/modules/region/region-form/region-form.component.ts +++ b/client/src/app/modules/region/region-form/region-form.component.ts @@ -21,7 +21,6 @@ import { TranslocoDirective } from '@jsverse/transloco'; import { catchError } from 'rxjs/operators'; import { of } from 'rxjs'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; import { RegionService } from '../../../services/crud/region.service'; import { Region } from '../../../models/region'; import { CardModule } from 'primeng/card'; @@ -138,9 +137,7 @@ export class RegionFormComponent implements OnInit { region.description = this.regionForm.get('description').value; region.rules = this.regionForm.get('rules').value; this.regionsService.updateRegion(region).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.REGION_UPDATED), - ); + this.store.dispatch(toastNotification('REGION_UPDATED')); this.router.navigate(['/topo']); this.loadingState = LoadingState.DEFAULT; }); diff --git a/client/src/app/modules/scale/scale-form/scale-form.component.ts b/client/src/app/modules/scale/scale-form/scale-form.component.ts index 31be4304..c7f9aaf0 100644 --- a/client/src/app/modules/scale/scale-form/scale-form.component.ts +++ b/client/src/app/modules/scale/scale-form/scale-form.component.ts @@ -36,7 +36,6 @@ import { ConfirmationService } from 'primeng/api'; import { environment } from '../../../../environments/environment'; import { marker } from '@jsverse/transloco-keys-manager/marker'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; import { Store } from '@ngrx/store'; import { DropdownModule } from 'primeng/dropdown'; import { MessageModule } from 'primeng/message'; @@ -342,16 +341,12 @@ export class ScaleFormComponent implements OnInit { }; this.scalesService.updateScale(this.scale).subscribe({ next: () => { - this.store.dispatch( - toastNotification(NotificationIdentifier.SCALE_UPDATED), - ); + this.store.dispatch(toastNotification('SCALE_UPDATED')); this.loadingState = LoadingState.DEFAULT; this.scaleForm.enable(); }, error: () => { - this.store.dispatch( - toastNotification(NotificationIdentifier.SCALE_UPDATED_ERROR), - ); + this.store.dispatch(toastNotification('SCALE_UPDATED_ERROR')); this.loadingState = LoadingState.DEFAULT; this.scaleForm.enable(); }, @@ -366,17 +361,13 @@ export class ScaleFormComponent implements OnInit { ); this.scalesService.createScale(scale).subscribe({ next: () => { - this.store.dispatch( - toastNotification(NotificationIdentifier.SCALE_CREATED), - ); + this.store.dispatch(toastNotification('SCALE_CREATED')); this.loadingState = LoadingState.DEFAULT; this.scaleForm.enable(); this.router.navigate(['/scales']); }, error: () => { - this.store.dispatch( - toastNotification(NotificationIdentifier.SCALE_CREATED_ERROR), - ); + this.store.dispatch(toastNotification('SCALE_CREATED_ERROR')); this.loadingState = LoadingState.DEFAULT; this.scaleForm.enable(); }, @@ -410,15 +401,11 @@ export class ScaleFormComponent implements OnInit { this.scalesService.deleteScale(this.scale).subscribe({ next: () => { this.router.navigate(['/scales']); - this.store.dispatch( - toastNotification(NotificationIdentifier.SCALE_DELETED), - ); + this.store.dispatch(toastNotification('SCALE_DELETED')); this.loadingState = LoadingState.DEFAULT; }, error: () => { - this.store.dispatch( - toastNotification(NotificationIdentifier.SCALE_DELETED_ERROR), - ); + this.store.dispatch(toastNotification('SCALE_DELETED_ERROR')); this.loadingState = LoadingState.DEFAULT; }, }); diff --git a/client/src/app/modules/sector/sector-form/sector-form.component.ts b/client/src/app/modules/sector/sector-form/sector-form.component.ts index 273acd84..b4d692c9 100644 --- a/client/src/app/modules/sector/sector-form/sector-form.component.ts +++ b/client/src/app/modules/sector/sector-form/sector-form.component.ts @@ -15,7 +15,6 @@ import { ConfirmationService, SelectItem } from 'primeng/api'; import { catchError, map } from 'rxjs/operators'; import { forkJoin, of } from 'rxjs'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; import { environment } from '../../../../environments/environment'; import { marker } from '@jsverse/transloco-keys-manager/marker'; import { Sector } from '../../../models/sector'; @@ -226,9 +225,7 @@ export class SectorFormComponent implements OnInit { if (this.sector) { sector.slug = this.sector.slug; this.sectorsService.updateSector(sector).subscribe((sector) => { - this.store.dispatch( - toastNotification(NotificationIdentifier.SECTOR_UPDATED), - ); + this.store.dispatch(toastNotification('SECTOR_UPDATED')); this.router.navigate(['/topo', this.cragSlug, sector.slug]); this.loadingState = LoadingState.DEFAULT; }); @@ -236,9 +233,7 @@ export class SectorFormComponent implements OnInit { this.sectorsService .createSector(sector, this.cragSlug) .subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.SECTOR_CREATED), - ); + this.store.dispatch(toastNotification('SECTOR_CREATED')); this.router.navigate(['/topo', this.cragSlug, 'sectors']); this.loadingState = LoadingState.DEFAULT; }); @@ -279,9 +274,7 @@ export class SectorFormComponent implements OnInit { */ public deleteSector() { this.sectorsService.deleteSector(this.sector).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.SECTOR_DELETED), - ); + this.store.dispatch(toastNotification('SECTOR_DELETED')); this.router.navigate(['/topo', this.cragSlug, 'sectors']); this.loadingState = LoadingState.DEFAULT; }); diff --git a/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.ts b/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.ts index 1d04c08c..07686984 100644 --- a/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.ts +++ b/client/src/app/modules/shared/components/leveled-grade-distribution/leveled-grade-distribution.component.ts @@ -26,7 +26,7 @@ type StackChartData = { export class LeveledGradeDistributionComponent implements OnInit { @Input() fetchingObservable: Observable; - public stackChartData = null; + public stackChartData: any = null; public gradeDistribution: GradeDistribution; public gradeDistributionEmpty = true; value = [ diff --git a/client/src/app/modules/todo/todo-button/todo-button.component.ts b/client/src/app/modules/todo/todo-button/todo-button.component.ts index 4e0dca7d..71450ea0 100644 --- a/client/src/app/modules/todo/todo-button/todo-button.component.ts +++ b/client/src/app/modules/todo/todo-button/todo-button.component.ts @@ -4,7 +4,6 @@ import { TranslocoDirective } from '@jsverse/transloco'; import { Store } from '@ngrx/store'; import { TodosService } from '../../../services/crud/todos.service'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; import { ButtonModule } from 'primeng/button'; import { NgClass, NgIf } from '@angular/common'; import { SharedModule } from 'primeng/api'; @@ -37,14 +36,10 @@ export class TodoButtonComponent { this.todosService.createTodo(this.line).subscribe( () => { this.store.dispatch(todoAdded({ todoLineId: this.line.id })); - this.store.dispatch( - toastNotification(NotificationIdentifier.TODO_ADDED), - ); + this.store.dispatch(toastNotification('TODO_ADDED')); }, () => { - this.store.dispatch( - toastNotification(NotificationIdentifier.TODO_ADD_ERROR), - ); + this.store.dispatch(toastNotification('TODO_ADD_ERROR')); }, ); } else { diff --git a/client/src/app/modules/todo/todo-list/todo-list.component.ts b/client/src/app/modules/todo/todo-list/todo-list.component.ts index 283a0acd..c15601d0 100644 --- a/client/src/app/modules/todo/todo-list/todo-list.component.ts +++ b/client/src/app/modules/todo/todo-list/todo-list.component.ts @@ -38,7 +38,6 @@ import { TickButtonComponent } from '../../ascent/tick-button/tick-button.compon import { MenuItemsService } from '../../../services/crud/menu-items.service'; import { Crag } from '../../../models/crag'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; import { ScalesService } from '../../../services/crud/scales.service'; import { LineType } from '../../../enums/line-type'; import { SharedModule } from '../../shared/shared.module'; @@ -263,9 +262,7 @@ export class TodoListComponent implements OnInit { deleteTodo(todo: Todo) { this.todosService.deleteTodo(todo).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.TODO_DELETED), - ); + this.store.dispatch(toastNotification('TODO_DELETED')); this.todos = this.todos.filter((t) => t.id !== todo.id); this.cdr.detectChanges(); }); diff --git a/client/src/app/modules/todo/todo-priority-button/todo-priority-button.component.ts b/client/src/app/modules/todo/todo-priority-button/todo-priority-button.component.ts index 821bcad2..0be2d260 100644 --- a/client/src/app/modules/todo/todo-priority-button/todo-priority-button.component.ts +++ b/client/src/app/modules/todo/todo-priority-button/todo-priority-button.component.ts @@ -10,7 +10,6 @@ import { marker } from '@jsverse/transloco-keys-manager/marker'; import { TodosService } from '../../../services/crud/todos.service'; import { Store } from '@ngrx/store'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; @Component({ selector: 'lc-todo-priority-button', @@ -37,9 +36,7 @@ export class TodoPriorityButtonComponent implements OnInit { .updateTodoPriority(this.todo, priority) .subscribe((todo) => { this.todo.priority = todo.priority; - this.store.dispatch( - toastNotification(NotificationIdentifier.TODO_PRIORITY_UPDATED), - ); + this.store.dispatch(toastNotification('TODO_PRIORITY_UPDATED')); }); } diff --git a/client/src/app/modules/topo-images/topo-image-form/topo-image-form.component.ts b/client/src/app/modules/topo-images/topo-image-form/topo-image-form.component.ts index 99955fb1..56d88004 100644 --- a/client/src/app/modules/topo-images/topo-image-form/topo-image-form.component.ts +++ b/client/src/app/modules/topo-images/topo-image-form/topo-image-form.component.ts @@ -14,7 +14,6 @@ import { TranslocoService } from '@jsverse/transloco'; import { catchError } from 'rxjs/operators'; import { of } from 'rxjs'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; import { marker } from '@jsverse/transloco-keys-manager/marker'; import { TopoImage } from '../../../models/topo-image'; import { TopoImagesService } from '../../../services/crud/topo-images.service'; @@ -149,9 +148,7 @@ export class TopoImageFormComponent implements OnInit { if (this.topoImage) { topoImage.id = this.topoImage.id; this.topoImagesService.updateTopoImage(topoImage).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.TOPO_IMAGE_UPDATED), - ); + this.store.dispatch(toastNotification('TOPO_IMAGE_UPDATED')); this.router.navigate([ '/topo', this.cragSlug, @@ -165,9 +162,7 @@ export class TopoImageFormComponent implements OnInit { this.topoImagesService .addTopoImage(topoImage, this.areaSlug) .subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.TOPO_IMAGE_ADDED), - ); + this.store.dispatch(toastNotification('TOPO_IMAGE_ADDED')); this.router.navigate([ '/topo', this.cragSlug, diff --git a/client/src/app/modules/topo-images/topo-image-list/topo-image-list.component.ts b/client/src/app/modules/topo-images/topo-image-list/topo-image-list.component.ts index 82b9282c..ceed1202 100644 --- a/client/src/app/modules/topo-images/topo-image-list/topo-image-list.component.ts +++ b/client/src/app/modules/topo-images/topo-image-list/topo-image-list.component.ts @@ -11,7 +11,6 @@ import { selectIsMobile } from '../../../ngrx/selectors/device.selectors'; import { TopoImage } from '../../../models/topo-image'; import { TopoImagesService } from '../../../services/crud/topo-images.service'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; import { LinePath } from '../../../models/line-path'; import { LinePathsService } from '../../../services/crud/line-paths.service'; import { DialogService, DynamicDialogRef } from 'primeng/dynamicdialog'; @@ -248,9 +247,7 @@ export class TopoImageListComponent implements OnInit { this.topoImagesService .deleteTopoImage(this.areaSlug, topoImage) .subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.TOPO_IMAGE_DELETED), - ); + this.store.dispatch(toastNotification('TOPO_IMAGE_DELETED')); this.topoImages.splice(this.topoImages.indexOf(topoImage), 1); this.topoImages = [...this.topoImages]; topoImage.loadingState = LoadingState.DEFAULT; @@ -299,9 +296,7 @@ export class TopoImageListComponent implements OnInit { public deleteLinePath(linePath: LinePath, topoImage: TopoImage) { linePath.loadingState = LoadingState.LOADING; this.linePathsService.deleteLinePath(linePath).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.LINE_PATH_DELETED), - ); + this.store.dispatch(toastNotification('LINE_PATH_DELETED')); topoImage.linePaths.splice(topoImage.linePaths.indexOf(linePath), 1); linePath.konvaLine.destroy(); linePath.konvaRect.destroy(); diff --git a/client/src/app/modules/user/user-list/user-list.component.ts b/client/src/app/modules/user/user-list/user-list.component.ts index 7b5c6563..301442d0 100644 --- a/client/src/app/modules/user/user-list/user-list.component.ts +++ b/client/src/app/modules/user/user-list/user-list.component.ts @@ -36,7 +36,6 @@ import { ChipModule } from 'primeng/chip'; import { MenuModule } from 'primeng/menu'; import { take } from 'rxjs/operators'; import { toastNotification } from '../../../ngrx/actions/notifications.actions'; -import { NotificationIdentifier } from '../../../utility/notifications/notification-identifier.enum'; import { environment } from '../../../../environments/environment'; import { ConfirmPopupModule } from 'primeng/confirmpopup'; import { ConfirmDialogModule } from 'primeng/confirmdialog'; @@ -210,9 +209,7 @@ export class UserListComponent implements OnInit { resendUserCreatedMail(user: User) { this.usersService.resendUserCreateMail(user).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.CREATE_USER_MAIL_SENT), - ); + this.store.dispatch(toastNotification('CREATE_USER_MAIL_SENT')); }); } @@ -241,18 +238,14 @@ export class UserListComponent implements OnInit { deleteUser(user: User) { this.usersService.deleteUser(user).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.USER_DELETED), - ); + this.store.dispatch(toastNotification('USER_DELETED')); this.refreshData(); }); } promoteUser(user: User, promotionTarget: UserPromotionTargets) { this.usersService.promoteUser(user.id, promotionTarget).subscribe(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.USER_PROMOTED), - ); + this.store.dispatch(toastNotification('USER_PROMOTED')); this.refreshData(); }); } diff --git a/client/src/app/ngrx/actions/notifications.actions.ts b/client/src/app/ngrx/actions/notifications.actions.ts index 5eca3513..51ea8c9a 100644 --- a/client/src/app/ngrx/actions/notifications.actions.ts +++ b/client/src/app/ngrx/actions/notifications.actions.ts @@ -1,15 +1,15 @@ import { createAction } from '@ngrx/store'; -import { NotificationIdentifier } from '../../utility/notifications/notification-identifier.enum'; import { HashMap } from '@jsverse/transloco'; +import { NotificationKey } from '../../utility/notifications'; export const toastNotification = createAction( '[Notifications] Toast Notification', ( - identifier: NotificationIdentifier, + notificationKey: NotificationKey, titleParams: HashMap = {}, messageParams: HashMap = {}, ) => ({ - identifier, + notificationKey, titleParams, messageParams, }), diff --git a/client/src/app/ngrx/effects/app-level-alerts.effects.ts b/client/src/app/ngrx/effects/app-level-alerts.effects.ts index ad73b246..d0fee172 100644 --- a/client/src/app/ngrx/effects/app-level-alerts.effects.ts +++ b/client/src/app/ngrx/effects/app-level-alerts.effects.ts @@ -1,5 +1,3 @@ -// noinspection JSUnusedGlobalSymbols - import { Injectable } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { Store } from '@ngrx/store'; @@ -11,7 +9,6 @@ import { showCookieAlert, } from '../actions/app-level-alerts.actions'; import { toastNotification } from '../actions/notifications.actions'; -import { NotificationIdentifier } from '../../utility/notifications/notification-identifier.enum'; /** * Declares effects for the app level alerts. @@ -47,9 +44,7 @@ export class AppLevelAlertsEffects { ofType(cookiesAccepted), tap(() => { localStorage.setItem('cookiesAccepted', JSON.stringify(true)); - this.store.dispatch( - toastNotification(NotificationIdentifier.COOKIES_ALLOWED), - ); + this.store.dispatch(toastNotification('COOKIES_ALLOWED')); }), ), { dispatch: false }, diff --git a/client/src/app/ngrx/effects/auth.effects.ts b/client/src/app/ngrx/effects/auth.effects.ts index a5f650d7..26cb8b75 100644 --- a/client/src/app/ngrx/effects/auth.effects.ts +++ b/client/src/app/ngrx/effects/auth.effects.ts @@ -36,7 +36,6 @@ import { bigIntTimer } from '../../utility/observables/bigint-timer'; import { showRefreshTokenAboutToExpireAlert } from '../actions/app-level-alerts.actions'; import { unixToDate } from '../../utility/operators/unix-to-date'; import { toastNotification } from '../actions/notifications.actions'; -import { NotificationIdentifier } from '../../utility/notifications/notification-identifier.enum'; import { LoginResponse } from '../../models/login-response'; import { differenceInMilliseconds, isAfter, subMilliseconds } from 'date-fns'; @@ -67,9 +66,7 @@ export class AuthEffects { map(() => AuthActions.forgotPasswordSuccess()), catchError((err) => { if (err === 'USER_NOT_ACTIVATED') { - this.store.dispatch( - toastNotification(NotificationIdentifier.USER_NOT_ACTIVATED), - ); + this.store.dispatch(toastNotification('USER_NOT_ACTIVATED')); } return of(AuthActions.forgotPasswordError()); }), @@ -87,9 +84,7 @@ export class AuthEffects { ofType(AuthActions.forgotPasswordSuccess), tap(() => { this.router.navigate(['/', 'forgot-password-check-mailbox']); - this.store.dispatch( - toastNotification(NotificationIdentifier.FORGOT_PASSWORD_SUCCESS), - ); + this.store.dispatch(toastNotification('FORGOT_PASSWORD_SUCCESS')); }), ), { dispatch: false }, @@ -117,9 +112,7 @@ export class AuthEffects { }), catchError((err) => { if (err === 'USER_NOT_ACTIVATED') { - this.store.dispatch( - toastNotification(NotificationIdentifier.USER_NOT_ACTIVATED), - ); + this.store.dispatch(toastNotification('USER_NOT_ACTIVATED')); } return of(AuthActions.resetPasswordError()); }), @@ -137,9 +130,7 @@ export class AuthEffects { ofType(AuthActions.resetPasswordSuccess), tap(() => { this.router.navigate(['']); - this.store.dispatch( - toastNotification(NotificationIdentifier.RESET_PASSWORD_SUCCESS), - ); + this.store.dispatch(toastNotification('RESET_PASSWORD_SUCCESS')); }), ), { dispatch: false }, @@ -168,9 +159,7 @@ export class AuthEffects { this.actions$.pipe( ofType(AuthActions.loginError), map(() => { - this.store.dispatch( - toastNotification(NotificationIdentifier.LOGIN_ERROR), - ); + this.store.dispatch(toastNotification('LOGIN_ERROR')); }), ), { dispatch: false }, @@ -185,9 +174,7 @@ export class AuthEffects { ofType(AuthActions.loginSuccess), map((action) => { this.router.navigate(['']); - this.store.dispatch( - toastNotification(NotificationIdentifier.LOGIN_SUCCESS), - ); + this.store.dispatch(toastNotification('LOGIN_SUCCESS')); return newAuthCredentials({ loginResponse: action.loginResponse, fromAutoLogin: false, @@ -327,9 +314,7 @@ export class AuthEffects { if (err.status === 0) { // We just notify here and don't force logout the user as there might be some unsaved work and the server might recover. this.store.dispatch( - toastNotification( - NotificationIdentifier.UNKNOWN_AUTHENTICATION_ERROR, - ), + toastNotification('UNKNOWN_AUTHENTICATION_ERROR'), ); } return of(AuthActions.refreshAccessTokenFailed()); @@ -380,13 +365,9 @@ export class AuthEffects { map((action) => { if (!action.silent) { if (!action.isAutoLogout) { - this.store.dispatch( - toastNotification(NotificationIdentifier.LOGOUT_SUCCESS), - ); + this.store.dispatch(toastNotification('LOGOUT_SUCCESS')); } else { - this.store.dispatch( - toastNotification(NotificationIdentifier.AUTO_LOGOUT_SUCCESS), - ); + this.store.dispatch(toastNotification('AUTO_LOGOUT_SUCCESS')); } } return AuthActions.cleanupCredentials({ @@ -485,13 +466,9 @@ export class AuthEffects { if (!action.silent) { // We notify about a successful logout, although it wasn't successful as we throw away the token locally. if (!action.isAutoLogout) { - this.store.dispatch( - toastNotification(NotificationIdentifier.LOGOUT_SUCCESS), - ); + this.store.dispatch(toastNotification('LOGOUT_SUCCESS')); } else { - this.store.dispatch( - toastNotification(NotificationIdentifier.AUTO_LOGOUT_SUCCESS), - ); + this.store.dispatch(toastNotification('AUTO_LOGOUT_SUCCESS')); } } return AuthActions.cleanupCredentials({ diff --git a/client/src/app/ngrx/effects/notifications.effects.ts b/client/src/app/ngrx/effects/notifications.effects.ts index ebc38539..ff3305bd 100644 --- a/client/src/app/ngrx/effects/notifications.effects.ts +++ b/client/src/app/ngrx/effects/notifications.effects.ts @@ -20,7 +20,7 @@ export class NotificationsEffects { ofType(toastNotification), tap((action) => { this.notificationsService.toast( - action.identifier, + action.notificationKey, action.titleParams, action.messageParams, ); diff --git a/client/src/app/services/core/app-notifications.service.ts b/client/src/app/services/core/app-notifications.service.ts index 47222c2a..a81c4229 100644 --- a/client/src/app/services/core/app-notifications.service.ts +++ b/client/src/app/services/core/app-notifications.service.ts @@ -1,11 +1,14 @@ import { Injectable } from '@angular/core'; import { select, Store } from '@ngrx/store'; -import { NotificationType } from '../../utility/notifications/notification-type.enum'; import { HashMap, TranslocoService } from '@jsverse/transloco'; -import { NotificationIdentifier } from '../../utility/notifications/notification-identifier.enum'; import { AppState } from '../../ngrx/reducers'; import { selectIsMobile } from '../../ngrx/selectors/device.selectors'; import { MessageService } from 'primeng/api'; +import { + getNotification, + NotificationKey, + NotificationType, +} from '../../utility/notifications'; /** * Wrapper service for the angular2-notifications. @@ -18,374 +21,6 @@ import { MessageService } from 'primeng/api'; export class AppNotificationsService { private isMobile: boolean; - private notificationTypeMap: Map = - new Map([ - [ - NotificationIdentifier.INSTANCE_SETTINGS_ERROR_MIGRATION_IMPOSSIBLE, - NotificationType.ERROR, - ], - /** - * t(notifications.INSTANCE_SETTINGS_ERROR_MIGRATION_IMPOSSIBLE_TITLE) - * t(notifications.INSTANCE_SETTINGS_ERROR_MIGRATION_IMPOSSIBLE_MESSAGE) - **/ - [NotificationIdentifier.MAP_MARKER_ADDED, NotificationType.SUCCESS], - /** - * t(notifications.MAP_MARKER_ADDED_TITLE) - * t(notifications.MAP_MARKER_ADDED_MESSAGE) - **/ - [NotificationIdentifier.MAP_MARKER_REMOVED, NotificationType.SUCCESS], - /** - * t(notifications.MAP_MARKER_REMOVED_TITLE) - * t(notifications.MAP_MARKER_REMOVED_MESSAGE) - **/ - [NotificationIdentifier.GALLERY_IMAGE_UPDATED, NotificationType.SUCCESS], - /** - * t(notifications.GALLERY_IMAGE_UPDATED_TITLE) - * t(notifications.GALLERY_IMAGE_UPDATED_MESSAGE) - **/ - [NotificationIdentifier.GALLERY_IMAGE_DELETED, NotificationType.SUCCESS], - /** - * t(notifications.GALLERY_IMAGE_DELETED_TITLE) - * t(notifications.GALLERY_IMAGE_DELETED_MESSAGE) - **/ - [NotificationIdentifier.GALLERY_IMAGE_CREATED, NotificationType.SUCCESS], - /** - * t(notifications.GALLERY_IMAGE_CREATED_TITLE) - * t(notifications.GALLERY_IMAGE_CREATED_MESSAGE) - **/ - [ - NotificationIdentifier.PROJECT_CLIMBED_MESSAGE_SENT, - NotificationType.SUCCESS, - ], - /** - * t(notifications.PROJECT_CLIMBED_MESSAGE_SENT_TITLE) - * t(notifications.PROJECT_CLIMBED_MESSAGE_SENT_MESSAGE) - **/ - [NotificationIdentifier.TODO_ADD_ERROR, NotificationType.ERROR], - /** - * t(notifications.TODO_ADD_ERROR_TITLE) - * t(notifications.TODO_ADD_ERROR_MESSAGE) - **/ - [NotificationIdentifier.TODO_DELETED, NotificationType.SUCCESS], - /** - * t(notifications.TODO_DELETED_TITLE) - * t(notifications.TODO_DELETED_MESSAGE) - **/ - [NotificationIdentifier.TODO_PRIORITY_UPDATED, NotificationType.SUCCESS], - /** - * t(notifications.TODO_PRIORITY_UPDATED_TITLE) - * t(notifications.TODO_PRIORITY_UPDATED_MESSAGE) - **/ - [NotificationIdentifier.TODO_ADDED, NotificationType.SUCCESS], - /** - * t(notifications.TODO_ADDED_TITLE) - * t(notifications.TODO_ADDED_MESSAGE) - **/ - [NotificationIdentifier.LINE_UPDATED, NotificationType.SUCCESS], - /** - * t(notifications.LINE_UPDATED_TITLE) - * t(notifications.LINE_UPDATED_MESSAGE) - **/ - [NotificationIdentifier.ASCENT_DELETED, NotificationType.SUCCESS], - /** - * t(notifications.ASCENT_DELETED_TITLE) - * t(notifications.ASCENT_DELETED_MESSAGE) - **/ - [NotificationIdentifier.ASCENT_ADDED, NotificationType.SUCCESS], - /** - * t(notifications.ASCENT_ADDED_TITLE) - * t(notifications.ASCENT_ADDED_MESSAGE) - **/ - [NotificationIdentifier.ASCENT_UPDATED, NotificationType.SUCCESS], - /** - * t(notifications.ASCENT_UPDATED_TITLE) - * t(notifications.ASCENT_UPDATED_MESSAGE) - **/ - [NotificationIdentifier.LOGIN_ERROR, NotificationType.ERROR], - /** - * t(notifications.LOGIN_ERROR_TITLE) - * t(notifications.LOGIN_ERROR_MESSAGE) - **/ - [NotificationIdentifier.USER_PROMOTED, NotificationType.SUCCESS], - /** - * t(notifications.USER_PROMOTED_TITLE) - * t(notifications.USER_PROMOTED_MESSAGE) - **/ - [NotificationIdentifier.USER_DELETED, NotificationType.SUCCESS], - /** - * t(notifications.USER_DELETED_TITLE) - * t(notifications.USER_DELETED_MESSAGE) - **/ - [NotificationIdentifier.CREATE_USER_MAIL_SENT, NotificationType.SUCCESS], - /** - * t(notifications.CREATE_USER_MAIL_SENT_TITLE) - * t(notifications.CREATE_USER_MAIL_SENT_MESSAGE) - **/ - [ - NotificationIdentifier.ACCOUNT_SETTINGS_UPDATED, - NotificationType.SUCCESS, - ], - /** - * t(notifications.ACCOUNT_SETTINGS_UPDATED_TITLE) - * t(notifications.ACCOUNT_SETTINGS_UPDATED_MESSAGE) - **/ - [NotificationIdentifier.USER_REGISTERED, NotificationType.SUCCESS], - /** - * t(notifications.USER_REGISTERED_TITLE) - * t(notifications.USER_REGISTERED_MESSAGE) - **/ - [ - NotificationIdentifier.INSTANCE_SETTINGS_UPDATED, - NotificationType.SUCCESS, - ], - /** - * t(notifications.INSTANCE_SETTINGS_UPDATED_TITLE) - * t(notifications.INSTANCE_SETTINGS_UPDATED_MESSAGE) - **/ - [NotificationIdentifier.MENU_ITEM_DELETED, NotificationType.SUCCESS], - /** - * t(notifications.MENU_ITEM_DELETED_TITLE) - * t(notifications.MENU_ITEM_DELETED_MESSAGE) - **/ - [NotificationIdentifier.MENU_ITEM_CREATED, NotificationType.SUCCESS], - /** - * t(notifications.MENU_ITEM_CREATED_TITLE) - * t(notifications.MENU_ITEM_CREATED_MESSAGE) - **/ - [NotificationIdentifier.MENU_ITEM_UPDATED, NotificationType.SUCCESS], - /** - * t(notifications.MENU_ITEM_UPDATED_TITLE) - * t(notifications.MENU_ITEM_UPDATED_MESSAGE) - **/ - [NotificationIdentifier.MENU_PAGE_DELETED, NotificationType.SUCCESS], - /** - * t(notifications.MENU_PAGE_DELETED_TITLE) - * t(notifications.MENU_PAGE_DELETED_MESSAGE) - **/ - [NotificationIdentifier.MENU_PAGE_CREATED, NotificationType.SUCCESS], - /** - * t(notifications.MENU_PAGE_CREATED_TITLE) - * t(notifications.MENU_PAGE_CREATED_MESSAGE) - **/ - [NotificationIdentifier.MENU_PAGE_UPDATED, NotificationType.SUCCESS], - /** - * t(notifications.MENU_PAGE_UPDATED_TITLE) - * t(notifications.MENU_PAGE_UPDATED_MESSAGE) - **/ - [NotificationIdentifier.TOPO_IMAGE_UPDATED, NotificationType.SUCCESS], - /** - * t(notifications.TOPO_IMAGE_UPDATED_TITLE) - * t(notifications.TOPO_IMAGE_UPDATED_MESSAGE) - **/ - [NotificationIdentifier.REGION_UPDATED, NotificationType.SUCCESS], - /** - * t(notifications.REGION_UPDATED_TITLE) - * t(notifications.REGION_UPDATED_MESSAGE) - **/ - [NotificationIdentifier.POST_DELETED, NotificationType.SUCCESS], - /** - * t(notifications.POST_DELETED_TITLE) - * t(notifications.POST_DELETED_MESSAGE) - **/ - [NotificationIdentifier.POST_UPDATED, NotificationType.SUCCESS], - /** - * t(notifications.POST_UPDATED_TITLE) - * t(notifications.POST_UPDATED_MESSAGE) - **/ - [NotificationIdentifier.POST_CREATED, NotificationType.SUCCESS], - /** - * t(notifications.POST_CREATED_TITLE) - * t(notifications.POST_CREATED_MESSAGE) - **/ - [NotificationIdentifier.LINE_CREATED, NotificationType.SUCCESS], - /** - * t(notifications.LINE_CREATED_TITLE) - * t(notifications.LINE_CREATED_MESSAGE) - **/ - [NotificationIdentifier.LINE_PATH_DELETED, NotificationType.SUCCESS], - /** - * t(notifications.LINE_PATH_DELETED_TITLE) - * t(notifications.LINE_PATH_DELETED_MESSAGE) - **/ - [NotificationIdentifier.LINE_PATH_ADDED, NotificationType.SUCCESS], - /** - * t(notifications.LINE_PATH_ADDED_TITLE) - * t(notifications.LINE_PATH_ADDED_MESSAGE) - **/ - [NotificationIdentifier.TOPO_IMAGE_DELETED, NotificationType.SUCCESS], - /** - * t(notifications.TOPO_IMAGE_DELETED_TITLE) - * t(notifications.TOPO_IMAGE_DELETED_MESSAGE) - **/ - [NotificationIdentifier.TOPO_IMAGE_ADDED, NotificationType.SUCCESS], - /** - * t(notifications.TOPO_IMAGE_ADDED_TITLE) - * t(notifications.TOPO_IMAGE_ADDED_MESSAGE) - **/ - [NotificationIdentifier.AREA_DELETED, NotificationType.SUCCESS], - /** - * t(notifications.AREA_DELETED_TITLE) - * t(notifications.AREA_DELETED_MESSAGE) - **/ - [NotificationIdentifier.AREA_UPDATED, NotificationType.SUCCESS], - /** - * t(notifications.AREA_UPDATED_TITLE) - * t(notifications.AREA_UPDATED_MESSAGE) - **/ - [NotificationIdentifier.AREA_CREATED, NotificationType.SUCCESS], - /** - * t(notifications.AREA_CREATED_TITLE) - * t(notifications.AREA_CREATED_MESSAGE) - **/ - [NotificationIdentifier.SECTOR_DELETED, NotificationType.SUCCESS], - /** - * t(notifications.SECTOR_DELETED_TITLE) - * t(notifications.SECTOR_DELETED_MESSAGE) - **/ - [NotificationIdentifier.SECTOR_UPDATED, NotificationType.SUCCESS], - /** - * t(notifications.SECTOR_UPDATED_TITLE) - * t(notifications.SECTOR_UPDATED_MESSAGE) - **/ - [NotificationIdentifier.SECTOR_CREATED, NotificationType.SUCCESS], - /** - * t(notifications.SECTOR_CREATED_TITLE) - * t(notifications.SECTOR_CREATED_MESSAGE) - **/ - [NotificationIdentifier.CRAG_DELETED, NotificationType.SUCCESS], - /** - * t(notifications.CRAG_DELETED_TITLE) - * t(notifications.CRAG_DELETED_MESSAGE) - **/ - [NotificationIdentifier.CRAG_UPDATED, NotificationType.SUCCESS], - /** - * t(notifications.CRAG_UPDATED_TITLE) - * t(notifications.CRAG_UPDATED_MESSAGE) - **/ - [NotificationIdentifier.CRAG_CREATED, NotificationType.SUCCESS], - /** - * t(notifications.CRAG_CREATED_TITLE) - * t(notifications.CRAG_CREATED_MESSAGE) - **/ - [NotificationIdentifier.USER_NOT_ACTIVATED, NotificationType.ERROR], - /** - * t(notifications.USER_NOT_ACTIVATED_TITLE) - * t(notifications.USER_NOT_ACTIVATED_MESSAGE) - **/ - [NotificationIdentifier.LOGIN_SUCCESS, NotificationType.SUCCESS], - /** - * t(notifications.LOGIN_SUCCESS_TITLE) - * t(notifications.LOGIN_SUCCESS_MESSAGE) - **/ - [ - NotificationIdentifier.CHANGE_PASSWORD_SUCCESS, - NotificationType.SUCCESS, - ], - /** - * t(notifications.CHANGE_PASSWORD_SUCCESS_TITLE) - * t(notifications.CHANGE_PASSWORD_SUCCESS_MESSAGE) - **/ - [ - NotificationIdentifier.FORGOT_PASSWORD_SUCCESS, - NotificationType.SUCCESS, - ], - /** - * t(notifications.FORGOT_PASSWORD_SUCCESS_TITLE) - * t(notifications.FORGOT_PASSWORD_SUCCESS_MESSAGE) - **/ - [NotificationIdentifier.RESET_PASSWORD_SUCCESS, NotificationType.SUCCESS], - /** - * t(notifications.RESET_PASSWORD_SUCCESS_TITLE) - * t(notifications.RESET_PASSWORD_SUCCESS_MESSAGE) - **/ - [NotificationIdentifier.COOKIES_ALLOWED, NotificationType.INFO], - /** - * t(notifications.COOKIES_ALLOWED_TITLE) - * t(notifications.COOKIES_ALLOWED_MESSAGE) - **/ - [NotificationIdentifier.LOGOUT_SUCCESS, NotificationType.INFO], - /** - * t(notifications.LOGOUT_SUCCESS_TITLE) - * t(notifications.LOGOUT_SUCCESS_MESSAGE) - **/ - [NotificationIdentifier.AUTO_LOGOUT_SUCCESS, NotificationType.INFO], - /** - * t(notifications.AUTO_LOGOUT_SUCCESS_TITLE) - * t(notifications.AUTO_LOGOUT_SUCCESS_MESSAGE) - **/ - [ - NotificationIdentifier.UNKNOWN_AUTHENTICATION_ERROR, - NotificationType.ERROR, - ], - /** - * t(notifications.UNKNOWN_AUTHENTICATION_ERROR_TITLE) - * t(notifications.UNKNOWN_AUTHENTICATION_ERROR_MESSAGE) - **/ - [NotificationIdentifier.UNKNOWN_ERROR, NotificationType.ERROR], - /** - * t(notifications.UNKNOWN_ERROR_TITLE) - * t(notifications.UNKNOWN_ERROR_MESSAGE) - **/ - [ - NotificationIdentifier.LOG_OUT_TO_USE_THIS_FUNCTION, - NotificationType.INFO, - ], - /** - * t(notifications.LOG_OUT_TO_USE_THIS_FUNCTION_TITLE) - * t(notifications.LOG_OUT_TO_USE_THIS_FUNCTION_MESSAGE) - **/ - [NotificationIdentifier.ARCHIVED, NotificationType.SUCCESS], - /** - * t(notifications.ARCHIVED_TITLE) - * t(notifications.ARCHIVED_MESSAGE) - **/ - [NotificationIdentifier.UNARCHIVED, NotificationType.SUCCESS], - /** - * t(notifications.UNARCHIVED_TITLE) - * t(notifications.UNARCHIVED_MESSAGE) - **/ - [NotificationIdentifier.ARCHIVED_ERROR, NotificationType.ERROR], - /** - * t(notifications.ARCHIVED_ERROR_TITLE) - * t(notifications.ARCHIVED_ERROR_MESSAGE) - **/ - [NotificationIdentifier.UNARCHIVED_ERROR, NotificationType.ERROR], - /** - * t(notifications.UNARCHIVED_ERROR_TITLE) - * t(notifications.UNARCHIVED_ERROR_MESSAGE) - **/ - [NotificationIdentifier.SCALE_CREATED, NotificationType.SUCCESS], - /** - * t(notifications.SCALE_CREATED_TITLE) - * t(notifications.SCALE_CREATED_MESSAGE) - **/ - [NotificationIdentifier.SCALE_CREATED_ERROR, NotificationType.ERROR], - /** - * t(notifications.SCALE_CREATED_ERROR_TITLE) - * t(notifications.SCALE_CREATED_ERROR_MESSAGE) - **/ - [NotificationIdentifier.SCALE_UPDATED, NotificationType.SUCCESS], - /** - * t(notifications.SCALE_UPDATED_TITLE) - * t(notifications.SCALE_UPDATED_MESSAGE) - **/ - [NotificationIdentifier.SCALE_UPDATED_ERROR, NotificationType.ERROR], - /** - * t(notifications.SCALE_UPDATED_ERROR_TITLE) - * t(notifications.SCALE_UPDATED_ERROR_MESSAGE) - **/ - [NotificationIdentifier.SCALE_DELETED, NotificationType.SUCCESS], - /** - * t(notifications.SCALE_DELETED_TITLE) - * t(notifications.SCALE_DELETED_MESSAGE) - **/ - [NotificationIdentifier.SCALE_DELETED_ERROR, NotificationType.ERROR], - /** - * t(notifications.SCALE_DELETED_ERROR_TITLE) - * t(notifications.SCALE_DELETED_ERROR_MESSAGE) - **/ - ]); - constructor( private messageService: MessageService, private translocoService: TranslocoService, @@ -399,16 +34,16 @@ export class AppNotificationsService { /** * Pushes a toast notification: either immediately or in a deferred way if currently language files are loading. * - * @param notificationIdentifier Identifier of the notification to push. + * @param notificationKey Key of the notification to push. * @param titleParams Translation params for the title string. * @param messageParams Translation params for the message string. */ public toast( - notificationIdentifier: NotificationIdentifier, + notificationKey: NotificationKey, titleParams: HashMap = {}, messageParams: HashMap = {}, ): void { - this.doToast(notificationIdentifier, titleParams, messageParams); + this.doToast(notificationKey, titleParams, messageParams); } /** @@ -478,24 +113,25 @@ export class AppNotificationsService { /** * Pushes a toast notification. * - * @param notificationIdentifier Identifier of the notification to push. + * @param notificationKey Key of the notification to push. * @param titleParams Translation params for the title string. * @param messageParams Translation params for the message string. */ private doToast( - notificationIdentifier: NotificationIdentifier, + notificationKey: NotificationKey, titleParams: HashMap = {}, messageParams: HashMap = {}, ) { + const notificationDefinition = getNotification(notificationKey); const title = this.translocoService.translate( - 'notifications.' + notificationIdentifier.toString() + '_TITLE', + notificationDefinition.title, titleParams, ); const message = this.translocoService.translate( - 'notifications.' + notificationIdentifier.toString() + '_MESSAGE', + notificationDefinition.message, messageParams, ); - switch (this.notificationTypeMap.get(notificationIdentifier)) { + switch (notificationDefinition.type) { case NotificationType.ERROR: this.error(title, message); break; diff --git a/client/src/app/services/core/error-handler.service.ts b/client/src/app/services/core/error-handler.service.ts index 2967ae44..5ddbd48b 100644 --- a/client/src/app/services/core/error-handler.service.ts +++ b/client/src/app/services/core/error-handler.service.ts @@ -1,9 +1,9 @@ import { Injectable } from '@angular/core'; import { HttpErrorResponse } from '@angular/common/http'; import { Store } from '@ngrx/store'; -import { NotificationIdentifier } from '../../utility/notifications/notification-identifier.enum'; import { AppState } from '../../ngrx/reducers'; import { toastNotification } from '../../ngrx/actions/notifications.actions'; +import { NOTIFICATIONS } from '../../utility/notifications'; /** * A simple error handling service for logging and messaging. @@ -20,16 +20,9 @@ export class ErrorHandlerService { * @param error The error to handle. */ public handleHttpError(error: HttpErrorResponse) { - const errIdentifier = - error.error['labnodeErrCode'] || error.error['message'] || null; - if (errIdentifier && errIdentifier in NotificationIdentifier) { - this.store.dispatch( - toastNotification( - NotificationIdentifier[ - errIdentifier as keyof typeof NotificationIdentifier - ], - ), - ); + const errIdentifier = error.error['message'] || null; + if (errIdentifier && errIdentifier in NOTIFICATIONS) { + this.store.dispatch(toastNotification(errIdentifier)); } console.error(error); } diff --git a/client/src/app/utility/notifications.ts b/client/src/app/utility/notifications.ts new file mode 100644 index 00000000..ec92c62e --- /dev/null +++ b/client/src/app/utility/notifications.ts @@ -0,0 +1,388 @@ +import { marker } from '@jsverse/transloco-keys-manager/marker'; + +/** + * Possible types of a notification. + */ +export enum NotificationType { + INFO, + ERROR, + WARNING, + SUCCESS, +} + +/** + * Definition of a notification. + */ +export type NotificationDefinition = { + type: NotificationType; + title: string; + message: string; +}; + +/** + * All available notifications. + */ +export const NOTIFICATIONS = { + MAP_MARKER_REMOVED: { + type: NotificationType.SUCCESS, + title: marker('notifications.MAP_MARKER_REMOVED_TITLE'), + message: marker('notifications.MAP_MARKER_REMOVED_MESSAGE'), + }, + MAP_MARKER_ADDED: { + type: NotificationType.SUCCESS, + title: marker('notifications.MAP_MARKER_ADDED_TITLE'), + message: marker('notifications.MAP_MARKER_ADDED_MESSAGE'), + }, + GALLERY_IMAGE_CREATED: { + type: NotificationType.SUCCESS, + title: marker('notifications.GALLERY_IMAGE_CREATED_TITLE'), + message: marker('notifications.GALLERY_IMAGE_CREATED_MESSAGE'), + }, + GALLERY_IMAGE_UPDATED: { + type: NotificationType.SUCCESS, + title: marker('notifications.GALLERY_IMAGE_UPDATED_TITLE'), + message: marker('notifications.GALLERY_IMAGE_UPDATED_MESSAGE'), + }, + GALLERY_IMAGE_DELETED: { + type: NotificationType.SUCCESS, + title: marker('notifications.GALLERY_IMAGE_DELETED_TITLE'), + message: marker('notifications.GALLERY_IMAGE_DELETED_MESSAGE'), + }, + PROJECT_CLIMBED_MESSAGE_SENT: { + type: NotificationType.SUCCESS, + title: marker('notifications.PROJECT_CLIMBED_MESSAGE_SENT_TITLE'), + message: marker('notifications.PROJECT_CLIMBED_MESSAGE_SENT_MESSAGE'), + }, + TODO_DELETED: { + type: NotificationType.SUCCESS, + title: marker('notifications.TODO_DELETED_TITLE'), + message: marker('notifications.TODO_DELETED_MESSAGE'), + }, + TODO_PRIORITY_UPDATED: { + type: NotificationType.SUCCESS, + title: marker('notifications.TODO_PRIORITY_UPDATED_TITLE'), + message: marker('notifications.TODO_PRIORITY_UPDATED_MESSAGE'), + }, + TODO_ADD_ERROR: { + type: NotificationType.ERROR, + title: marker('notifications.TODO_ADD_ERROR_TITLE'), + message: marker('notifications.TODO_ADD_ERROR_MESSAGE'), + }, + TODO_ADDED: { + type: NotificationType.SUCCESS, + title: marker('notifications.TODO_ADDED_TITLE'), + message: marker('notifications.TODO_ADDED_MESSAGE'), + }, + ASCENT_ADDED: { + type: NotificationType.SUCCESS, + title: marker('notifications.ASCENT_ADDED_TITLE'), + message: marker('notifications.ASCENT_ADDED_MESSAGE'), + }, + ASCENT_UPDATED: { + type: NotificationType.SUCCESS, + title: marker('notifications.ASCENT_UPDATED_TITLE'), + message: marker('notifications.ASCENT_UPDATED_MESSAGE'), + }, + ASCENT_DELETED: { + type: NotificationType.SUCCESS, + title: marker('notifications.ASCENT_DELETED_TITLE'), + message: marker('notifications.ASCENT_DELETED_MESSAGE'), + }, + ARCHIVED: { + type: NotificationType.SUCCESS, + title: marker('notifications.ARCHIVED_TITLE'), + message: marker('notifications.ARCHIVED_MESSAGE'), + }, + UNARCHIVED: { + type: NotificationType.SUCCESS, + title: marker('notifications.UNARCHIVED_TITLE'), + message: marker('notifications.UNARCHIVED_MESSAGE'), + }, + ARCHIVED_ERROR: { + type: NotificationType.ERROR, + title: marker('notifications.ARCHIVED_ERROR_TITLE'), + message: marker('notifications.ARCHIVED_ERROR_MESSAGE'), + }, + UNARCHIVED_ERROR: { + type: NotificationType.ERROR, + title: marker('notifications.UNARCHIVED_ERROR_TITLE'), + message: marker('notifications.UNARCHIVED_ERROR_MESSAGE'), + }, + LOGIN_ERROR: { + type: NotificationType.SUCCESS, + title: marker('notifications.LOGIN_ERROR_TITLE'), + message: marker('notifications.LOGIN_ERROR_MESSAGE'), + }, + USER_PROMOTED: { + type: NotificationType.SUCCESS, + title: marker('notifications.USER_PROMOTED_TITLE'), + message: marker('notifications.USER_PROMOTED_MESSAGE'), + }, + USER_DELETED: { + type: NotificationType.SUCCESS, + title: marker('notifications.USER_DELETED_TITLE'), + message: marker('notifications.USER_DELETED_MESSAGE'), + }, + CREATE_USER_MAIL_SENT: { + type: NotificationType.SUCCESS, + title: marker('notifications.CREATE_USER_MAIL_SENT_TITLE'), + message: marker('notifications.CREATE_USER_MAIL_SENT_MESSAGE'), + }, + ACCOUNT_SETTINGS_UPDATED: { + type: NotificationType.SUCCESS, + title: marker('notifications.ACCOUNT_SETTINGS_UPDATED_TITLE'), + message: marker('notifications.ACCOUNT_SETTINGS_UPDATED_MESSAGE'), + }, + INSTANCE_SETTINGS_UPDATED: { + type: NotificationType.SUCCESS, + title: marker('notifications.INSTANCE_SETTINGS_UPDATED_TITLE'), + message: marker('notifications.INSTANCE_SETTINGS_UPDATED_MESSAGE'), + }, + INSTANCE_SETTINGS_ERROR_MIGRATION_IMPOSSIBLE: { + type: NotificationType.ERROR, + title: marker( + 'notifications.INSTANCE_SETTINGS_ERROR_MIGRATION_IMPOSSIBLE_TITLE', + ), + message: marker( + 'notifications.INSTANCE_SETTINGS_ERROR_MIGRATION_IMPOSSIBLE_MESSAGE', + ), + }, + REGION_UPDATED: { + type: NotificationType.SUCCESS, + title: marker('notifications.REGION_UPDATED_TITLE'), + message: marker('notifications.REGION_UPDATED_MESSAGE'), + }, + USER_NOT_ACTIVATED: { + type: NotificationType.ERROR, + title: marker('notifications.USER_NOT_ACTIVATED_TITLE'), + message: marker('notifications.USER_NOT_ACTIVATED_MESSAGE'), + }, + LOGIN_SUCCESS: { + type: NotificationType.SUCCESS, + title: marker('notifications.LOGIN_SUCCESS_TITLE'), + message: marker('notifications.LOGIN_SUCCESS_MESSAGE'), + }, + CHANGE_PASSWORD_SUCCESS: { + type: NotificationType.SUCCESS, + title: marker('notifications.CHANGE_PASSWORD_SUCCESS_TITLE'), + message: marker('notifications.CHANGE_PASSWORD_SUCCESS_MESSAGE'), + }, + FORGOT_PASSWORD_SUCCESS: { + type: NotificationType.SUCCESS, + title: marker('notifications.FORGOT_PASSWORD_SUCCESS_TITLE'), + message: marker('notifications.FORGOT_PASSWORD_SUCCESS_MESSAGE'), + }, + RESET_PASSWORD_SUCCESS: { + type: NotificationType.SUCCESS, + title: marker('notifications.RESET_PASSWORD_SUCCESS_TITLE'), + message: marker('notifications.RESET_PASSWORD_SUCCESS_MESSAGE'), + }, + COOKIES_ALLOWED: { + type: NotificationType.INFO, + title: marker('notifications.COOKIES_ALLOWED_TITLE'), + message: marker('notifications.COOKIES_ALLOWED_MESSAGE'), + }, + LOGOUT_SUCCESS: { + type: NotificationType.INFO, + title: marker('notifications.LOGOUT_SUCCESS_TITLE'), + message: marker('notifications.LOGOUT_SUCCESS_MESSAGE'), + }, + AUTO_LOGOUT_SUCCESS: { + type: NotificationType.INFO, + title: marker('notifications.AUTO_LOGOUT_SUCCESS_TITLE'), + message: marker('notifications.AUTO_LOGOUT_SUCCESS_MESSAGE'), + }, + UNKNOWN_AUTHENTICATION_ERROR: { + type: NotificationType.ERROR, + title: marker('notifications.UNKNOWN_AUTHENTICATION_ERROR_TITLE'), + message: marker('notifications.UNKNOWN_AUTHENTICATION_ERROR_MESSAGE'), + }, + UNKNOWN_ERROR: { + type: NotificationType.ERROR, + title: marker('notifications.UNKNOWN_ERROR_TITLE'), + message: marker('notifications.UNKNOWN_ERROR_MESSAGE'), + }, + LOG_OUT_TO_USE_THIS_FUNCTION: { + type: NotificationType.INFO, + title: marker('notifications.LOG_OUT_TO_USE_THIS_FUNCTION_TITLE'), + message: marker('notifications.LOG_OUT_TO_USE_THIS_FUNCTION_MESSAGE'), + }, + CRAG_CREATED: { + type: NotificationType.SUCCESS, + title: marker('notifications.CRAG_CREATED_TITLE'), + message: marker('notifications.CRAG_CREATED_MESSAGE'), + }, + CRAG_UPDATED: { + type: NotificationType.SUCCESS, + title: marker('notifications.CRAG_UPDATED_TITLE'), + message: marker('notifications.CRAG_UPDATED_MESSAGE'), + }, + CRAG_DELETED: { + type: NotificationType.SUCCESS, + title: marker('notifications.CRAG_DELETED_TITLE'), + message: marker('notifications.CRAG_DELETED_MESSAGE'), + }, + SECTOR_CREATED: { + type: NotificationType.SUCCESS, + title: marker('notifications.SECTOR_CREATED_TITLE'), + message: marker('notifications.SECTOR_CREATED_MESSAGE'), + }, + SECTOR_UPDATED: { + type: NotificationType.SUCCESS, + title: marker('notifications.SECTOR_UPDATED_TITLE'), + message: marker('notifications.SECTOR_UPDATED_MESSAGE'), + }, + SECTOR_DELETED: { + type: NotificationType.SUCCESS, + title: marker('notifications.SECTOR_DELETED_TITLE'), + message: marker('notifications.SECTOR_DELETED_MESSAGE'), + }, + AREA_CREATED: { + type: NotificationType.SUCCESS, + title: marker('notifications.AREA_CREATED_TITLE'), + message: marker('notifications.AREA_CREATED_MESSAGE'), + }, + AREA_UPDATED: { + type: NotificationType.SUCCESS, + title: marker('notifications.AREA_UPDATED_TITLE'), + message: marker('notifications.AREA_UPDATED_MESSAGE'), + }, + AREA_DELETED: { + type: NotificationType.SUCCESS, + title: marker('notifications.AREA_DELETED_TITLE'), + message: marker('notifications.AREA_DELETED_MESSAGE'), + }, + LINE_CREATED: { + type: NotificationType.SUCCESS, + title: marker('notifications.LINE_CREATED_TITLE'), + message: marker('notifications.LINE_CREATED_MESSAGE'), + }, + LINE_UPDATED: { + type: NotificationType.SUCCESS, + title: marker('notifications.LINE_UPDATED_TITLE'), + message: marker('notifications.LINE_UPDATED_MESSAGE'), + }, + LINE_DELETED: { + type: NotificationType.SUCCESS, + title: marker('notifications.LINE_DELETED_TITLE'), + message: marker('notifications.LINE_DELETED_MESSAGE'), + }, + POST_CREATED: { + type: NotificationType.SUCCESS, + title: marker('notifications.POST_CREATED_TITLE'), + message: marker('notifications.POST_CREATED_MESSAGE'), + }, + POST_UPDATED: { + type: NotificationType.SUCCESS, + title: marker('notifications.POST_UPDATED_TITLE'), + message: marker('notifications.POST_UPDATED_MESSAGE'), + }, + POST_DELETED: { + type: NotificationType.SUCCESS, + title: marker('notifications.POST_DELETED_TITLE'), + message: marker('notifications.POST_DELETED_MESSAGE'), + }, + TOPO_IMAGE_ADDED: { + type: NotificationType.SUCCESS, + title: marker('notifications.TOPO_IMAGE_ADDED_TITLE'), + message: marker('notifications.TOPO_IMAGE_ADDED_MESSAGE'), + }, + TOPO_IMAGE_UPDATED: { + type: NotificationType.SUCCESS, + title: marker('notifications.TOPO_IMAGE_UPDATED_TITLE'), + message: marker('notifications.TOPO_IMAGE_UPDATED_MESSAGE'), + }, + TOPO_IMAGE_DELETED: { + type: NotificationType.SUCCESS, + title: marker('notifications.TOPO_IMAGE_DELETED_TITLE'), + message: marker('notifications.TOPO_IMAGE_DELETED_MESSAGE'), + }, + LINE_PATH_ADDED: { + type: NotificationType.SUCCESS, + title: marker('notifications.LINE_PATH_ADDED_TITLE'), + message: marker('notifications.LINE_PATH_ADDED_MESSAGE'), + }, + LINE_PATH_DELETED: { + type: NotificationType.SUCCESS, + title: marker('notifications.LINE_PATH_DELETED_TITLE'), + message: marker('notifications.LINE_PATH_DELETED_MESSAGE'), + }, + MENU_PAGE_DELETED: { + type: NotificationType.SUCCESS, + title: marker('notifications.MENU_PAGE_DELETED_TITLE'), + message: marker('notifications.MENU_PAGE_DELETED_MESSAGE'), + }, + MENU_PAGE_UPDATED: { + type: NotificationType.SUCCESS, + title: marker('notifications.MENU_PAGE_UPDATED_TITLE'), + message: marker('notifications.MENU_PAGE_UPDATED_MESSAGE'), + }, + MENU_PAGE_CREATED: { + type: NotificationType.SUCCESS, + title: marker('notifications.MENU_PAGE_CREATED_TITLE'), + message: marker('notifications.MENU_PAGE_CREATED_MESSAGE'), + }, + MENU_ITEM_DELETED: { + type: NotificationType.SUCCESS, + title: marker('notifications.MENU_ITEM_DELETED_TITLE'), + message: marker('notifications.MENU_ITEM_DELETED_MESSAGE'), + }, + MENU_ITEM_UPDATED: { + type: NotificationType.SUCCESS, + title: marker('notifications.MENU_ITEM_UPDATED_TITLE'), + message: marker('notifications.MENU_ITEM_UPDATED_MESSAGE'), + }, + MENU_ITEM_CREATED: { + type: NotificationType.SUCCESS, + title: marker('notifications.MENU_ITEM_CREATED_TITLE'), + message: marker('notifications.MENU_ITEM_CREATED_MESSAGE'), + }, + USER_REGISTERED: { + type: NotificationType.SUCCESS, + title: marker('notifications.USER_REGISTERED_TITLE'), + message: marker('notifications.USER_REGISTERED_MESSAGE'), + }, + SCALE_CREATED: { + type: NotificationType.SUCCESS, + title: marker('notifications.SCALE_CREATED_TITLE'), + message: marker('notifications.SCALE_CREATED_MESSAGE'), + }, + SCALE_CREATED_ERROR: { + type: NotificationType.ERROR, + title: marker('notifications.SCALE_CREATED_ERROR_TITLE'), + message: marker('notifications.SCALE_CREATED_ERROR_MESSAGE'), + }, + SCALE_UPDATED: { + type: NotificationType.SUCCESS, + title: marker('notifications.SCALE_UPDATED_TITLE'), + message: marker('notifications.SCALE_UPDATED_MESSAGE'), + }, + SCALE_UPDATED_ERROR: { + type: NotificationType.ERROR, + title: marker('notifications.SCALE_UPDATED_ERROR_TITLE'), + message: marker('notifications.SCALE_UPDATED_ERROR_MESSAGE'), + }, + SCALE_DELETED: { + type: NotificationType.SUCCESS, + title: marker('notifications.SCALE_DELETED_TITLE'), + message: marker('notifications.SCALE_DELETED_MESSAGE'), + }, + SCALE_DELETED_ERROR: { + type: NotificationType.ERROR, + title: marker('notifications.SCALE_DELETED_ERROR_TITLE'), + message: marker('notifications.SCALE_DELETED_ERROR_MESSAGE'), + }, +} satisfies { [key: string]: NotificationDefinition }; + +export type NotificationKey = keyof typeof NOTIFICATIONS; + +/** + * Get a notification by key. In contrast to directly accessing the NOTIFICATIONS object, this function + * will throw an error if the key does not exist. + * @param key Key of the notification to get. + */ +export const getNotification = ( + key: NotificationKey, +): NotificationDefinition => { + return NOTIFICATIONS[key]; +}; diff --git a/client/src/app/utility/notifications/notification-identifier.enum.ts b/client/src/app/utility/notifications/notification-identifier.enum.ts deleted file mode 100644 index d255fa52..00000000 --- a/client/src/app/utility/notifications/notification-identifier.enum.ts +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Defines identifiers for all notifications in the system. - */ -export enum NotificationIdentifier { - MAP_MARKER_REMOVED = 'MAP_MARKER_REMOVED', - MAP_MARKER_ADDED = 'MAP_MARKER_ADDED', - GALLERY_IMAGE_CREATED = 'GALLERY_IMAGE_CREATED', - GALLERY_IMAGE_UPDATED = 'GALLERY_IMAGE_UPDATED', - GALLERY_IMAGE_DELETED = 'GALLERY_IMAGE_DELETED', - PROJECT_CLIMBED_MESSAGE_SENT = 'PROJECT_CLIMBED_MESSAGE_SENT', - TODO_DELETED = 'TODO_DELETED', - TODO_PRIORITY_UPDATED = 'TODO_PRIORITY_UPDATED', - TODO_ADD_ERROR = 'TODO_ADD_ERROR', - TODO_ADDED = 'TODO_ADDED', - ASCENT_ADDED = 'ASCENT_ADDED', - ASCENT_UPDATED = 'ASCENT_UPDATED', - ASCENT_DELETED = 'ASCENT_DELETED', - ARCHIVED = 'ARCHIVED', - UNARCHIVED = 'UNARCHIVED', - ARCHIVED_ERROR = 'ARCHIVED_ERROR', - UNARCHIVED_ERROR = 'UNARCHIVED_ERROR', - LOGIN_ERROR = 'LOGIN_ERROR', - USER_PROMOTED = 'USER_PROMOTED', - USER_DELETED = 'USER_DELETED', - CREATE_USER_MAIL_SENT = 'CREATE_USER_MAIL_SENT', - ACCOUNT_SETTINGS_UPDATED = 'ACCOUNT_SETTINGS_UPDATED', - INSTANCE_SETTINGS_UPDATED = 'INSTANCE_SETTINGS_UPDATED', - INSTANCE_SETTINGS_ERROR_MIGRATION_IMPOSSIBLE = 'INSTANCE_SETTINGS_ERROR_MIGRATION_IMPOSSIBLE', - REGION_UPDATED = 'REGION_UPDATED', - USER_NOT_ACTIVATED = 'USER_NOT_ACTIVATED', - LOGIN_SUCCESS = 'LOGIN_SUCCESS', - CHANGE_PASSWORD_SUCCESS = 'CHANGE_PASSWORD_SUCCESS', - FORGOT_PASSWORD_SUCCESS = 'FORGOT_PASSWORD_SUCCESS', - RESET_PASSWORD_SUCCESS = 'RESET_PASSWORD_SUCCESS', - COOKIES_ALLOWED = 'COOKIES_ALLOWED', - LOGOUT_SUCCESS = 'LOGOUT_SUCCESS', - AUTO_LOGOUT_SUCCESS = 'AUTO_LOGOUT_SUCCESS', - UNKNOWN_AUTHENTICATION_ERROR = 'UNKNOWN_AUTHENTICATION_ERROR', - UNKNOWN_ERROR = 'UNKNOWN_ERROR', - LOG_OUT_TO_USE_THIS_FUNCTION = 'LOG_OUT_TO_USE_THIS_FUNCTION', - CRAG_CREATED = 'CRAG_CREATED', - CRAG_UPDATED = 'CRAG_UPDATED', - CRAG_DELETED = 'CRAG_DELETED', - SECTOR_CREATED = 'SECTOR_CREATED', - SECTOR_UPDATED = 'SECTOR_UPDATED', - SECTOR_DELETED = 'SECTOR_DELETED', - AREA_CREATED = 'AREA_CREATED', - AREA_UPDATED = 'AREA_UPDATED', - AREA_DELETED = 'AREA_DELETED', - LINE_CREATED = 'LINE_CREATED', - LINE_UPDATED = 'LINE_UPDATED', - LINE_DELETED = 'LINE_DELETED', - POST_CREATED = 'POST_CREATED', - POST_UPDATED = 'POST_UPDATED', - POST_DELETED = 'POST_DELETED', - TOPO_IMAGE_ADDED = 'TOPO_IMAGE_ADDED', - TOPO_IMAGE_UPDATED = 'TOPO_IMAGE_UPDATED', - TOPO_IMAGE_DELETED = 'TOPO_IMAGE_DELETED', - LINE_PATH_ADDED = 'LINE_PATH_ADDED', - LINE_PATH_DELETED = 'LINE_PATH_DELETED', - MENU_PAGE_DELETED = 'MENU_PAGE_DELETED', - MENU_PAGE_UPDATED = 'MENU_PAGE_UPDATED', - MENU_PAGE_CREATED = 'MENU_PAGE_CREATED', - MENU_ITEM_DELETED = 'MENU_ITEM_DELETED', - MENU_ITEM_UPDATED = 'MENU_ITEM_UPDATED', - MENU_ITEM_CREATED = 'MENU_ITEM_CREATED', - USER_REGISTERED = 'USER_REGISTERED', - SCALE_CREATED = 'SCALE_CREATED', - SCALE_CREATED_ERROR = 'SCALE_CREATED_ERROR', - SCALE_UPDATED = 'SCALE_UPDATED', - SCALE_UPDATED_ERROR = 'SCALE_UPDATED_ERROR', - SCALE_DELETED = 'SCALE_DELETED', - SCALE_DELETED_ERROR = 'SCALE_DELETED_ERROR', -} diff --git a/client/src/app/utility/notifications/notification-type.enum.ts b/client/src/app/utility/notifications/notification-type.enum.ts deleted file mode 100644 index ac0cc55b..00000000 --- a/client/src/app/utility/notifications/notification-type.enum.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Possible types of a notification. - */ -export enum NotificationType { - INFO, - ERROR, - WARNING, - SUCCESS, -} From 12977579133aae07485adddb4f8bfceeede79910 Mon Sep 17 00:00:00 2001 From: Felix Engelmann Date: Sun, 16 Feb 2025 16:23:20 +0100 Subject: [PATCH 16/18] Add missing i18n --- client/src/assets/i18n/area/de.json | 2 +- client/src/assets/i18n/crag/de.json | 2 +- client/src/assets/i18n/de.json | 150 ++++++++++++----------- client/src/assets/i18n/line/de.json | 2 +- client/src/assets/i18n/linePath/de.json | 2 +- client/src/assets/i18n/maps/de.json | 2 +- client/src/assets/i18n/sector/de.json | 2 +- client/src/assets/i18n/topoImage/de.json | 2 +- 8 files changed, 83 insertions(+), 81 deletions(-) diff --git a/client/src/assets/i18n/area/de.json b/client/src/assets/i18n/area/de.json index 82eb4f05..6ff60779 100644 --- a/client/src/assets/i18n/area/de.json +++ b/client/src/assets/i18n/area/de.json @@ -38,4 +38,4 @@ "areaForm.lngLabel": "Längengrad", "areaForm.lngPlaceholder": "z.B. 7.26605", "areaForm.invalidLng": "Ungültiger Längengrad" -} +} \ No newline at end of file diff --git a/client/src/assets/i18n/crag/de.json b/client/src/assets/i18n/crag/de.json index e15f67cb..8901075c 100644 --- a/client/src/assets/i18n/crag/de.json +++ b/client/src/assets/i18n/crag/de.json @@ -39,4 +39,4 @@ "cragForm.invalidLng": "Ungültiger Längengrad", "crag.cragRules": "Gebietsregeln", "cragList.cragListTitle": "Gebiete" -} +} \ No newline at end of file diff --git a/client/src/assets/i18n/de.json b/client/src/assets/i18n/de.json index e94c1c30..90f55a64 100644 --- a/client/src/assets/i18n/de.json +++ b/client/src/assets/i18n/de.json @@ -357,40 +357,40 @@ "ascentCount.ascents": "geloggte Begehungen", "archiveButton.archive": "Archivieren", "archiveButton.unarchive": "Wiederherstellen", - "clipboardSuccessToastTitle": "In Zwischenablage kopiert", - "clipboardSuccessToastDescription": "Der Text wurde erfolgreich in die Zwischenablage kopiert.", - "clipboardErrorToastTitle": "Fehler", - "clipboardErrorToastDescription": "Der Text konnte nicht in die Zwischenablage kopiert werden.", - "notifications.INSTANCE_SETTINGS_ERROR_MIGRATION_IMPOSSIBLE_TITLE": "Fehler beim Speichern", - "notifications.INSTANCE_SETTINGS_ERROR_MIGRATION_IMPOSSIBLE_MESSAGE": "Existierende Objekte verhindern eine Veränderung der Hierarchieebenen.", - "notifications.MAP_MARKER_ADDED_TITLE": "Marker hinzugefügt", - "notifications.MAP_MARKER_ADDED_MESSAGE": "Der Marker wurde erfolgreich hinzugefügt.", "notifications.MAP_MARKER_REMOVED_TITLE": "Marker entfernt", "notifications.MAP_MARKER_REMOVED_MESSAGE": "Der Marker wurde erfolgreich entfernt.", + "notifications.MAP_MARKER_ADDED_TITLE": "Marker hinzugefügt", + "notifications.MAP_MARKER_ADDED_MESSAGE": "Der Marker wurde erfolgreich hinzugefügt.", + "notifications.GALLERY_IMAGE_CREATED_TITLE": "Bild hinzugefügt", + "notifications.GALLERY_IMAGE_CREATED_MESSAGE": "Das Bild wurde erfolgreich hinzugefügt.", "notifications.GALLERY_IMAGE_UPDATED_TITLE": "Bild gespeichert", "notifications.GALLERY_IMAGE_UPDATED_MESSAGE": "Das Bild wurde erfolgreich gespeichert.", "notifications.GALLERY_IMAGE_DELETED_TITLE": "Bild gelöscht", "notifications.GALLERY_IMAGE_DELETED_MESSAGE": "Das Bild wurde erfolgreich gelöscht.", - "notifications.GALLERY_IMAGE_CREATED_TITLE": "Bild hinzugefügt", - "notifications.GALLERY_IMAGE_CREATED_MESSAGE": "Das Bild wurde erfolgreich hinzugefügt.", "notifications.PROJECT_CLIMBED_MESSAGE_SENT_TITLE": "Nachricht gesendet", "notifications.PROJECT_CLIMBED_MESSAGE_SENT_MESSAGE": "Deine Nachricht wurde erfolgreich versendet.", - "notifications.TODO_ADD_ERROR_TITLE": "Todo hinzufügen fehlgeschlagen", - "notifications.TODO_ADD_ERROR_MESSAGE": "Die Linie konnte nicht auf Deine Todo-Liste gesetzt werden.", "notifications.TODO_DELETED_TITLE": "Todo gelöscht", "notifications.TODO_DELETED_MESSAGE": "Das Todo wurde erfolgreich gelöscht.", "notifications.TODO_PRIORITY_UPDATED_TITLE": "Todo Priorität geändert", "notifications.TODO_PRIORITY_UPDATED_MESSAGE": "Die Priorität des Todos wurde erfolgreich geändert.", + "notifications.TODO_ADD_ERROR_TITLE": "Todo hinzufügen fehlgeschlagen", + "notifications.TODO_ADD_ERROR_MESSAGE": "Die Linie konnte nicht auf Deine Todo-Liste gesetzt werden.", "notifications.TODO_ADDED_TITLE": "Todo hinzugefügt", "notifications.TODO_ADDED_MESSAGE": "Die Linie wurde erfolgreich auf Deine Todo-Liste gesetzt.", - "notifications.LINE_UPDATED_TITLE": "Linie gespeichert", - "notifications.LINE_UPDATED_MESSAGE": "Die Linie wurde erfolgreich gespeichert.", - "notifications.ASCENT_DELETED_TITLE": "Begehung gelöscht", - "notifications.ASCENT_DELETED_MESSAGE": "Die Begehung wurde erfolgreicvh gelöscht.", "notifications.ASCENT_ADDED_TITLE": "Begehung geloggt", "notifications.ASCENT_ADDED_MESSAGE": "Deine Begehung wurde erfolgreich geloggt.", "notifications.ASCENT_UPDATED_TITLE": "Änderungen gespeichert", "notifications.ASCENT_UPDATED_MESSAGE": "Die Änderungen der Begehung wurden gepsichert.", + "notifications.ASCENT_DELETED_TITLE": "Begehung gelöscht", + "notifications.ASCENT_DELETED_MESSAGE": "Die Begehung wurde erfolgreicvh gelöscht.", + "notifications.ARCHIVED_TITLE": "Archiv", + "notifications.ARCHIVED_MESSAGE": "Objekt(e) erfolgreich archiviert", + "notifications.UNARCHIVED_TITLE": "Archiv", + "notifications.UNARCHIVED_MESSAGE": "Objekt(e) erfolgreich wiederhergestellt", + "notifications.ARCHIVED_ERROR_TITLE": "Archiv", + "notifications.ARCHIVED_ERROR_MESSAGE": "Fehler beim Archivieren", + "notifications.UNARCHIVED_ERROR_TITLE": "Archiv", + "notifications.UNARCHIVED_ERROR_MESSAGE": "Fehler beim Wiederherstellen", "notifications.LOGIN_ERROR_TITLE": "Login fehlgeschlagen", "notifications.LOGIN_ERROR_MESSAGE": "Sind E-Mail und Passwort korrekt?", "notifications.USER_PROMOTED_TITLE": "Benutzerrechte geändert", @@ -401,60 +401,12 @@ "notifications.CREATE_USER_MAIL_SENT_MESSAGE": "Die Mail wurde erneut versendet.", "notifications.ACCOUNT_SETTINGS_UPDATED_TITLE": "Account Einstellungen gespeichert", "notifications.ACCOUNT_SETTINGS_UPDATED_MESSAGE": "Die Account Einstellungen wurden erfolgreich gespeichert.", - "notifications.USER_REGISTERED_TITLE": "Account erstellt", - "notifications.USER_REGISTERED_MESSAGE": "Überprüfe Deine E-Mails zum Aktivieren des Accounts.", "notifications.INSTANCE_SETTINGS_UPDATED_TITLE": "Einstellungen gespeichert", "notifications.INSTANCE_SETTINGS_UPDATED_MESSAGE": "Die Instanzeinstellungen wurden erfolgreich gespeichert.", - "notifications.MENU_ITEM_DELETED_TITLE": "Menüpunkt gelöscht", - "notifications.MENU_ITEM_DELETED_MESSAGE": "Der Menüpunkt wurde erfolgreich gelöscht.", - "notifications.MENU_ITEM_CREATED_TITLE": "Menüpunkt erstellt", - "notifications.MENU_ITEM_CREATED_MESSAGE": "Der Menüpunkt wurde erfolgreich erstellt.", - "notifications.MENU_ITEM_UPDATED_TITLE": "Menüpunkt gespeichert", - "notifications.MENU_ITEM_UPDATED_MESSAGE": "Der Menüpunkt wurde erfolgreich gespeichert.", - "notifications.MENU_PAGE_DELETED_TITLE": "Menü Seite gelöscht", - "notifications.MENU_PAGE_DELETED_MESSAGE": "Die Menü Seite wurde erfolgreich gelöscht.", - "notifications.MENU_PAGE_CREATED_TITLE": "Menü Seite erstellt", - "notifications.MENU_PAGE_CREATED_MESSAGE": "Die Menü Seite wurde erfolgreich erstellt.", - "notifications.MENU_PAGE_UPDATED_TITLE": "Menü Seite gespeichert", - "notifications.MENU_PAGE_UPDATED_MESSAGE": "Die Menü Seite wurde erfolgreich gespeichert.", - "notifications.TOPO_IMAGE_UPDATED_TITLE": "Topo Bild gespeichert", - "notifications.TOPO_IMAGE_UPDATED_MESSAGE": "Das Topo Bild wurde erfolgreich gespeichert.", + "notifications.INSTANCE_SETTINGS_ERROR_MIGRATION_IMPOSSIBLE_TITLE": "Fehler beim Speichern", + "notifications.INSTANCE_SETTINGS_ERROR_MIGRATION_IMPOSSIBLE_MESSAGE": "Existierende Objekte verhindern eine Veränderung der Hierarchieebenen.", "notifications.REGION_UPDATED_TITLE": "Region gespeichert", "notifications.REGION_UPDATED_MESSAGE": "Deine Änderungen wurden erfolgreich gespeichert.", - "notifications.POST_DELETED_TITLE": "Post gelöscht", - "notifications.POST_DELETED_MESSAGE": "Der Post wurde erfolgreich gelöscht.", - "notifications.POST_UPDATED_TITLE": "Post gespeichert.", - "notifications.POST_UPDATED_MESSAGE": "Deine Änderungen wurden erfolgreich gespeichert.", - "notifications.POST_CREATED_TITLE": "Post erstellt", - "notifications.POST_CREATED_MESSAGE": "Der Post wurde erfolgreich erstellt.", - "notifications.LINE_CREATED_TITLE": "Linie erstellt", - "notifications.LINE_CREATED_MESSAGE": "Die Linie wurde erfolgreich erstellt.", - "notifications.LINE_PATH_DELETED_TITLE": "Linienverlauf gelöscht", - "notifications.LINE_PATH_DELETED_MESSAGE": "Der Linienverlauf wurde erfolgreich gelöscht.", - "notifications.LINE_PATH_ADDED_TITLE": "Linie eingezeichnet", - "notifications.LINE_PATH_ADDED_MESSAGE": "Die Linie wurde erfolgreich eingezeichnet.", - "notifications.TOPO_IMAGE_DELETED_TITLE": "Topo Bild gelöscht", - "notifications.TOPO_IMAGE_DELETED_MESSAGE": "Du hast das Topo Bild erfolgreich gelöscht.", - "notifications.TOPO_IMAGE_ADDED_TITLE": "Topo Bild hinzugefügt", - "notifications.TOPO_IMAGE_ADDED_MESSAGE": "Das Topo Bild wurde erfolgreich hinzugefügt.", - "notifications.AREA_DELETED_TITLE": "Bereich gelöscht", - "notifications.AREA_DELETED_MESSAGE": "Du hast den Bereich erfolgreich gelöscht.", - "notifications.AREA_UPDATED_TITLE": "Bereich gespeichert", - "notifications.AREA_UPDATED_MESSAGE": "Deine Änderungen wurden erfolgreich gespeichert.", - "notifications.AREA_CREATED_TITLE": "Bereich erstellt", - "notifications.AREA_CREATED_MESSAGE": "Der Bereich wurde erfolgreich erstellt.", - "notifications.SECTOR_DELETED_TITLE": "Sektor gelöscht", - "notifications.SECTOR_DELETED_MESSAGE": "Du hast den Sektor erfolgreich gelöscht.", - "notifications.SECTOR_UPDATED_TITLE": "Sektor gespeichert", - "notifications.SECTOR_UPDATED_MESSAGE": "Deine Änderungen wurden erfolgreich gespeichert.", - "notifications.SECTOR_CREATED_TITLE": "Sektor erstellt", - "notifications.SECTOR_CREATED_MESSAGE": "Der Sektor wurde erfolgreich erstellt.", - "notifications.CRAG_DELETED_TITLE": "Gebiet gelöscht", - "notifications.CRAG_DELETED_MESSAGE": "Du hast das Gebiet erfolgreich gelöscht.", - "notifications.CRAG_UPDATED_TITLE": "Gebiet gespeichert", - "notifications.CRAG_UPDATED_MESSAGE": "Deine Änderungen wurden erfolgreich gespeichert.", - "notifications.CRAG_CREATED_TITLE": "Gebiet erstellt", - "notifications.CRAG_CREATED_MESSAGE": "Das Gebiet wurde erfolgreich erstellt.", "notifications.USER_NOT_ACTIVATED_TITLE": "Benutzer nicht aktiviert", "notifications.USER_NOT_ACTIVATED_MESSAGE": "Der Benutzer ist noch nicht aktiviert.", "notifications.LOGIN_SUCCESS_TITLE": "Login erfolgreich", @@ -477,14 +429,60 @@ "notifications.UNKNOWN_ERROR_MESSAGE": "Hier ist etwas schief gelaufen.", "notifications.LOG_OUT_TO_USE_THIS_FUNCTION_TITLE": "Abmelden zum Nutzen", "notifications.LOG_OUT_TO_USE_THIS_FUNCTION_MESSAGE": "Diese Funktion kann nur von ausgeloggten Benutzern genutzt werden.", - "notifications.ARCHIVED_TITLE": "Archiv", - "notifications.ARCHIVED_MESSAGE": "Objekt(e) erfolgreich archiviert", - "notifications.UNARCHIVED_TITLE": "Archiv", - "notifications.UNARCHIVED_MESSAGE": "Objekt(e) erfolgreich wiederhergestellt", - "notifications.ARCHIVED_ERROR_TITLE": "Archiv", - "notifications.ARCHIVED_ERROR_MESSAGE": "Fehler beim Archivieren", - "notifications.UNARCHIVED_ERROR_TITLE": "Archiv", - "notifications.UNARCHIVED_ERROR_MESSAGE": "Fehler beim Wiederherstellen", + "notifications.CRAG_CREATED_TITLE": "Gebiet erstellt", + "notifications.CRAG_CREATED_MESSAGE": "Das Gebiet wurde erfolgreich erstellt.", + "notifications.CRAG_UPDATED_TITLE": "Gebiet gespeichert", + "notifications.CRAG_UPDATED_MESSAGE": "Deine Änderungen wurden erfolgreich gespeichert.", + "notifications.CRAG_DELETED_TITLE": "Gebiet gelöscht", + "notifications.CRAG_DELETED_MESSAGE": "Du hast das Gebiet erfolgreich gelöscht.", + "notifications.SECTOR_CREATED_TITLE": "Sektor erstellt", + "notifications.SECTOR_CREATED_MESSAGE": "Der Sektor wurde erfolgreich erstellt.", + "notifications.SECTOR_UPDATED_TITLE": "Sektor gespeichert", + "notifications.SECTOR_UPDATED_MESSAGE": "Deine Änderungen wurden erfolgreich gespeichert.", + "notifications.SECTOR_DELETED_TITLE": "Sektor gelöscht", + "notifications.SECTOR_DELETED_MESSAGE": "Du hast den Sektor erfolgreich gelöscht.", + "notifications.AREA_CREATED_TITLE": "Bereich erstellt", + "notifications.AREA_CREATED_MESSAGE": "Der Bereich wurde erfolgreich erstellt.", + "notifications.AREA_UPDATED_TITLE": "Bereich gespeichert", + "notifications.AREA_UPDATED_MESSAGE": "Deine Änderungen wurden erfolgreich gespeichert.", + "notifications.AREA_DELETED_TITLE": "Bereich gelöscht", + "notifications.AREA_DELETED_MESSAGE": "Du hast den Bereich erfolgreich gelöscht.", + "notifications.LINE_CREATED_TITLE": "Linie erstellt", + "notifications.LINE_CREATED_MESSAGE": "Die Linie wurde erfolgreich erstellt.", + "notifications.LINE_UPDATED_TITLE": "Linie gespeichert", + "notifications.LINE_UPDATED_MESSAGE": "Die Linie wurde erfolgreich gespeichert.", + "notifications.LINE_DELETED_TITLE": "Linie gelöscht", + "notifications.LINE_DELETED_MESSAGE": "Die Linie wurde erfolgreich gelöscht.", + "notifications.POST_CREATED_TITLE": "Post erstellt", + "notifications.POST_CREATED_MESSAGE": "Der Post wurde erfolgreich erstellt.", + "notifications.POST_UPDATED_TITLE": "Post gespeichert.", + "notifications.POST_UPDATED_MESSAGE": "Deine Änderungen wurden erfolgreich gespeichert.", + "notifications.POST_DELETED_TITLE": "Post gelöscht", + "notifications.POST_DELETED_MESSAGE": "Der Post wurde erfolgreich gelöscht.", + "notifications.TOPO_IMAGE_ADDED_TITLE": "Topo Bild hinzugefügt", + "notifications.TOPO_IMAGE_ADDED_MESSAGE": "Das Topo Bild wurde erfolgreich hinzugefügt.", + "notifications.TOPO_IMAGE_UPDATED_TITLE": "Topo Bild gespeichert", + "notifications.TOPO_IMAGE_UPDATED_MESSAGE": "Das Topo Bild wurde erfolgreich gespeichert.", + "notifications.TOPO_IMAGE_DELETED_TITLE": "Topo Bild gelöscht", + "notifications.TOPO_IMAGE_DELETED_MESSAGE": "Du hast das Topo Bild erfolgreich gelöscht.", + "notifications.LINE_PATH_ADDED_TITLE": "Linie eingezeichnet", + "notifications.LINE_PATH_ADDED_MESSAGE": "Die Linie wurde erfolgreich eingezeichnet.", + "notifications.LINE_PATH_DELETED_TITLE": "Linienverlauf gelöscht", + "notifications.LINE_PATH_DELETED_MESSAGE": "Der Linienverlauf wurde erfolgreich gelöscht.", + "notifications.MENU_PAGE_DELETED_TITLE": "Menü Seite gelöscht", + "notifications.MENU_PAGE_DELETED_MESSAGE": "Die Menü Seite wurde erfolgreich gelöscht.", + "notifications.MENU_PAGE_UPDATED_TITLE": "Menü Seite gespeichert", + "notifications.MENU_PAGE_UPDATED_MESSAGE": "Die Menü Seite wurde erfolgreich gespeichert.", + "notifications.MENU_PAGE_CREATED_TITLE": "Menü Seite erstellt", + "notifications.MENU_PAGE_CREATED_MESSAGE": "Die Menü Seite wurde erfolgreich erstellt.", + "notifications.MENU_ITEM_DELETED_TITLE": "Menüpunkt gelöscht", + "notifications.MENU_ITEM_DELETED_MESSAGE": "Der Menüpunkt wurde erfolgreich gelöscht.", + "notifications.MENU_ITEM_UPDATED_TITLE": "Menüpunkt gespeichert", + "notifications.MENU_ITEM_UPDATED_MESSAGE": "Der Menüpunkt wurde erfolgreich gespeichert.", + "notifications.MENU_ITEM_CREATED_TITLE": "Menüpunkt erstellt", + "notifications.MENU_ITEM_CREATED_MESSAGE": "Der Menüpunkt wurde erfolgreich erstellt.", + "notifications.USER_REGISTERED_TITLE": "Account erstellt", + "notifications.USER_REGISTERED_MESSAGE": "Überprüfe Deine E-Mails zum Aktivieren des Accounts.", "notifications.SCALE_CREATED_TITLE": "Erstellt", "notifications.SCALE_CREATED_MESSAGE": "Skala erfolreich erstellt", "notifications.SCALE_CREATED_ERROR_TITLE": "Fehler beim Erstellen", @@ -497,6 +495,10 @@ "notifications.SCALE_DELETED_MESSAGE": "Skala erfolreich entfernt", "notifications.SCALE_DELETED_ERROR_TITLE": "Fehler beim Entfernen", "notifications.SCALE_DELETED_ERROR_MESSAGE": "Beim Entfernen ist ein Fehler aufgetreten. Wird die Skala noch von Linien oder Hierarchieebenen genutzt?", + "clipboardSuccessToastTitle": "In Zwischenablage kopiert", + "clipboardSuccessToastDescription": "Der Text wurde erfolgreich in die Zwischenablage kopiert.", + "clipboardErrorToastTitle": "Fehler", + "clipboardErrorToastDescription": "Der Text konnte nicht in die Zwischenablage kopiert werden.", "sortAZ": "A..Z", "sortZA": "Z..A", "usersMenu.promoteToUser": "Zu normalem Benutzer ernennen", diff --git a/client/src/assets/i18n/line/de.json b/client/src/assets/i18n/line/de.json index 483a1f46..f7372ec0 100644 --- a/client/src/assets/i18n/line/de.json +++ b/client/src/assets/i18n/line/de.json @@ -114,4 +114,4 @@ "lineBoolPropList.sitstart": "Sitzstart", "lineForm.lineSitstartLabel": "Sitzstart", "lineList.lineListTitle": "Linien" -} +} \ No newline at end of file diff --git a/client/src/assets/i18n/linePath/de.json b/client/src/assets/i18n/linePath/de.json index 21f3493b..a5714e90 100644 --- a/client/src/assets/i18n/linePath/de.json +++ b/client/src/assets/i18n/linePath/de.json @@ -9,4 +9,4 @@ "linePathForm.leaveEditorButtonLabel": "Editor verlassen", "linePathForm.addLinePathTitle": "Linie einzeichnen", "linePathForm.cancelButtonLabel": "Abbrechen" -} +} \ No newline at end of file diff --git a/client/src/assets/i18n/maps/de.json b/client/src/assets/i18n/maps/de.json index 545a2704..ad2a8f99 100644 --- a/client/src/assets/i18n/maps/de.json +++ b/client/src/assets/i18n/maps/de.json @@ -30,4 +30,4 @@ "mapMarkerForm.PARKING": "Parkplatz", "mapMarkerForm.ACCESS_POINT": "Zustiegspunkt", "mapMarkerForm.OTHER": "Verschiedenes" -} +} \ No newline at end of file diff --git a/client/src/assets/i18n/sector/de.json b/client/src/assets/i18n/sector/de.json index 412d5ea1..53fb9757 100644 --- a/client/src/assets/i18n/sector/de.json +++ b/client/src/assets/i18n/sector/de.json @@ -38,4 +38,4 @@ "sectorForm.lngLabel": "Längengrad", "sectorForm.lngPlaceholder": "z.B. 7.26605", "sectorForm.invalidLng": "Ungültiger Längengrad" -} +} \ No newline at end of file diff --git a/client/src/assets/i18n/topoImage/de.json b/client/src/assets/i18n/topoImage/de.json index b04f9c4f..3a3a1b76 100644 --- a/client/src/assets/i18n/topoImage/de.json +++ b/client/src/assets/i18n/topoImage/de.json @@ -30,4 +30,4 @@ "topoImageForm.invalidLng": "Ungültiger Längengrad", "topoImageForm.topoImageImageImageLabel": "Bild", "topoImageList.deleteTopoImageButtonLabel": "Löschen" -} +} \ No newline at end of file From 5afe29d6d0fd44b5653cc92bb7c9d16de0fbeb26 Mon Sep 17 00:00:00 2001 From: Felix Engelmann Date: Sun, 16 Feb 2025 16:38:12 +0100 Subject: [PATCH 17/18] Prettier --- client/package.json | 3 +++ client/src/assets/i18n/area/de.json | 2 +- client/src/assets/i18n/crag/de.json | 2 +- client/src/assets/i18n/line/de.json | 2 +- client/src/assets/i18n/linePath/de.json | 2 +- client/src/assets/i18n/maps/de.json | 2 +- client/src/assets/i18n/sector/de.json | 2 +- client/src/assets/i18n/topoImage/de.json | 2 +- 8 files changed, 10 insertions(+), 7 deletions(-) diff --git a/client/package.json b/client/package.json index 701d3dc2..77a711cf 100644 --- a/client/package.json +++ b/client/package.json @@ -84,6 +84,9 @@ "**/*.{js,ts}": [ "prettier --write .", "eslint" + ], + "**/*.{json}": [ + "prettier --write ." ] } } diff --git a/client/src/assets/i18n/area/de.json b/client/src/assets/i18n/area/de.json index 6ff60779..82eb4f05 100644 --- a/client/src/assets/i18n/area/de.json +++ b/client/src/assets/i18n/area/de.json @@ -38,4 +38,4 @@ "areaForm.lngLabel": "Längengrad", "areaForm.lngPlaceholder": "z.B. 7.26605", "areaForm.invalidLng": "Ungültiger Längengrad" -} \ No newline at end of file +} diff --git a/client/src/assets/i18n/crag/de.json b/client/src/assets/i18n/crag/de.json index 8901075c..e15f67cb 100644 --- a/client/src/assets/i18n/crag/de.json +++ b/client/src/assets/i18n/crag/de.json @@ -39,4 +39,4 @@ "cragForm.invalidLng": "Ungültiger Längengrad", "crag.cragRules": "Gebietsregeln", "cragList.cragListTitle": "Gebiete" -} \ No newline at end of file +} diff --git a/client/src/assets/i18n/line/de.json b/client/src/assets/i18n/line/de.json index f7372ec0..483a1f46 100644 --- a/client/src/assets/i18n/line/de.json +++ b/client/src/assets/i18n/line/de.json @@ -114,4 +114,4 @@ "lineBoolPropList.sitstart": "Sitzstart", "lineForm.lineSitstartLabel": "Sitzstart", "lineList.lineListTitle": "Linien" -} \ No newline at end of file +} diff --git a/client/src/assets/i18n/linePath/de.json b/client/src/assets/i18n/linePath/de.json index a5714e90..21f3493b 100644 --- a/client/src/assets/i18n/linePath/de.json +++ b/client/src/assets/i18n/linePath/de.json @@ -9,4 +9,4 @@ "linePathForm.leaveEditorButtonLabel": "Editor verlassen", "linePathForm.addLinePathTitle": "Linie einzeichnen", "linePathForm.cancelButtonLabel": "Abbrechen" -} \ No newline at end of file +} diff --git a/client/src/assets/i18n/maps/de.json b/client/src/assets/i18n/maps/de.json index ad2a8f99..545a2704 100644 --- a/client/src/assets/i18n/maps/de.json +++ b/client/src/assets/i18n/maps/de.json @@ -30,4 +30,4 @@ "mapMarkerForm.PARKING": "Parkplatz", "mapMarkerForm.ACCESS_POINT": "Zustiegspunkt", "mapMarkerForm.OTHER": "Verschiedenes" -} \ No newline at end of file +} diff --git a/client/src/assets/i18n/sector/de.json b/client/src/assets/i18n/sector/de.json index 53fb9757..412d5ea1 100644 --- a/client/src/assets/i18n/sector/de.json +++ b/client/src/assets/i18n/sector/de.json @@ -38,4 +38,4 @@ "sectorForm.lngLabel": "Längengrad", "sectorForm.lngPlaceholder": "z.B. 7.26605", "sectorForm.invalidLng": "Ungültiger Längengrad" -} \ No newline at end of file +} diff --git a/client/src/assets/i18n/topoImage/de.json b/client/src/assets/i18n/topoImage/de.json index 3a3a1b76..b04f9c4f 100644 --- a/client/src/assets/i18n/topoImage/de.json +++ b/client/src/assets/i18n/topoImage/de.json @@ -30,4 +30,4 @@ "topoImageForm.invalidLng": "Ungültiger Längengrad", "topoImageForm.topoImageImageImageLabel": "Bild", "topoImageList.deleteTopoImageButtonLabel": "Löschen" -} \ No newline at end of file +} From 87e4f43987d233727b843495ae56db33ad85e959 Mon Sep 17 00:00:00 2001 From: Felix Engelmann Date: Sun, 16 Feb 2025 16:39:09 +0100 Subject: [PATCH 18/18] Prettier --- client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/package.json b/client/package.json index 77a711cf..360c7be6 100644 --- a/client/package.json +++ b/client/package.json @@ -85,7 +85,7 @@ "prettier --write .", "eslint" ], - "**/*.{json}": [ + "src/assets/i18n/**/*.{json}": [ "prettier --write ." ] }