diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 1a29c63..9637082 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -208,6 +208,7 @@ import { CircleParallelMinorPipePipe } from './tools/pipes/circle-parallel-minor
import { MitgliedInstrumenteListComponent } from './components/mitglieder/mitglied-instrumente-list/mitglied-instrumente-list.component';
import { MkjRatingComponent } from './utilities/form-input-components/mkj-rating/mkj-rating.component';
import { MkjUserNotificationsComponent } from './utilities/mkj-user-notifications/mkj-user-notifications.component';
+import { MkjCommentsComponent } from './utilities/mkj-comments/mkj-comments.component';
// FullCalendarModule.registerPlugins([
// dayGridPlugin,
@@ -416,6 +417,7 @@ registerLocaleData(localeDe);
MitgliedInstrumenteListComponent,
MkjRatingComponent,
MkjUserNotificationsComponent,
+ MkjCommentsComponent,
],
providers: [
mkjAppInitializer(),
diff --git a/src/app/components/archiv/noten/noten-overview/noten-overview.component.html b/src/app/components/archiv/noten/noten-overview/noten-overview.component.html
index f417ae7..bbe6d5a 100644
--- a/src/app/components/archiv/noten/noten-overview/noten-overview.component.html
+++ b/src/app/components/archiv/noten/noten-overview/noten-overview.component.html
@@ -23,6 +23,10 @@
(click)="navigateToEditView(noten)"
>
+
+
+
+
diff --git a/src/app/models/Kommentar.ts b/src/app/models/Kommentar.ts
new file mode 100644
index 0000000..d44fc9d
--- /dev/null
+++ b/src/app/models/Kommentar.ts
@@ -0,0 +1,15 @@
+import { ModelType } from './_ModelType';
+
+export interface Kommentar {
+ id?: string;
+ text?: string;
+ commentable_type?: ModelType;
+ commentable_id?: string;
+ mitglied_id?: number;
+ mitglied_name?: string;
+ number_child_comments?: number;
+ parent_comment_id?: string;
+ created_at?: string;
+ updated_at?: string;
+ subComments?: Kommentar[];
+}
diff --git a/src/app/models/_ModelType.ts b/src/app/models/_ModelType.ts
new file mode 100644
index 0000000..86a1fcb
--- /dev/null
+++ b/src/app/models/_ModelType.ts
@@ -0,0 +1,5 @@
+export enum ModelType {
+ Kommentar = 'kommentar',
+ Mitglied = 'mitglied',
+ Noten = 'noten',
+}
diff --git a/src/app/services/api/_generic-field-value-serivce.interface.ts b/src/app/services/api/_generic-field-value-serivce.interface.ts
index 6af624c..02c1b41 100644
--- a/src/app/services/api/_generic-field-value-serivce.interface.ts
+++ b/src/app/services/api/_generic-field-value-serivce.interface.ts
@@ -1,6 +1,5 @@
import { Observable } from 'rxjs';
import { GetListOutput } from 'src/app/interfaces/api-middleware';
-import { KeyOf } from 'src/app/types/KeyOf';
import { GenericFieldValue } from 'src/app/utilities/_list-datasources/generic-field-values-datasource.class';
export interface GenericFieldValueService {
diff --git a/src/app/services/api/kommentar-api.service.ts b/src/app/services/api/kommentar-api.service.ts
new file mode 100644
index 0000000..1d9ddd3
--- /dev/null
+++ b/src/app/services/api/kommentar-api.service.ts
@@ -0,0 +1,15 @@
+import { HttpClient } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { Kommentar } from 'src/app/models/Kommentar';
+import { AbstractCrudApiService } from './_abstract-crud-api-service.class';
+
+@Injectable({
+ providedIn: 'root',
+})
+export class KommentarApiService extends AbstractCrudApiService {
+ protected controllerApiUrlKey = 'kommentare';
+
+ constructor(httpClient: HttpClient) {
+ super(httpClient);
+ }
+}
diff --git a/src/app/services/api/noten-api.service.ts b/src/app/services/api/noten-api.service.ts
index f851eb2..a2c7e41 100644
--- a/src/app/services/api/noten-api.service.ts
+++ b/src/app/services/api/noten-api.service.ts
@@ -6,11 +6,12 @@ import { environment } from 'src/environments/environment';
import { Noten } from '../../models/Noten';
import { AbstractCrudApiService } from './_abstract-crud-api-service.class';
import { KeyOf } from 'src/app/types/KeyOf';
+import { GenericFieldValueService } from './_generic-field-value-serivce.interface';
@Injectable({
providedIn: 'root',
})
-export class NotenApiService extends AbstractCrudApiService {
+export class NotenApiService extends AbstractCrudApiService implements GenericFieldValueService {
protected controllerApiUrlKey: string = 'noten';
private apiURL = environment.apiUrl;
diff --git a/src/app/utilities/_display-model-configurations/display-model-configuration.interface.ts b/src/app/utilities/_display-model-configurations/display-model-configuration.interface.ts
index 41898c2..1230809 100644
--- a/src/app/utilities/_display-model-configurations/display-model-configuration.interface.ts
+++ b/src/app/utilities/_display-model-configurations/display-model-configuration.interface.ts
@@ -1,6 +1,7 @@
export interface DisplayModelConfiguration {
fields: DisplayModelField[];
rateable?: boolean;
+ commentable?: boolean;
}
export interface DisplayModelField {
diff --git a/src/app/utilities/mkj-comments/mkj-comments.component.html b/src/app/utilities/mkj-comments/mkj-comments.component.html
new file mode 100644
index 0000000..66808c9
--- /dev/null
+++ b/src/app/utilities/mkj-comments/mkj-comments.component.html
@@ -0,0 +1,81 @@
+@for (c of comments; track c) {
+
+}
+@if (subCommentId == null) {
+
+
+
+}
+
+
+
+
+
+
+
+
diff --git a/src/app/utilities/mkj-comments/mkj-comments.component.scss b/src/app/utilities/mkj-comments/mkj-comments.component.scss
new file mode 100644
index 0000000..535ba46
--- /dev/null
+++ b/src/app/utilities/mkj-comments/mkj-comments.component.scss
@@ -0,0 +1,53 @@
+.comment {
+ display: flex;
+ flex-direction: column;
+
+ border: 1px solid var(--surface-200);
+ border-radius: 5px;
+
+ &__header {
+ display: flex;
+ gap: 0.5rem;
+ align-items: end;
+ justify-content: space-between;
+ padding: 10px;
+ font-weight: 600;
+ background: var(--surface-200);
+
+ .date {
+ font-weight: normal;
+ opacity: 0.7;
+ }
+ }
+
+ &__text {
+ padding: 10px;
+ }
+
+ &__footer {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 10px;
+ width: 100%;
+ }
+
+ &__subcomments {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ margin-left: 3rem;
+ padding: 10px;
+ }
+
+ &__input_wrapper {
+ padding: 10px;
+ background: var(--surface-200);
+ }
+
+ &__input {
+ display: flex;
+ align-items: center;
+ justify-content: end;
+ }
+}
diff --git a/src/app/utilities/mkj-comments/mkj-comments.component.ts b/src/app/utilities/mkj-comments/mkj-comments.component.ts
new file mode 100644
index 0000000..aa91055
--- /dev/null
+++ b/src/app/utilities/mkj-comments/mkj-comments.component.ts
@@ -0,0 +1,114 @@
+import { Component, Input, OnInit, ViewChild } from '@angular/core';
+import { GetListInput } from 'src/app/interfaces/api-middleware';
+import { Kommentar } from 'src/app/models/Kommentar';
+import { ModelType } from 'src/app/models/_ModelType';
+import { KommentarApiService } from 'src/app/services/api/kommentar-api.service';
+
+@Component({
+ selector: 'mkj-comments',
+ templateUrl: './mkj-comments.component.html',
+ styleUrl: './mkj-comments.component.scss',
+})
+export class MkjCommentsComponent implements OnInit {
+ @Input({ required: true }) modelType: ModelType;
+ @Input({ required: true }) modelId: string;
+
+ @ViewChild('inputTemplate') inputTemplate: any;
+
+ public comments: Kommentar[] = [];
+ public commentText: string;
+ public subCommentId: string;
+
+ public saving = false;
+
+ constructor(private apiService: KommentarApiService) {}
+
+ public ngOnInit(): void {
+ this.getComments();
+ }
+
+ public getComments(parent?: Kommentar): void {
+ if (parent) {
+ (parent as any).loading = true;
+ }
+
+ const input: GetListInput = {
+ filterAnd: [
+ {
+ field: 'commentable_type',
+ value: this.modelType,
+ },
+ {
+ field: 'commentable_id',
+ value: this.modelId,
+ },
+ {
+ field: 'parent_comment_id',
+ value: parent?.id || null,
+ operator: '=',
+ },
+ ],
+ sort: {
+ field: 'updated_at',
+ order: 'asc',
+ },
+ };
+
+ this.apiService.getList(input).subscribe((response) => {
+ if (parent) {
+ parent.subComments = response.values;
+ (parent as any).loading = false;
+ } else {
+ this.comments = response.values;
+ }
+ });
+ }
+
+ public createComment(parent?: Kommentar): void {
+ this.saving = true;
+
+ this.apiService
+ .create({
+ text: this.commentText,
+ commentable_type: this.modelType,
+ commentable_id: this.modelId,
+ parent_comment_id: parent?.id,
+ })
+ .subscribe((response) => {
+ this.insertComment(response);
+ this.saving = false;
+ });
+ }
+
+ public initCommentInput(comment: Kommentar, text?: string): void {
+ this.subCommentId = this.subCommentId === comment.id ? null : comment.id;
+ this.commentText = text || '';
+ }
+
+ private insertComment(comment: Kommentar): void {
+ this.commentText = '';
+ this.subCommentId = null;
+ if (comment.parent_comment_id) {
+ const parent = this.findParentComment(comment, this.comments);
+ if (parent) {
+ parent.subComments = [...(parent.subComments ?? []), comment];
+ parent.number_child_comments++;
+ }
+ } else {
+ this.comments.push(comment);
+ }
+ }
+
+ private findParentComment(c: Kommentar, arr: Kommentar[]): Kommentar {
+ for (const item of arr) {
+ if (item.id === c.parent_comment_id) {
+ return item;
+ } else if (item.subComments) {
+ const result = this.findParentComment(c, item.subComments);
+ if (result) return result;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/app/utilities/mkj-user-notifications/mkj-user-notifications.component.html b/src/app/utilities/mkj-user-notifications/mkj-user-notifications.component.html
index a15a8a6..2f128c3 100644
--- a/src/app/utilities/mkj-user-notifications/mkj-user-notifications.component.html
+++ b/src/app/utilities/mkj-user-notifications/mkj-user-notifications.component.html
@@ -1,6 +1,7 @@