Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
da9c478
fix postgres
mohamedalaaser Feb 24, 2026
ea0ecb1
managing degree programs and specializations
mohamedalaaser Mar 1, 2026
ad4c35f
fix
mohamedalaaser Mar 1, 2026
7d79b14
module creation steps
mohamedalaaser Mar 8, 2026
fc0d56a
fixes
mohamedalaaser Mar 8, 2026
90237af
fix
mohamedalaaser Mar 8, 2026
0541fbe
first iteration without showing feedbacks
mohamedalaaser Mar 17, 2026
3d22936
rename
mohamedalaaser Mar 17, 2026
e6f44e9
show feedbacks
mohamedalaaser Mar 18, 2026
3c1e5a4
invalidate previous feedbacks when needed
mohamedalaaser Mar 18, 2026
f7c20a9
fix
mohamedalaaser Mar 18, 2026
54500e7
imrpove feedback view
mohamedalaaser Mar 19, 2026
d881878
fix
mohamedalaaser Mar 19, 2026
66637dd
display new module fields in feedback
mohamedalaaser Mar 19, 2026
58f7742
imrpove feedback given visualisation
mohamedalaaser Mar 19, 2026
6f7916f
cleanup
mohamedalaaser Mar 19, 2026
b316bb8
ui improvements for feedback messages
mohamedalaaser Mar 20, 2026
a80760e
cleanup stepper
mohamedalaaser Mar 20, 2026
32361b0
fix
mohamedalaaser Mar 20, 2026
5328ea3
stepper feedback status adjustment
mohamedalaaser Mar 21, 2026
674f3ef
proposal and module version status updates
mohamedalaaser Mar 21, 2026
15dd1d4
status adjustment
mohamedalaaser Mar 21, 2026
333834d
fix
mohamedalaaser Mar 21, 2026
6fc6640
Program directors and areas responsibles (#76)
mohamedalaaser Mar 18, 2026
b1cf527
Merge branch 'main' into step-by-step-module-creation
mohamedalaaser Mar 21, 2026
d1c606e
fix
mohamedalaaser Mar 21, 2026
4345e6c
email setup
mohamedalaaser Mar 21, 2026
dfd270c
small comment
mohamedalaaser Mar 21, 2026
5a3669e
no legacy-peer-deps in client docker build
mohamedalaaser Mar 21, 2026
4d27b40
add mailpit to prod
mohamedalaaser Mar 21, 2026
e25feb2
use keycloak passkey extension
mohamedalaaser Mar 21, 2026
d76e944
sign in with passkey
mohamedalaaser Mar 21, 2026
cc620d9
cleanup
mohamedalaaser Mar 21, 2026
fd0a924
passkey popup
mohamedalaaser Mar 21, 2026
1eb42bf
sign in component
mohamedalaaser Mar 22, 2026
9c8682a
db migration
mohamedalaaser Mar 22, 2026
367785b
entity model
mohamedalaaser Mar 22, 2026
5524448
exmaination board crud backend
mohamedalaaser Mar 22, 2026
94ae526
generate openapi
mohamedalaaser Mar 22, 2026
a066770
frontend admin routes
mohamedalaaser Mar 22, 2026
01ca8b8
frontend manage exmination boards
mohamedalaaser Mar 22, 2026
4e1edd0
add separate examination board submission
mohamedalaaser Mar 22, 2026
8386e6b
Merge branch 'main' into examination-board-feedback
mohamedalaaser Apr 30, 2026
896cae9
examination board feedback step done
mohamedalaaser May 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Slim context for docker/keycloak/Dockerfile (build context: Module-Management root)
Client/node_modules
Client/dist
Server/build
Server/.gradle
**/.git
.git
4 changes: 4 additions & 0 deletions Client/src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { UsersPageComponent } from './pages/admin/users/users-page.component';
import { AllDegreeProgramsPageComponent } from './pages/admin/degree-programs/all-degree-programs-page.component';
import { DegreeProgramDetailsPageComponent } from './pages/admin/degree-programs/degree-program-details-page.component';
import { AllSpecializationsPageComponent } from './pages/admin/degree-program-specializations/all-specializations-page.component';
import { ExaminationBoardDetailPageComponent } from './pages/admin/examination-boards/examination-board-detail-page.component';
import { ExaminationBoardsPageComponent } from './pages/admin/examination-boards/examination-boards-page.component';
export const routes: Routes = [
{ path: '', component: IndexComponent },
{
Expand Down Expand Up @@ -55,6 +57,8 @@ export const routes: Routes = [
canActivate: [AuthGuard, AdminGuard],
children: [
{ path: 'users', component: UsersPageComponent },
{ path: 'examination-boards/:id', component: ExaminationBoardDetailPageComponent },
{ path: 'examination-boards', component: ExaminationBoardsPageComponent },
{ path: 'degree-programs/specializations', component: AllSpecializationsPageComponent },
{ path: 'degree-programs/:id', component: DegreeProgramDetailsPageComponent },
{ path: 'degree-programs', component: AllDegreeProgramsPageComponent },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { Injectable, signal } from '@angular/core';
export class BreadcrumbLabelsService {
/** Degree program details page: program name. */
readonly degreeProgramName = signal<string | null>(null);
/** Examination board detail page: board name. */
readonly examinationBoardName = signal<string | null>(null);
/** Proposal/view segment: module title (e.g. from latestModuleVersion.titleEng). */
readonly proposalTitle = signal<string | null>(null);
/** Version segment: e.g. "Version 2" from moduleVersion.version. */
Expand Down
14 changes: 14 additions & 0 deletions Client/src/app/components/breadcrumb/breadcrumb.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,20 @@ export class BreadcrumbComponent {
return items;
}

if (segments[1] === 'examination-boards') {
items.push({ label: 'Examination boards', routerLink: ['/admin/examination-boards'] });
if (segments.length > 2 && segments[2] && segments[2] !== 'specializations') {
const boardId = segments[2];
const label =
(this.breadcrumbLabels.examinationBoardName() ?? '').trim() || `Board ${boardId}`;
items.push({
label,
routerLink: ['/admin/examination-boards', boardId]
});
}
return items;
}

if (segments[1] === 'degree-programs') {
items.push({ label: 'Degree Programs', routerLink: ['/admin/degree-programs'] });
if (segments.length <= 2) return items;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ <h1 class="text-2xl mb-4">{{ moduleVersionDto()?.titleEng ? 'Edit Proposal for '
}
<div class="mt-2"><input pInputText formControlName="successorModuleName" placeholder="Title of the successor module" class="w-full" /></div>
</div>
<!-- <div>
<div>
<label class="text-xl">Level</label>
@if (moduleVersionId && hasFeedback('levelFeedback')) {
@for (feedback of feedbacks(); track feedback.feedbackId) {
Expand All @@ -241,11 +241,11 @@ <h1 class="text-2xl mb-4">{{ moduleVersionDto()?.titleEng ? 'Edit Proposal for '
{ value: 'Master', label: 'Master' },
{ value: 'Bachelor/Master', label: 'Bachelor/Master' }
]"
[required]="false"
[required]="true"
formControlName="levelEng"
></app-toggle-button-group>
</div>
</div> -->
</div>

<!-- <div>
<label class="text-xl">Language</label>
Expand Down Expand Up @@ -765,41 +765,45 @@ <h1 class="text-2xl mb-4">{{ moduleVersionDto()?.titleEng ? 'Edit Proposal for '
<div class="space-y-6">
@if (isCreateMode()) {
<p-message severity="info" variant="outlined">
Complete all steps and get coordinator feedback accepted first. Then you can submit for full feedback (quality management, program advisor, examination board).
Complete all steps and get coordinator feedback accepted first. You will then submit for examination board feedback.
</p-message>
} @else {
<p class="text-lg mb-4">
Submit for full feedback from quality management, academic program advisor, and examination board. This requires all steps to be completed and all program/area
coordinator feedback to be accepted.
</p>
<!-- @if (coordinatorFeedbacksForCurrentAssignments().length > 0 && !allCoordinatorFeedbacksAccepted()) {
<p-message severity="warn" variant="outlined">
All coordinator feedback must be accepted before you can submit for full feedback. Complete the first submission step and wait for approvals.
</p-message>
}
@if (currentVersionFeedbacks().length > 0) {
<p class="text-lg mb-4">Submit for feedback from the examination boards of the degree programs you selected. All the previous steps needs to be completed first.</p>
@if (examinationBoardMemberFeedbacksForStep6().feedbacks.length > 0) {
<div class="mb-4">
<label class="text-xl block mb-2">Feedback status</label>
<label class="text-xl block mb-2">
Examination board feedback status {{ examinationBoardMemberFeedbacksForStep6().fromPrevious ? '(from previous version)' : '(this version)' }}
</label>
<ul class="space-y-2 list-none p-0 m-0">
@for (fb of currentVersionFeedbacks(); track fb.feedbackId) {
<li class="flex items-center gap-2 flex-wrap">
<span class="font-medium">{{ fb | feedbackAuthorDisplay }}:</span>
<p-tag
[value]="(fb.feedbackStatus ?? 'PENDING_FEEDBACK' | feedbackStatus).text"
[severity]="(fb.feedbackStatus ?? 'PENDING_FEEDBACK' | feedbackStatus).severity"
/>
@for (fb of examinationBoardMemberFeedbacksForStep6().feedbacks; track fb.feedbackId) {
<li>
<div class="flex items-center gap-2 flex-wrap">
<span class="font-medium">{{ fb | feedbackAuthorDisplay }}:</span>
<p-tag
[value]="(fb.feedbackStatus ?? 'PENDING_FEEDBACK' | feedbackStatus).text"
[severity]="(fb.feedbackStatus ?? 'PENDING_FEEDBACK' | feedbackStatus).severity"
/>
</div>
@if (fb.feedbackStatus === 'REJECTED' && fb.rejectionComment) {
<feedback-message
[authorDisplay]="fb | feedbackAuthorDisplay"
[authorUserName]="fb.requestedFromUserName ?? null"
[feedbackText]="fb.rejectionComment"
styleClass="mt-2"
/>
}
</li>
}
</ul>
</div>
} -->
@if (canRequestFullFeedback()) {
}
@if (canRequestExaminationBoardFeedback()) {
<p-button
type="button"
severity="success"
label="Submit for full feedback"
label="Submit for examination board feedback"
icon="pi pi-send"
(onClick)="requestFullFeedback()"
(onClick)="requestExaminationBoardFeedback()"
[loading]="loading()"
[disabled]="loading()"
></p-button>
Expand All @@ -817,28 +821,24 @@ <h1 class="text-2xl mb-4">{{ moduleVersionDto()?.titleEng ? 'Edit Proposal for '
@if (isCreateMode()) {
<p-button
type="button"
type="submit"
severity="contrast"
label="Create proposal &amp; continue"
icon="pi pi-arrow-right"
[loading]="loading()"
[disabled]="loading() || !proposalForm.get('titleEng')?.value?.trim()"
(onClick)="onSubmit()"
></p-button>
}
@if (!isCreateMode() && currentStepIndex() < MODULE_EDIT_STEPS.length - 1) {
<p-button type="button" severity="contrast" (onClick)="goToStep(currentStepIndex() + 1)">Next</p-button>
}
@if (!isCreateMode()) {
<p-button type="submit" severity="contrast" [disabled]="loading()">
{{ loading() ? 'Saving...' : 'Update proposal' }}
{{ 'Update proposal' }}
</p-button>
}
</div>
</div>

@if (error()) {
<p-message severity="error" icon="pi pi-exclamation-circle">{{ error() }}</p-message>
}
</form>
</div>
</div>
Loading
Loading