Skip to content
This repository was archived by the owner on Sep 10, 2025. It is now read-only.

Commit 5deb98d

Browse files
committed
Removed some overhead from the list system
1 parent 031667e commit 5deb98d

File tree

11 files changed

+238
-132
lines changed

11 files changed

+238
-132
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<div class="container">
2+
3+
<mat-progress-bar *ngIf="!loaded" mode="indeterminate"></mat-progress-bar>
4+
5+
<div *ngIf="loaded && values.length == 0" class="empty-list-content">
6+
<mat-icon class="empty-list-icon">folder_open</mat-icon>
7+
<button (click)="addPressed.emit()" mat-flat-button>Create first entry</button>
8+
</div>
9+
10+
<list
11+
#list
12+
*ngIf="loaded && values.length > 0"
13+
[height]="300"
14+
[itemInfo]="itemInfo()"
15+
[selectionType]="SelectionType.MULTIPLE" [values]="values">
16+
<div style="margin-left: auto">
17+
<button (click)="deletePressed.emit(list.selected)" *ngIf="list.selected.length > 0" mat-icon-button>
18+
<mat-icon>delete</mat-icon>
19+
</button>
20+
<button (click)="addPressed.emit()" mat-icon-button>
21+
<mat-icon>add</mat-icon>
22+
</button>
23+
</div>
24+
</list>
25+
</div>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
.container {
2+
height: 500px;
3+
4+
mat-progress-bar {
5+
width: 50%;
6+
margin: 0;
7+
position: absolute;
8+
top: 50%;
9+
left: 50%;
10+
transform: translate(-50%, -50%);
11+
}
12+
13+
.empty-list-content {
14+
display: flex;
15+
flex-direction: column;
16+
align-items: center;
17+
justify-content: center;
18+
height: 100%;
19+
text-align: center;
20+
21+
.mat-icon {
22+
transform: scale(4);
23+
padding: 30px;
24+
-webkit-text-fill-color: var(--widget-color);
25+
}
26+
27+
button {
28+
margin-top: 8px;
29+
}
30+
}
31+
32+
}
33+
34+
35+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { EntityListComponent } from './entity-list.component';
4+
5+
describe('EntityListComponent', () => {
6+
let component: EntityListComponent;
7+
let fixture: ComponentFixture<EntityListComponent>;
8+
9+
beforeEach(async () => {
10+
await TestBed.configureTestingModule({
11+
imports: [EntityListComponent]
12+
})
13+
.compileComponents();
14+
15+
fixture = TestBed.createComponent(EntityListComponent);
16+
component = fixture.componentInstance;
17+
fixture.detectChanges();
18+
});
19+
20+
it('should create', () => {
21+
expect(component).toBeTruthy();
22+
});
23+
});
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import {AfterViewInit, Component, EventEmitter, input, InputSignal, Output} from '@angular/core';
2+
import {MatIcon} from "@angular/material/icon";
3+
import {AbstractList, ListItemInfo, SelectionType} from "../../common/abstract-list/abstract-list.component";
4+
import {NgIf} from "@angular/common";
5+
import {MatProgressBar} from "@angular/material/progress-bar";
6+
import {MatButton, MatIconButton} from "@angular/material/button";
7+
import {EntityService} from "../entity-service";
8+
9+
@Component({
10+
selector: 'app-entity-list',
11+
imports: [
12+
MatIcon,
13+
AbstractList,
14+
NgIf,
15+
MatProgressBar,
16+
MatButton,
17+
MatIconButton
18+
],
19+
templateUrl: './entity-list.component.html',
20+
styleUrl: './entity-list.component.scss'
21+
})
22+
export class EntityListComponent<T extends { id: any }> implements AfterViewInit {
23+
24+
protected readonly SelectionType: typeof SelectionType = SelectionType;
25+
@Output() public readonly addPressed: EventEmitter<void> = new EventEmitter<void>;
26+
@Output() public readonly deletePressed: EventEmitter<T[]> = new EventEmitter<T[]>;
27+
28+
public readonly service: InputSignal<EntityService<any, T, any, any> | null> = input<EntityService<any, T, any, any> | null>(null)
29+
public readonly itemInfo: InputSignal<ListItemInfo<T> | null> = input<ListItemInfo<T> | null>(null);
30+
31+
private _values: readonly T[] = [];
32+
33+
public ngAfterViewInit(): void {
34+
if(!this.service())
35+
{
36+
return;
37+
}
38+
this.service()?.value$.subscribe((value: T[]): void => { this._values = value; })
39+
}
40+
41+
protected get loaded(): boolean
42+
{
43+
return this.service()?.fetched || false;
44+
}
45+
46+
protected get values(): readonly T[] {
47+
return this._values;
48+
}
49+
}

EEDU-Frontend/src/app/management/management.component.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ <h1 class="text">Manage users</h1>
5151
{{ tab.label }}
5252
</ng-template>
5353
<ng-template matTabContent>
54-
<ng-container *ngComponentOutlet="tab.component"></ng-container>
54+
<app-entity-list (addPressed)="openDialog(tab.newDialog)" [itemInfo]="tab.itemInfo" [service]="tab.service">
55+
</app-entity-list >
5556
</ng-template>
5657
</mat-tab>
5758
</mat-tab-group>

EEDU-Frontend/src/app/management/management.component.ts

Lines changed: 95 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,53 @@ import {
88
MatExpansionPanelHeader,
99
MatExpansionPanelTitle
1010
} from '@angular/material/expansion';
11-
import {NgComponentOutlet, NgForOf, NgIf} from "@angular/common";
11+
import {NgForOf, NgIf} from "@angular/common";
1212
import {MatTab, MatTabContent, MatTabGroup, MatTabLabel} from "@angular/material/tabs";
1313
import {icons} from "../../environment/styles";
1414
import {
1515
IllnessNotificationModel
1616
} from "../illness-notification/model/illness-notification-model";
17-
import {SubjectListComponent} from "../user/courses/subject/subject-dialogs/subject-dialogs.component";
18-
import {RoomListComponent} from "../user/courses/room/room-dialogs/room-list.component";
19-
import {ClassRoomListComponent} from "../user/courses/classroom/class-room-dialogs/class-room-dialogs.component";
20-
import {CourseListComponent} from "../user/courses/course-dialogs/course-dialogs.component";
2117
import {FileService} from "../file/file.service";
2218
import {MatButton} from "@angular/material/button";
2319
import {MatIcon} from "@angular/material/icon";
2420
import {ManagementService} from "./management.service";
2521
import {IllnessNotificationStatus} from "../illness-notification/illness-notification-status";
22+
import {ListItemInfo} from "../common/abstract-list/abstract-list.component";
23+
import {CourseModel} from "../user/courses/course-model";
24+
import {ClassRoomModel} from "../user/courses/classroom/class-room-model";
25+
import {RoomModel} from "../user/courses/room/room-model";
26+
import {SubjectModel} from "../user/courses/subject/subject-model";
27+
import {EntityListComponent} from "../entity/entity-list/entity-list.component";
28+
import {SubjectService} from "../user/courses/subject/subject.service";
29+
import {RoomService} from "../user/courses/room/room.service";
30+
import {ClassRoomService} from "../user/courses/classroom/class-room.service";
31+
import {CourseService} from "../user/courses/course.service";
32+
import {EntityService} from "../entity/entity-service";
33+
import {ListItemContent} from "../common/abstract-list/list-item-content";
34+
import {CourseListItemComponent} from "../user/courses/course-dialogs/course-list-item/course-list-item.component";
35+
import {ComponentType} from "@angular/cdk/overlay";
36+
import {CreateCourseComponent} from "../user/courses/course-dialogs/course-dialogs.component";
37+
import {DeleteDialogComponent} from "../common/delete-dialog/delete-dialog.component";
38+
import {
39+
CreateSubjectComponent,
40+
DeleteSubjectComponent
41+
} from "../user/courses/subject/subject-dialogs/subject-dialogs.component";
42+
import {
43+
CreateClassRoomComponent,
44+
DeleteClassRoomComponent
45+
} from "../user/courses/classroom/class-room-dialogs/class-room-dialogs.component";
46+
import {CreateRoomComponent, DeleteRoomComponent} from "../user/courses/room/room-dialogs/room-list.component";
47+
import {MatDialog} from "@angular/material/dialog";
2648

2749
export interface CourseTab
2850
{
29-
label: string,
30-
icon: string,
31-
component: Type<any>
51+
label: string;
52+
icon: string;
53+
service: EntityService<any, any, any, any>;
54+
itemInfo: ListItemInfo<any>;
55+
newDialog: ComponentType<any>,
56+
deleteDialog: ComponentType<any>,
57+
content?: Type<ListItemContent<any>>
3258
}
3359

3460

@@ -50,25 +76,72 @@ export interface CourseTab
5076
MatTabLabel,
5177
MatIcon,
5278
NgForOf,
53-
NgComponentOutlet,
79+
EntityListComponent
5480
],
5581
templateUrl: './management.component.html',
5682
styleUrl: './management.component.scss'
5783
})
5884
export class ManagementComponent implements OnInit {
5985

60-
private readonly _courseComponentsTabs: CourseTab[] = [
61-
{ label: 'Courses', icon: icons.course, component: CourseListComponent },
62-
{ label: 'Class Rooms', icon: icons.classroom, component: ClassRoomListComponent },
63-
{ label: 'Rooms', icon: icons.room, component: RoomListComponent },
64-
{ label: 'Subjects', icon: icons.subject, component: SubjectListComponent }
65-
];
86+
private readonly _courseComponentsTabs: CourseTab[];
6687

6788
userList: UserModel[] = [];
6889

6990
illnessNotifications: IllnessNotificationModel[] = []
7091

71-
public constructor(protected managementService: ManagementService, protected userService: UserService, protected fileService: FileService) {
92+
public constructor(
93+
private readonly _matDialog: MatDialog,
94+
protected managementService: ManagementService,
95+
protected userService: UserService,
96+
protected fileService: FileService,
97+
courseService: CourseService,
98+
classRoomService: ClassRoomService,
99+
roomService: RoomService,
100+
subjectService: SubjectService)
101+
{
102+
const idTitle: (obj: { id: string }) => string = (obj: { id: string }): string => obj.id
103+
104+
this._courseComponentsTabs = [{
105+
label: 'Courses',
106+
icon: icons.course,
107+
service: courseService,
108+
newDialog: CreateCourseComponent,
109+
deleteDialog: DeleteDialogComponent,
110+
itemInfo: {
111+
title: (value: CourseModel): string => value.name,
112+
chips: (value: CourseModel): string[] => [
113+
`${value.teacher?.name}`,
114+
`${value.students?.length} Student(s)`, value.subject.id,
115+
`${value.appointmentEntries.length} Appointment(s)`,
116+
`${value.frequentAppointments.length} Frequent Appointment(s)`
117+
]
118+
},
119+
content: CourseListItemComponent
120+
}, {
121+
label: 'Class Rooms',
122+
icon: icons.classroom,
123+
service: classRoomService,
124+
newDialog: CreateClassRoomComponent,
125+
deleteDialog: DeleteClassRoomComponent,
126+
itemInfo: {
127+
title: idTitle,
128+
chips: (value: ClassRoomModel): string[] => [`Tutor: ${value.tutor.name}`, `${value.students.length} Users`]
129+
}
130+
}, {
131+
label: 'Rooms',
132+
icon: icons.room,
133+
service: roomService,
134+
newDialog: CreateRoomComponent,
135+
deleteDialog: DeleteRoomComponent,
136+
itemInfo: { title: idTitle }
137+
}, {
138+
label: 'Subjects',
139+
icon: icons.subject,
140+
service: subjectService,
141+
newDialog: CreateSubjectComponent,
142+
deleteDialog: DeleteSubjectComponent,
143+
itemInfo: { title: idTitle }
144+
}];
72145
}
73146

74147
ngOnInit(): void {
@@ -78,6 +151,11 @@ export class ManagementComponent implements OnInit {
78151
});
79152
}
80153

154+
protected openDialog(dialog: ComponentType<any>)
155+
{
156+
return this._matDialog.open(dialog, {width: '600px', disableClose: true});
157+
}
158+
81159
protected get courseComponentTabs(): CourseTab[] {
82160
return this._courseComponentsTabs;
83161
}
@@ -99,5 +177,6 @@ export class ManagementComponent implements OnInit {
99177
}
100178

101179
protected readonly IllnessNotificationStatus = IllnessNotificationStatus;
180+
protected readonly open = open;
102181
}
103182

EEDU-Frontend/src/app/user/courses/classroom/class-room-dialogs/class-room-dialogs.component.ts

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
import {Component, Inject} from '@angular/core';
2-
import {AbstractList} from "../../../../common/abstract-list/abstract-list.component";
32
import {ClassRoomModel} from "../class-room-model";
43
import {ClassRoomService} from "../class-room.service";
5-
import {MatIcon} from "@angular/material/icon";
6-
import {MAT_DIALOG_DATA, MatDialog, MatDialogClose, MatDialogRef} from "@angular/material/dialog";
7-
import {MatButton, MatIconButton} from "@angular/material/button";
8-
import {NgIf} from "@angular/common";
9-
import {MatProgressBar} from "@angular/material/progress-bar";
10-
import {AbstractCourseComponentList} from "../../abstract-course-components/list/abstract-course-component-list";
4+
import {MAT_DIALOG_DATA, MatDialogClose, MatDialogRef} from "@angular/material/dialog";
5+
import {MatButton} from "@angular/material/button";
116
import {DeleteDialogComponent} from "../../../../common/delete-dialog/delete-dialog.component";
127
import {AbstractDeleteDialog} from "../../abstract-course-components/delete/abstract-delete-dialog";
138
import {CourseModel} from "../../course-model";
@@ -27,21 +22,6 @@ import {UserModel} from "../../../user-model";
2722
import {ClassRoomCreateModel} from "../class-room-create-model";
2823
import {AccountType} from "../../../account-type";
2924

30-
@Component({
31-
imports: [MatProgressBar, AbstractList, MatIconButton, MatButton, MatIcon, NgIf,],
32-
templateUrl: '../../abstract-course-components/list/abstract-course-components-list.html',
33-
styleUrl: '../../abstract-course-components/list/abstract-course-components-list.scss'
34-
})
35-
export class ClassRoomListComponent extends AbstractCourseComponentList<string, ClassRoomModel> {
36-
37-
public constructor(service: ClassRoomService, dialog: MatDialog) {
38-
super(service, dialog, CreateClassRoomComponent, DeleteClassRoomComponent, {
39-
title: (value: ClassRoomModel): string => value.id,
40-
chips: (value: ClassRoomModel): string[] => [`Tutor: ${value.tutor.name}`, `${value.students.length} Users`]
41-
});
42-
}
43-
}
44-
4525
@Component({
4626
imports: [MatCardActions, MatButton, MatDialogClose, ReactiveFormsModule, MatInput, MatLabel, MatFormField, MatCardContent, GeneralCardComponent, SelectionInput],
4727
templateUrl: './create-class-room.component.html',

EEDU-Frontend/src/app/user/courses/course-dialogs/course-dialogs.component.ts

Lines changed: 3 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,8 @@
1-
import {Component, Inject, Type} from '@angular/core';
2-
import {AbstractList} from "../../../common/abstract-list/abstract-list.component";
1+
import {Component, Inject} from '@angular/core';
32
import {CourseModel} from "../course-model";
43
import {CourseService} from "../course.service";
5-
import {MatButton, MatIconButton} from "@angular/material/button";
6-
import {MatIcon} from "@angular/material/icon";
7-
import {MAT_DIALOG_DATA, MatDialog, MatDialogClose, MatDialogRef} from "@angular/material/dialog";
8-
import {NgIf} from "@angular/common";
9-
import {MatProgressBar} from "@angular/material/progress-bar";
10-
import {AbstractCourseComponentList} from "../abstract-course-components/list/abstract-course-component-list";
11-
import {ListItemContent} from "../../../common/abstract-list/list-item-content";
12-
import {CourseListItemComponent} from "./course-list-item/course-list-item.component";
4+
import {MatButton} from "@angular/material/button";
5+
import {MAT_DIALOG_DATA, MatDialogClose, MatDialogRef} from "@angular/material/dialog";
136
import {DeleteDialogComponent} from "../../../common/delete-dialog/delete-dialog.component";
147
import {AbstractDeleteDialog} from "../abstract-course-components/delete/abstract-delete-dialog";
158
import {AccountType} from '../../account-type';
@@ -29,31 +22,6 @@ import {GeneralCardComponent} from "../../../common/general-card-component/gener
2922
import {MatFormField, MatLabel} from "@angular/material/form-field";
3023
import {MatInput} from "@angular/material/input";
3124

32-
@Component({
33-
imports: [MatProgressBar, AbstractList, MatIconButton, MatButton, MatIcon, NgIf],
34-
templateUrl: '../abstract-course-components/list/abstract-course-components-list.html',
35-
styleUrl: '../abstract-course-components/list/abstract-course-components-list.scss'
36-
})
37-
export class CourseListComponent extends AbstractCourseComponentList<bigint, CourseModel> {
38-
39-
public constructor(service: CourseService, dialog: MatDialog) {
40-
super(service, dialog, CreateCourseComponent, DeleteCourseComponent, {
41-
title: (value: CourseModel): string => value.name,
42-
chips: (value: CourseModel): string[] => [
43-
`${value.teacher?.name}`,
44-
`${value.students?.length} Student(s)`,
45-
value.subject.id,
46-
`${value.appointmentEntries.length} Appointment(s)`,
47-
`${value.frequentAppointments.length} Frequent Appointment(s)`,
48-
]
49-
});
50-
}
51-
52-
protected override get content(): Type<ListItemContent<CourseModel>> | null {
53-
return CourseListItemComponent;
54-
}
55-
}
56-
5725
@Component({
5826
imports: [MatCardContent, MatDialogClose, MatLabel, MatFormField, MatInput, GeneralCardComponent, MatCardActions, MatButton, ReactiveFormsModule, SelectionInput],
5927
templateUrl: './create-course.component.html',

0 commit comments

Comments
 (0)