From 6a263b56b1a4689274ec4f2e90a8b46407ed9710 Mon Sep 17 00:00:00 2001 From: disururathnayake Date: Wed, 10 Sep 2025 13:15:41 +1000 Subject: [PATCH 1/5] feat: add task definition toggle --- src/app/api/models/task-definition.ts | 1 + src/app/doubtfire-angular.module.ts | 2 + .../task-definition-editor.component.html | 22 ++++++++++ ...finition-tutorial-enrolment.component.html | 25 ++++++++++++ ...finition-tutorial-enrolment.component.scss | 0 ...definition-tutorial-enrolment.component.ts | 40 +++++++++++++++++++ 6 files changed, 90 insertions(+) create mode 100644 src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.html create mode 100644 src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.scss create mode 100644 src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.ts diff --git a/src/app/api/models/task-definition.ts b/src/app/api/models/task-definition.ts index b848686768..f242f6837e 100644 --- a/src/app/api/models/task-definition.ts +++ b/src/app/api/models/task-definition.ts @@ -37,6 +37,7 @@ export class TaskDefinition extends Entity { scormBypassTest: boolean; scormTimeDelayEnabled: boolean; scormAttemptLimit: number = 0; + tutorialSelfEnrolmentEnabled: boolean; hasTaskAssessmentResources: boolean; isGraded: boolean; maxQualityPts: number; diff --git a/src/app/doubtfire-angular.module.ts b/src/app/doubtfire-angular.module.ts index c714ff0e5a..5ce88ed9cf 100644 --- a/src/app/doubtfire-angular.module.ts +++ b/src/app/doubtfire-angular.module.ts @@ -253,6 +253,7 @@ import {ScormExtensionModalComponent} from './common/modals/scorm-extension-moda import { GradeIconComponent } from './common/grade-icon/grade-icon.component'; import { GradeTaskModalComponent } from './tasks/modals/grade-task-modal/grade-task-modal.component'; import { PrivacyPolicy } from './config/privacy-policy/privacy-policy'; +import { TaskDefinitionTutorialEnrolmentComponent } from './units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component'; // See https://stackoverflow.com/questions/55721254/how-to-change-mat-datepicker-date-format-to-dd-mm-yyyy-in-simplest-way/58189036#58189036 const MY_DATE_FORMAT = { @@ -389,6 +390,7 @@ import { UnitStudentEnrolmentModalComponent } from './units/modals/unit-student- TaskScormCardComponent, ScormExtensionCommentComponent, ScormExtensionModalComponent, + TaskDefinitionTutorialEnrolmentComponent, ], // Services we provide providers: [ diff --git a/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-editor.component.html b/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-editor.component.html index 7d6224f343..a91b67a62a 100644 --- a/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-editor.component.html +++ b/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-editor.component.html @@ -152,7 +152,29 @@

SCORM test

8 +
+

+ Tutorial enrolment form +

+

+ Select the tutorial stream(s) that allow students to enrol in tutorials from +

+
+ +
+
+ +
+
+
+ 9 +
+

Optional settings

Apply other options

diff --git a/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.html b/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.html new file mode 100644 index 0000000000..352cf4021c --- /dev/null +++ b/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.html @@ -0,0 +1,25 @@ +
+ + Enable tutorial self enrolment for task + + + @if (taskDefinition.tutorialSelfEnrolmentEnabled) { +
+ + Tutorial Streams - Which stream students can enrol into a tutorial from? + + @for (stream of unit.tutorialStreams; track stream) { + {{ stream.name }} — ({{ + stream.tutorialsIn(taskDefinition.unit).length + }} + tutorials) + } + + +
+ } +
diff --git a/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.scss b/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.ts b/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.ts new file mode 100644 index 0000000000..4cd5e61ab7 --- /dev/null +++ b/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.ts @@ -0,0 +1,40 @@ +import {Component, Input, OnInit} from '@angular/core'; +import {FormControl, Validators} from '@angular/forms'; +import {AlertService} from 'src/app/common/services/alert.service'; +import {TaskDefinition} from 'src/app/api/models/task-definition'; +import {Unit} from 'src/app/api/models/unit'; +import {TaskDefinitionService} from 'src/app/api/services/task-definition.service'; +import {TutorialStream} from 'src/app/api/models/doubtfire-model'; + +@Component({ + selector: 'f-task-definition-tutorial-enrolment', + templateUrl: 'task-definition-tutorial-enrolment.component.html', + styleUrls: ['task-definition-tutorial-enrolment.component.scss'], +}) +export class TaskDefinitionTutorialEnrolmentComponent implements OnInit { + @Input() taskDefinition: TaskDefinition; + + public _tutorialStreams: TutorialStream[] = []; + + public _selectedTutorialStreams: TutorialStream[] = []; + + tutoralStreamControl = new FormControl(null, Validators.required); + + public ngOnInit(): void { + const tutorialStreams = this.taskDefinition.unit.tutorialStreamsCache.currentValues; + console.log(tutorialStreams); + for (const stream of tutorialStreams) { + console.log(stream); + this._tutorialStreams.push(stream); + } + } + + constructor( + private alerts: AlertService, + private taskDefinitionService: TaskDefinitionService, + ) {} + + public get unit(): Unit { + return this.taskDefinition?.unit; + } +} From acd18c83c563600cf1dbb29dea9d4ee8dc9d58e0 Mon Sep 17 00:00:00 2001 From: chelaz1234 Date: Sat, 13 Sep 2025 17:47:47 +1000 Subject: [PATCH 2/5] fix: remove console log --- .../task-definition-tutorial-enrolment.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.ts b/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.ts index 4cd5e61ab7..4e30ad62f5 100644 --- a/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.ts +++ b/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.ts @@ -24,7 +24,7 @@ export class TaskDefinitionTutorialEnrolmentComponent implements OnInit { const tutorialStreams = this.taskDefinition.unit.tutorialStreamsCache.currentValues; console.log(tutorialStreams); for (const stream of tutorialStreams) { - console.log(stream); + this._tutorialStreams.push(stream); } } From 049a67892bf19199b787eaf1246b08112116712f Mon Sep 17 00:00:00 2001 From: chelaz1234 Date: Sat, 13 Sep 2025 18:05:54 +1000 Subject: [PATCH 3/5] feat:create enrolment prompt card --- src/app/doubtfire-angular.module.ts | 2 + src/app/doubtfire-angularjs.module.ts | 6 +++ .../task-dashboard/task-dashboard.tpl.html | 1 + ...ask-tutorial-enrolment-card.component.html | 23 ++++++++++++ ...ask-tutorial-enrolment-card.component.scss | 0 .../task-tutorial-enrolment-card.component.ts | 37 +++++++++++++++++++ ...definition-tutorial-enrolment.component.ts | 1 - 7 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 src/app/projects/states/dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.html create mode 100644 src/app/projects/states/dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.scss create mode 100644 src/app/projects/states/dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.ts diff --git a/src/app/doubtfire-angular.module.ts b/src/app/doubtfire-angular.module.ts index 5ce88ed9cf..3a8cdf85d0 100644 --- a/src/app/doubtfire-angular.module.ts +++ b/src/app/doubtfire-angular.module.ts @@ -254,6 +254,7 @@ import { GradeIconComponent } from './common/grade-icon/grade-icon.component'; import { GradeTaskModalComponent } from './tasks/modals/grade-task-modal/grade-task-modal.component'; import { PrivacyPolicy } from './config/privacy-policy/privacy-policy'; import { TaskDefinitionTutorialEnrolmentComponent } from './units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component'; +import {TaskTutorialEnrolmentCardComponent} from './projects/states/dashboard/directives/task-dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component'; // See https://stackoverflow.com/questions/55721254/how-to-change-mat-datepicker-date-format-to-dd-mm-yyyy-in-simplest-way/58189036#58189036 const MY_DATE_FORMAT = { @@ -391,6 +392,7 @@ import { UnitStudentEnrolmentModalComponent } from './units/modals/unit-student- ScormExtensionCommentComponent, ScormExtensionModalComponent, TaskDefinitionTutorialEnrolmentComponent, + TaskTutorialEnrolmentCardComponent, ], // Services we provide providers: [ diff --git a/src/app/doubtfire-angularjs.module.ts b/src/app/doubtfire-angularjs.module.ts index d9dc6955d5..db14f2abc2 100644 --- a/src/app/doubtfire-angularjs.module.ts +++ b/src/app/doubtfire-angularjs.module.ts @@ -224,6 +224,7 @@ import {GradeService} from './common/services/grade.service'; import {TaskScormCardComponent} from './projects/states/dashboard/directives/task-dashboard/directives/task-scorm-card/task-scorm-card.component'; import { UnitStudentEnrolmentModalService } from './units/modals/unit-student-enrolment-modal/unit-student-enrolment-modal.service'; import { PrivacyPolicy } from './config/privacy-policy/privacy-policy'; +import {TaskTutorialEnrolmentCardComponent} from './projects/states/dashboard/directives/task-dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component'; export const DoubtfireAngularJSModule = angular.module('doubtfire', [ 'doubtfire.config', @@ -495,3 +496,8 @@ DoubtfireAngularJSModule.directive( 'fTaskVisualisation', downgradeComponent({ component: TaskVisualisationComponent }) ); + +DoubtfireAngularJSModule.directive( + 'fTaskTutorialEnrolmentCard', + downgradeComponent({component: TaskTutorialEnrolmentCardComponent}), +); diff --git a/src/app/projects/states/dashboard/directives/task-dashboard/task-dashboard.tpl.html b/src/app/projects/states/dashboard/directives/task-dashboard/task-dashboard.tpl.html index d2797fa663..7a4b783d16 100644 --- a/src/app/projects/states/dashboard/directives/task-dashboard/task-dashboard.tpl.html +++ b/src/app/projects/states/dashboard/directives/task-dashboard/task-dashboard.tpl.html @@ -42,6 +42,7 @@ + diff --git a/src/app/projects/states/dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.html b/src/app/projects/states/dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.html new file mode 100644 index 0000000000..22db7ba422 --- /dev/null +++ b/src/app/projects/states/dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.html @@ -0,0 +1,23 @@ +@if (!isSubmitted) { + + + Tutorial Enrolment + + +

+ This task allows you to select a tutorial to enrol in for the semester. Depending on which + tutorial you pick from, you will be assigned to the corresponding tutor that will mark your + work throughout the teaching period. +

+

To complete this task, you must enrol into a tutorial from the provided list.

+
+ + + +
+} \ No newline at end of file diff --git a/src/app/projects/states/dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.scss b/src/app/projects/states/dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/projects/states/dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.ts b/src/app/projects/states/dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.ts new file mode 100644 index 0000000000..6434337da0 --- /dev/null +++ b/src/app/projects/states/dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.ts @@ -0,0 +1,37 @@ +import {Component, Input, OnChanges, SimpleChanges} from '@angular/core'; +import {Task, User, UserService} from 'src/app/api/models/doubtfire-model'; + +@Component({ + selector: 'f-task-tutorial-enrolment-card', + templateUrl: './task-tutorial-enrolment-card.component.html', + styleUrls: ['./task-tutorial-enrolment-card.component.scss'], +}) +export class TaskTutorialEnrolmentCardComponent implements OnChanges { + @Input() task: Task; + user: User; + isSubmitted: boolean; // task submitted and student enrolled in tutorial + tutorialEnrolmentEnabled: boolean; + + constructor(private userService: UserService) { + this.user = this.userService.currentUser; + this.tutorialEnrolmentEnabled = true; // debug + } + + ngOnChanges(changes: SimpleChanges) { + if ( + changes.task && + changes.task.currentValue && + changes.task.currentValue.definition.tutorialSelfEnrolmentEnabled + ) { + console.log('self enrolment feature is enabled for this task'); // debug + } + } + + isNotStudent(): boolean { + return this.user !== this.task.project.student; + } + + launchTutorialEnrolment(): void { + console.log('launching tutorial enrolment...'); // debug + } +} \ No newline at end of file diff --git a/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.ts b/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.ts index 4e30ad62f5..051002089f 100644 --- a/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.ts +++ b/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.ts @@ -24,7 +24,6 @@ export class TaskDefinitionTutorialEnrolmentComponent implements OnInit { const tutorialStreams = this.taskDefinition.unit.tutorialStreamsCache.currentValues; console.log(tutorialStreams); for (const stream of tutorialStreams) { - this._tutorialStreams.push(stream); } } From 5b5cbbf6f622e4cfedd82418b68b103d54aee928 Mon Sep 17 00:00:00 2001 From: chelaz1234 Date: Sat, 13 Sep 2025 18:35:48 +1000 Subject: [PATCH 4/5] fix:issue fixes --- ...ask-tutorial-enrolment-card.component.html | 34 +++++++++++++++ ...ask-tutorial-enrolment-card.component.scss | 0 .../task-tutorial-enrolment-card.component.ts | 42 +++++++++++++++++++ ...ask-tutorial-enrolment-card.component.html | 23 ---------- .../task-tutorial-enrolment-card.component.ts | 37 ---------------- 5 files changed, 76 insertions(+), 60 deletions(-) create mode 100644 src/app/projects/states/dashboard/directives/task-dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.html rename src/app/projects/states/dashboard/directives/{ => task-dashboard/directives}/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.scss (100%) create mode 100644 src/app/projects/states/dashboard/directives/task-dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.ts delete mode 100644 src/app/projects/states/dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.html delete mode 100644 src/app/projects/states/dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.ts diff --git a/src/app/projects/states/dashboard/directives/task-dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.html b/src/app/projects/states/dashboard/directives/task-dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.html new file mode 100644 index 0000000000..d1a30bebd7 --- /dev/null +++ b/src/app/projects/states/dashboard/directives/task-dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.html @@ -0,0 +1,34 @@ + + + Tutorial Enrolment + + +

+ This task lets you choose a tutorial to enrol in for the teaching period. Your selected + tutorial will determine which tutor is assigned to mark your work throughout the teaching + period. +

+ @if (!isStudentEnrolledInTutorialStream()) { +

To complete this task, you must enrol in a tutorial from the provided list.

+ } + @if (isStudentEnrolledInTutorialStream()) { +

You're enrolled! You can now proceed to the next task.

+

+ You can confirm your enrolment in the + Tutorials List. +

+ } +
+ @if (!isStudentEnrolledInTutorialStream()) { + + + + } +
\ No newline at end of file diff --git a/src/app/projects/states/dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.scss b/src/app/projects/states/dashboard/directives/task-dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.scss similarity index 100% rename from src/app/projects/states/dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.scss rename to src/app/projects/states/dashboard/directives/task-dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.scss diff --git a/src/app/projects/states/dashboard/directives/task-dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.ts b/src/app/projects/states/dashboard/directives/task-dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.ts new file mode 100644 index 0000000000..89573b5d1f --- /dev/null +++ b/src/app/projects/states/dashboard/directives/task-dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.ts @@ -0,0 +1,42 @@ +import {Component, Input} from '@angular/core'; +import {Task, User, UserService} from 'src/app/api/models/doubtfire-model'; +import {TutorialEnrolmentModalService} from 'src/app/common/modals/tutorial-enrolment-modal/tutorial-enrolment-modal.service'; + +@Component({ + selector: 'f-task-tutorial-enrolment-card', + templateUrl: './task-tutorial-enrolment-card.component.html', + styleUrls: ['./task-tutorial-enrolment-card.component.scss'], +}) +export class TaskTutorialEnrolmentCardComponent { + @Input() task: Task; + user: User; + + constructor( + private userService: UserService, + private tutorialEnrolmentModalService: TutorialEnrolmentModalService, + ) { + this.user = this.userService.currentUser; + } + + isStudentEnrolledInTutorialStream(): boolean { + const tutorialStreamAbbreviation = + this.task.definition.tutorialSelfEnrolmentStream.abbreviation; + let isEnrolledInTutorialStream = false; + + const studentTutorialEnrolments = this.task.project.tutorials; + for (const tutorial of studentTutorialEnrolments) { + if (tutorial.tutorialStream.abbreviation === tutorialStreamAbbreviation) { + isEnrolledInTutorialStream = true; + } + } + return isEnrolledInTutorialStream; + } + + isNotStudent(): boolean { + return this.user !== this.task.project.student; + } + + launchTutorialEnrolment(): void { + this.tutorialEnrolmentModalService.show({task: this.task}); + } +} \ No newline at end of file diff --git a/src/app/projects/states/dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.html b/src/app/projects/states/dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.html deleted file mode 100644 index 22db7ba422..0000000000 --- a/src/app/projects/states/dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.html +++ /dev/null @@ -1,23 +0,0 @@ -@if (!isSubmitted) { - - - Tutorial Enrolment - - -

- This task allows you to select a tutorial to enrol in for the semester. Depending on which - tutorial you pick from, you will be assigned to the corresponding tutor that will mark your - work throughout the teaching period. -

-

To complete this task, you must enrol into a tutorial from the provided list.

-
- - - -
-} \ No newline at end of file diff --git a/src/app/projects/states/dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.ts b/src/app/projects/states/dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.ts deleted file mode 100644 index 6434337da0..0000000000 --- a/src/app/projects/states/dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.ts +++ /dev/null @@ -1,37 +0,0 @@ -import {Component, Input, OnChanges, SimpleChanges} from '@angular/core'; -import {Task, User, UserService} from 'src/app/api/models/doubtfire-model'; - -@Component({ - selector: 'f-task-tutorial-enrolment-card', - templateUrl: './task-tutorial-enrolment-card.component.html', - styleUrls: ['./task-tutorial-enrolment-card.component.scss'], -}) -export class TaskTutorialEnrolmentCardComponent implements OnChanges { - @Input() task: Task; - user: User; - isSubmitted: boolean; // task submitted and student enrolled in tutorial - tutorialEnrolmentEnabled: boolean; - - constructor(private userService: UserService) { - this.user = this.userService.currentUser; - this.tutorialEnrolmentEnabled = true; // debug - } - - ngOnChanges(changes: SimpleChanges) { - if ( - changes.task && - changes.task.currentValue && - changes.task.currentValue.definition.tutorialSelfEnrolmentEnabled - ) { - console.log('self enrolment feature is enabled for this task'); // debug - } - } - - isNotStudent(): boolean { - return this.user !== this.task.project.student; - } - - launchTutorialEnrolment(): void { - console.log('launching tutorial enrolment...'); // debug - } -} \ No newline at end of file From 620965cde530d700c191a7bf0c06727db7005149 Mon Sep 17 00:00:00 2001 From: disururathnayake Date: Sat, 13 Sep 2025 19:21:42 +1000 Subject: [PATCH 5/5] feat: create tutorial enrolment modal --- src/app/api/models/task-definition.ts | 40 +++++++++----- .../api/services/task-definition.service.ts | 28 +++++++--- .../tutorial-enrolment-modal.component.html | 30 ++++++++++ .../tutorial-enrolment-modal.component.ts | 55 +++++++++++++++++++ .../tutorial-enrolment-modal.service.ts | 24 ++++++++ src/app/doubtfire-angular.module.ts | 2 + .../task-tutorial-enrolment-card.component.ts | 2 +- ...finition-tutorial-enrolment.component.html | 4 +- ...definition-tutorial-enrolment.component.ts | 15 ++++- 9 files changed, 173 insertions(+), 27 deletions(-) create mode 100644 src/app/common/modals/tutorial-enrolment-modal/tutorial-enrolment-modal.component.html create mode 100644 src/app/common/modals/tutorial-enrolment-modal/tutorial-enrolment-modal.component.ts create mode 100644 src/app/common/modals/tutorial-enrolment-modal/tutorial-enrolment-modal.service.ts diff --git a/src/app/api/models/task-definition.ts b/src/app/api/models/task-definition.ts index f242f6837e..e4f7b79cff 100644 --- a/src/app/api/models/task-definition.ts +++ b/src/app/api/models/task-definition.ts @@ -1,14 +1,20 @@ -import { HttpClient } from '@angular/common/http'; -import { Entity, EntityMapping } from 'ngx-entity-service'; -import { Observable, tap } from 'rxjs'; -import { AppInjector } from 'src/app/app-injector'; -import { DoubtfireConstants } from 'src/app/config/constants/doubtfire-constants'; -import { Grade, GroupSet, TutorialStream, Unit } from './doubtfire-model'; -import { TaskDefinitionService } from '../services/task-definition.service'; - -export type UploadRequirement = { key: string; name: string; type: string; tiiCheck?: boolean; tiiPct?: number }; +import {HttpClient} from '@angular/common/http'; +import {Entity, EntityMapping} from 'ngx-entity-service'; +import {Observable, tap} from 'rxjs'; +import {AppInjector} from 'src/app/app-injector'; +import {DoubtfireConstants} from 'src/app/config/constants/doubtfire-constants'; +import {Grade, GroupSet, TutorialStream, Unit} from './doubtfire-model'; +import {TaskDefinitionService} from '../services/task-definition.service'; + +export type UploadRequirement = { + key: string; + name: string; + type: string; + tiiCheck?: boolean; + tiiPct?: number; +}; -export type SimilarityCheck = { key: string; type: string; pattern: string }; +export type SimilarityCheck = {key: string; type: string; pattern: string}; export class TaskDefinition extends Entity { id: number; @@ -38,6 +44,7 @@ export class TaskDefinition extends Entity { scormTimeDelayEnabled: boolean; scormAttemptLimit: number = 0; tutorialSelfEnrolmentEnabled: boolean; + tutorialSelfEnrolmentStream: TutorialStream = null; hasTaskAssessmentResources: boolean; isGraded: boolean; maxQualityPts: number; @@ -74,7 +81,7 @@ export class TaskDefinition extends Entity { entity: this, cache: this.unit.taskDefinitionCache, constructorParams: this.unit, - } + }, ); } else { return svc.update( @@ -82,7 +89,7 @@ export class TaskDefinition extends Entity { unitId: this.unit.id, id: this.id, }, - { entity: this } + {entity: this}, ); } } @@ -129,7 +136,10 @@ export class TaskDefinition extends Entity { } public matches(text: string): boolean { - return this.abbreviation.toLowerCase().indexOf(text) !== -1 || this.name.toLowerCase().indexOf(text) !== -1; + return ( + this.abbreviation.toLowerCase().indexOf(text) !== -1 || + this.name.toLowerCase().indexOf(text) !== -1 + ); } /** @@ -222,7 +232,9 @@ export class TaskDefinition extends Entity { public deleteTaskResources(): Observable { const httpClient = AppInjector.get(HttpClient); - return httpClient.delete(this.taskResourcesUploadUrl).pipe(tap(() => (this.hasTaskResources = false))); + return httpClient + .delete(this.taskResourcesUploadUrl) + .pipe(tap(() => (this.hasTaskResources = false))); } public deleteScormData(): Observable { diff --git a/src/app/api/services/task-definition.service.ts b/src/app/api/services/task-definition.service.ts index 0914929fbe..f65ec82c30 100644 --- a/src/app/api/services/task-definition.service.ts +++ b/src/app/api/services/task-definition.service.ts @@ -1,11 +1,11 @@ -import { CachedEntityService } from 'ngx-entity-service'; -import { TaskDefinition, Unit } from 'src/app/api/models/doubtfire-model'; -import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; +import {CachedEntityService} from 'ngx-entity-service'; +import {TaskDefinition, Unit} from 'src/app/api/models/doubtfire-model'; +import {Injectable} from '@angular/core'; +import {HttpClient} from '@angular/common/http'; import API_URL from 'src/app/config/constants/apiURL'; -import { MappingFunctions } from './mapping-fn'; -import { AppInjector } from 'src/app/app-injector'; -import { Observable } from 'rxjs'; +import {MappingFunctions} from './mapping-fn'; +import {AppInjector} from 'src/app/app-injector'; +import {Observable} from 'rxjs'; @Injectable() export class TaskDefinitionService extends CachedEntityService { @@ -81,6 +81,15 @@ export class TaskDefinitionService extends CachedEntityService { return taskDef.tutorialStream?.abbreviation; }, }, + { + keys: ['tutorialSelfEnrolmentStream', 'tutorial_self_enrolment_stream_abbr'], + toEntityFn: (data: object, key: string, taskDef: TaskDefinition, params?: any) => { + return taskDef.unit.tutorialStreamsCache.get(data[key]); + }, + toJsonFn: (taskDef: TaskDefinition, key: string) => { + return taskDef.tutorialSelfEnrolmentStream?.abbreviation; + }, + }, 'plagiarismWarnPct', 'restrictStatusUpdates', { @@ -100,6 +109,7 @@ export class TaskDefinitionService extends CachedEntityService { 'hasTaskResources', 'hasTaskAssessmentResources', 'scormEnabled', + 'tutorialSelfEnrolmentEnabled', 'hasScormData', 'scormAllowReview', 'scormBypassTest', @@ -108,7 +118,7 @@ export class TaskDefinitionService extends CachedEntityService { 'isGraded', 'maxQualityPts', 'overseerImageId', - 'assessmentEnabled' + 'assessmentEnabled', ); this.mapping.mapAllKeysToJsonExcept( @@ -116,7 +126,7 @@ export class TaskDefinitionService extends CachedEntityService { 'hasTaskSheet', 'hasTaskResources', 'hasTaskAssessmentResources', - 'hasScormData' + 'hasScormData', ); } diff --git a/src/app/common/modals/tutorial-enrolment-modal/tutorial-enrolment-modal.component.html b/src/app/common/modals/tutorial-enrolment-modal/tutorial-enrolment-modal.component.html new file mode 100644 index 0000000000..500ddff2d2 --- /dev/null +++ b/src/app/common/modals/tutorial-enrolment-modal/tutorial-enrolment-modal.component.html @@ -0,0 +1,30 @@ +

Tutorial Enrolment

+
+

+ Please select from the dropdown below the tutorial you would like to enrol in. +

+ + + Tutorials + + @for (tutorial of getTutorialsForStream(); track tutorial) { + {{ tutorial.abbreviation }} + } + + +
+ +
+ + +
diff --git a/src/app/common/modals/tutorial-enrolment-modal/tutorial-enrolment-modal.component.ts b/src/app/common/modals/tutorial-enrolment-modal/tutorial-enrolment-modal.component.ts new file mode 100644 index 0000000000..d001aa2408 --- /dev/null +++ b/src/app/common/modals/tutorial-enrolment-modal/tutorial-enrolment-modal.component.ts @@ -0,0 +1,55 @@ +import {Component, Inject, LOCALE_ID} from '@angular/core'; +import {MatDialogRef, MAT_DIALOG_DATA} from '@angular/material/dialog'; +import { + TaskComment, + TaskCommentService, + Task, + Unit, + Tutorial, +} from 'src/app/api/models/doubtfire-model'; +import {AppInjector} from 'src/app/app-injector'; +import {FormControl, Validators, FormGroup, FormGroupDirective, NgForm} from '@angular/forms'; +import {ErrorStateMatcher} from '@angular/material/core'; +import {AlertService} from '../../services/alert.service'; + +export interface TutorialEnrolmentModalData { + task: Task; +} + +@Component({ + selector: 'f-tutorial-enrolment-modal', + templateUrl: './tutorial-enrolment-modal.component.html', +}) +export class TutorialEnrolmentModalComponent { + tutorialsFormControl = new FormControl(null); + + constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: TutorialEnrolmentModalData, + private alerts: AlertService, + ) {} + + public getTutorialsForStream() { + const tutorialStreamAbbreviation = + this.data.task.definition.tutorialSelfEnrolmentStream.abbreviation; + + const student = this.data.task.project; + return this.data.task.unit.tutorials.filter((tutorial) => { + // Filter workshops based on campus allocation + const result: boolean = + student.campus == null || + tutorial.campus == null || + student.campus.id === tutorial.campus.id; + + if (!result) return result; + if (tutorial.tutorialStream) { + return tutorial.tutorialStream.abbreviation === tutorialStreamAbbreviation; + } + }); + } + + attemptTutorialEnrolment() { + const selectedTutorial = this.tutorialsFormControl.value; + this.data.task.project.switchToTutorial(selectedTutorial); + } +} diff --git a/src/app/common/modals/tutorial-enrolment-modal/tutorial-enrolment-modal.service.ts b/src/app/common/modals/tutorial-enrolment-modal/tutorial-enrolment-modal.service.ts new file mode 100644 index 0000000000..01b72f0b31 --- /dev/null +++ b/src/app/common/modals/tutorial-enrolment-modal/tutorial-enrolment-modal.service.ts @@ -0,0 +1,24 @@ +import {Injectable} from '@angular/core'; +import {Task} from 'src/app/api/models/task'; +import {MatDialogRef, MatDialog} from '@angular/material/dialog'; +import { + TutorialEnrolmentModalComponent, + TutorialEnrolmentModalData, +} from './tutorial-enrolment-modal.component'; + +@Injectable({ + providedIn: 'root', +}) +export class TutorialEnrolmentModalService { + constructor(public dialog: MatDialog) {} + + public show(data: TutorialEnrolmentModalData) { + let dialogRef: MatDialogRef; + + dialogRef = this.dialog.open(TutorialEnrolmentModalComponent, {data}); + + dialogRef.afterOpened().subscribe((result: any) => {}); + + dialogRef.afterClosed().subscribe((result: any) => {}); + } +} diff --git a/src/app/doubtfire-angular.module.ts b/src/app/doubtfire-angular.module.ts index 3a8cdf85d0..b186c06370 100644 --- a/src/app/doubtfire-angular.module.ts +++ b/src/app/doubtfire-angular.module.ts @@ -255,6 +255,7 @@ import { GradeTaskModalComponent } from './tasks/modals/grade-task-modal/grade-t import { PrivacyPolicy } from './config/privacy-policy/privacy-policy'; import { TaskDefinitionTutorialEnrolmentComponent } from './units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component'; import {TaskTutorialEnrolmentCardComponent} from './projects/states/dashboard/directives/task-dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component'; +import { TutorialEnrolmentModalComponent } from './common/modals/tutorial-enrolment-modal/tutorial-enrolment-modal.component'; // See https://stackoverflow.com/questions/55721254/how-to-change-mat-datepicker-date-format-to-dd-mm-yyyy-in-simplest-way/58189036#58189036 const MY_DATE_FORMAT = { @@ -393,6 +394,7 @@ import { UnitStudentEnrolmentModalComponent } from './units/modals/unit-student- ScormExtensionModalComponent, TaskDefinitionTutorialEnrolmentComponent, TaskTutorialEnrolmentCardComponent, + TutorialEnrolmentModalComponent, ], // Services we provide providers: [ diff --git a/src/app/projects/states/dashboard/directives/task-dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.ts b/src/app/projects/states/dashboard/directives/task-dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.ts index 89573b5d1f..28fc291938 100644 --- a/src/app/projects/states/dashboard/directives/task-dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.ts +++ b/src/app/projects/states/dashboard/directives/task-dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component.ts @@ -39,4 +39,4 @@ export class TaskTutorialEnrolmentCardComponent { launchTutorialEnrolment(): void { this.tutorialEnrolmentModalService.show({task: this.task}); } -} \ No newline at end of file +} diff --git a/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.html b/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.html index 352cf4021c..b1180917dd 100644 --- a/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.html +++ b/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.html @@ -9,9 +9,9 @@ Tutorial Streams - Which stream students can enrol into a tutorial from? - + @for (stream of unit.tutorialStreams; track stream) { - {{ stream.name }} — ({{ stream.tutorialsIn(taskDefinition.unit).length }} diff --git a/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.ts b/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.ts index 051002089f..2146e114f0 100644 --- a/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.ts +++ b/src/app/units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component.ts @@ -18,14 +18,27 @@ export class TaskDefinitionTutorialEnrolmentComponent implements OnInit { public _selectedTutorialStreams: TutorialStream[] = []; - tutoralStreamControl = new FormControl(null, Validators.required); + tutoralStreamControl = new FormControl(null, Validators.required); public ngOnInit(): void { const tutorialStreams = this.taskDefinition.unit.tutorialStreamsCache.currentValues; console.log(tutorialStreams); for (const stream of tutorialStreams) { + console.log(stream); this._tutorialStreams.push(stream); } + + + this.tutoralStreamControl.setValue(this.taskDefinition.tutorialSelfEnrolmentStream); + } + + public onSelectStream(): void { + const selectedStream = this.tutoralStreamControl.value; + if (selectedStream) { + console.log(selectedStream); + + this.taskDefinition.tutorialSelfEnrolmentStream = selectedStream; + } } constructor(