diff --git a/.prettierrc.json b/.prettierrc.json index 08d6d29d..c7db42fc 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,4 +1,7 @@ { "tabWidth": 4, - "printWidth": 120 + "printWidth": 120, + "singleQuote": true, + "semi": true, + "trailingComma": "es5" } diff --git a/.vscode/settings.json b/.vscode/settings.json index affc663d..ca621d30 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,9 @@ { "workbench.tree.indent": 18, "editor.tabSize": 4, + "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", - "editor.formatOnSave": true + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + } } diff --git a/src/app/common/commonMethods.ts b/src/app/common/commonMethods.ts index ed49cbde..60ea891f 100644 --- a/src/app/common/commonMethods.ts +++ b/src/app/common/commonMethods.ts @@ -2,7 +2,7 @@ // random numbers generated are lowerbound inclusive and upperbound exclusive: [lowerbound, upperbound) export function generateRandomNonrepeatingNumberList(size: number, lowerBound: number, upperBound: number): number[] { - if (size > upperBound - lowerBound) throw new Error("Size cannot be greater than the bounds"); + if (size > upperBound - lowerBound) throw new Error('Size cannot be greater than the bounds'); const randList = []; while (randList.length < size) { const randNum = getRandomNumber(lowerBound, upperBound); @@ -79,30 +79,6 @@ export function wait(time: number): Promise { }); } -export function isSurveyMonkeyQuestionnaire(task: string): boolean { - return false; - // return task.includes(RouteMap.surveymonkeyquestionnaire.id); -} - -export function isCustomTask(task: string): boolean { - return false; - // return task.includes(RouteMap.pavloviatask.id); -} - -export const ConsentIds = [ - // RouteMap.consent.id, - // RouteMap.webPhenoClinical.id, - // RouteMap.webPhenoClinicalFR.id, - // RouteMap.stressClinical.id, - // RouteMap.stressClinicalFR.id, - // RouteMap.stressClinicalDebrief.id, - // RouteMap.stressPilot.id, -]; -// remove later, this is a bandaid fix -export function isConsent(id: string): boolean { - return ConsentIds.includes(id); -} - export function thisOrDefault(value: T, defaultValue: T): T { return value === null || value === undefined ? defaultValue : value; } @@ -114,3 +90,7 @@ export function throwErrIfNotDefined(value: any, errStr: string): any { return value; } } + +export function objIsEmpty(arg: any): boolean { + return Object.keys(arg).length === 0; +} diff --git a/src/app/models/TaskData.ts b/src/app/models/TaskData.ts index 96e55087..14a0e9c2 100644 --- a/src/app/models/TaskData.ts +++ b/src/app/models/TaskData.ts @@ -1,20 +1,20 @@ -import { UseHand } from "../pages/tasks/task-playables/finger-tapping/finger-tapping-task.component"; -import { RatingTaskCounterBalance } from "../pages/tasks/task-playables/everyday-choice/rater/rater.component"; -import { SmileyFaceTaskCounterbalance } from "../pages/tasks/task-playables/smiley-face/smiley-face.component"; -import { Key, UserResponse } from "./InternalDTOs"; -import { ParticipantType } from "./enums"; +import { UseHand } from '../pages/tasks/task-playables/finger-tapping/finger-tapping-task.component'; +import { RatingTaskCounterBalance } from '../pages/tasks/task-playables/everyday-choice/rater/rater.component'; +import { SmileyFaceTaskCounterbalance } from '../pages/tasks/task-playables/smiley-face/smiley-face.component'; +import { Key, UserResponse } from './InternalDTOs'; +import { ParticipantType } from './enums'; export enum TaskNames { - ODDBALL = "oddball", - DIGITSPAN = "digitspan", - FINGERTAPPING = "fingertapping", - NBACK = "nback", - STROOP = "stroop", - TRAILMAKING = "trailmaking", - TASKSWITCHING = "taskswitching", - DEMANDSELECTION = "demandselection", - SMILEYFACE = "smileyface", - EVERYDAYCHOICE = "everydaychoice", + ODDBALL = 'oddball', + DIGITSPAN = 'digitspan', + FINGERTAPPING = 'fingertapping', + NBACK = 'nback', + STROOP = 'stroop', + TRAILMAKING = 'trailmaking', + TASKSWITCHING = 'taskswitching', + DEMANDSELECTION = 'demandselection', + SMILEYFACE = 'smileyface', + EVERYDAYCHOICE = 'everydaychoice', } export abstract class TaskData { @@ -132,17 +132,10 @@ export class EverydayChoiceTaskData extends TaskData { activity: string; question: string; userAnswer: number; - activityType: "DoNothing" | "DoSomething" | ""; + activityType: 'DoNothing' | 'DoSomething' | ''; responseTime: number; } -export class CustomTask { - customTaskID: string; - name: string; - url: string; - description: string; -} - export class ParticipantData { userId: string; studyId: number; diff --git a/src/app/models/enums.ts b/src/app/models/enums.ts index 208f7594..33fe4991 100644 --- a/src/app/models/enums.ts +++ b/src/app/models/enums.ts @@ -1,69 +1,72 @@ export enum RouteNames { - LANDINGPAGE_LOGIN_SUBROUTE = "login", - LANDINGPAGE_REGISTER_SUBROUTE = "register", - LANDINGPAGE_FORGOT_PASSWORD = "send-reset-email", - LANDINGPAGE_RESET_PASSWORD = "reset", + LANDINGPAGE_LOGIN_SUBROUTE = 'login', + LANDINGPAGE_REGISTER_SUBROUTE = 'register', + LANDINGPAGE_FORGOT_PASSWORD = 'send-reset-email', + LANDINGPAGE_RESET_PASSWORD = 'reset', - TASKPLAYER = "playtask", + TASKPLAYER = 'playtask', - QUESTIONNAIRE = "questionnaire", + QUESTIONNAIRE = 'questionnaire', - PAVLOVIA = "pavlovia", + PAVLOVIA = 'pavlovia', - CONSENT = "consent", + CONSENT = 'consent', + + INFO_DISPLAY = 'infodisplay', } export enum AdminRouteNames { - DASHBOARD_BASEROUTE = "admin-dashboard", + DASHBOARD_BASEROUTE = 'admin-dashboard', - COMPONENTS_SUBROUTE = "components", - DATA_SUBROUTE = "data", - GUESTS_SUBROUTE = "guests", - STUDIES_SUBROUTE = "studies", - STUDIES_CREATE_SUBROUTE = "create", - STUDIES_VIEW_SUBROUTE = "view", + COMPONENTS_SUBROUTE = 'components', + DATA_SUBROUTE = 'data', + GUESTS_SUBROUTE = 'guests', + STUDIES_SUBROUTE = 'studies', + STUDIES_CREATE_SUBROUTE = 'create', + STUDIES_VIEW_SUBROUTE = 'view', } export enum ParticipantRouteNames { - DASHBOARD_BASEROUTE = "participant-dashboard", - CROWDSOURCEPARTICIPANT_REGISTER_BASEROUTE = "crowdsource-participant", + DASHBOARD_BASEROUTE = 'participant-dashboard', + CROWDSOURCEPARTICIPANT_REGISTER_BASEROUTE = 'crowdsource-participant', - STUDIES_SUBROUTE = "studies", - PROFILE_SUBROUTE = "profile", + STUDIES_SUBROUTE = 'studies', + PROFILE_SUBROUTE = 'profile', } // Each task is one of the following type export enum TaskType { - NAB = "NAB", - EXPERIMENTAL = "EXPERIMENTAL", - QUESTIONNAIRE = "QUESTIONNAIRE", - CONSENT = "CONSENT", + NAB = 'NAB', + EXPERIMENTAL = 'EXPERIMENTAL', + QUESTIONNAIRE = 'QUESTIONNAIRE', + CONSENT = 'CONSENT', + INFO_DISPLAY = 'INFO_DISPLAY', } export enum Platform { - PSHARPLAB = "PSHARPLAB", - SURVEYMONKEY = "SURVEYMONEKY", - PAVLOVIA = "PAVLOVIA", + PSHARPLAB = 'PSHARPLAB', + SURVEYMONKEY = 'SURVEYMONEKY', + PAVLOVIA = 'PAVLOVIA', } export enum SnackbarType { - SUCCESS = "SUCCESS", - ERROR = "ERROR", - INFO = "INFO", + SUCCESS = 'SUCCESS', + ERROR = 'ERROR', + INFO = 'INFO', } export enum Role { - ADMIN = "ADMIN", - PARTICIPANT = "PARTICIPANT", - GUEST = "GUEST", // access to admin views but cannot make any calls to backend + ADMIN = 'ADMIN', + PARTICIPANT = 'PARTICIPANT', + GUEST = 'GUEST', // access to admin views but cannot make any calls to backend } export enum StimuliProvidedType { - HARDCODED = "hardcoded", - GENERATED = "generated", + HARDCODED = 'hardcoded', + GENERATED = 'generated', } export enum ParticipantType { - CROWDSOURCED = "CROWDSOURCED", - ACCOUNTHOLDER = "ACCOUNTHOLDER", + CROWDSOURCED = 'CROWDSOURCED', + ACCOUNTHOLDER = 'ACCOUNTHOLDER', } diff --git a/src/app/pages/admin/admin-dashboard/studies/create-modify-study/create-modify-study.component.scss b/src/app/pages/admin/admin-dashboard/studies/create-modify-study/create-modify-study.component.scss index 5064c4df..13390f9a 100644 --- a/src/app/pages/admin/admin-dashboard/studies/create-modify-study/create-modify-study.component.scss +++ b/src/app/pages/admin/admin-dashboard/studies/create-modify-study/create-modify-study.component.scss @@ -93,10 +93,6 @@ background-color: #b157a6; } -.CustomTask { - background-color: #3e1e52; -} - .badge { padding: 5px 8px; border-radius: 10px; diff --git a/src/app/pages/admin/admin-dashboard/study-components/manage-questionnaires/manage-questionnaires.component.html b/src/app/pages/admin/admin-dashboard/study-components/manage-questionnaires/manage-questionnaires.component.html deleted file mode 100644 index c6ad85a2..00000000 --- a/src/app/pages/admin/admin-dashboard/study-components/manage-questionnaires/manage-questionnaires.component.html +++ /dev/null @@ -1,43 +0,0 @@ -
-
-

Psharplab Questionnaires

-
- -
No questionnaires
- - - - - - - - - - - - - - - - - - - - - - - -
Name{{ questionnaire.name }}Description{{ questionnaire.description }}URL{{ questionnaire.url }}Actions -
- -
-
-
diff --git a/src/app/pages/admin/admin-dashboard/study-components/manage-questionnaires/manage-questionnaires.component.ts b/src/app/pages/admin/admin-dashboard/study-components/manage-questionnaires/manage-questionnaires.component.ts deleted file mode 100644 index a40c9784..00000000 --- a/src/app/pages/admin/admin-dashboard/study-components/manage-questionnaires/manage-questionnaires.component.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Component, OnInit } from "@angular/core"; -import { MatDialog } from "@angular/material/dialog"; -import { Router } from "@angular/router"; -import { Observable, Subscription } from "rxjs"; -import { map } from "rxjs/operators"; -import { RouteNames, TaskType } from "src/app/models/enums"; -import { Task } from "src/app/models/Task"; -import { QuestionnaireNavigationConfig } from "src/app/pages/tasks/questionnaire-reader/questionnaire-reader.component"; -import { TaskService } from "src/app/services/task.service"; - -@Component({ - selector: "app-manage-questionnaires", - templateUrl: "./manage-questionnaires.component.html", - styleUrls: ["./manage-questionnaires.component.scss"], -}) -export class ManageQuestionnairesComponent implements OnInit { - constructor(private dialog: MatDialog, private taskService: TaskService, private router: Router) {} - - subscriptions: Subscription[] = []; - displayedQuestionnaireColumns = ["name", "description", "actions"]; - - ngOnInit() { - this.tasks = this.taskService.tasks; - } - - tasks: Observable; - - get questionnaires(): Observable { - return this.tasks.pipe( - map((tasks) => (tasks ? tasks.filter((task) => task.taskType === TaskType.QUESTIONNAIRE) : [])) - ); - } - - previewQuestionnaire(questionnaire: Task) { - const questionnaireConfig: QuestionnaireNavigationConfig = { - metadata: questionnaire.config, - mode: "test", - }; - - this.router.navigate([`${RouteNames.QUESTIONNAIRE}`], { state: questionnaireConfig }); - } - - ngOnDestroy() { - this.subscriptions.forEach((sub) => sub.unsubscribe()); - } -} diff --git a/src/app/pages/admin/admin-dashboard/study-components/pavlovia-tasks/pavlovia-tasks.component.html b/src/app/pages/admin/admin-dashboard/study-components/pavlovia-tasks/pavlovia-tasks.component.html deleted file mode 100644 index 2256d504..00000000 --- a/src/app/pages/admin/admin-dashboard/study-components/pavlovia-tasks/pavlovia-tasks.component.html +++ /dev/null @@ -1,87 +0,0 @@ -
-
-

Pavlovia Tasks

-
- -
No pavlovia tasks
- -

Neuropsych Battery (NAB)

-
- - - - - - - - - - - - - - - - - - - - - - - - -
Name{{ NABTask.name }}Description{{ NABTask.description }}URL{{ task.externalURL }}Actions - -
- No NAB pavlovia tasks -
- -
-

Experimental Tasks

-
- - - - - - - - - - - - - - - - - - - - - - - -
Name{{ task.name }}Description{{ task.description }}URL{{ task.externalURL }}Actions - -
- No experimental pavlovia tasks -
diff --git a/src/app/pages/admin/admin-dashboard/study-components/pavlovia-tasks/pavlovia-tasks.component.ts b/src/app/pages/admin/admin-dashboard/study-components/pavlovia-tasks/pavlovia-tasks.component.ts deleted file mode 100644 index c56976a9..00000000 --- a/src/app/pages/admin/admin-dashboard/study-components/pavlovia-tasks/pavlovia-tasks.component.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Component, OnInit } from "@angular/core"; -import { Router } from "@angular/router"; -import { Observable } from "rxjs"; -import { map } from "rxjs/operators"; -import { Platform, RouteNames, TaskType } from "src/app/models/enums"; -import { Task } from "src/app/models/Task"; -import { CustomTask } from "src/app/models/TaskData"; -import { EmbeddedPageNavigationConfig } from "src/app/pages/tasks/embedded-page/embedded-page.component"; -import { TaskService } from "src/app/services/task.service"; - -@Component({ - selector: "app-pavlovia-tasks", - templateUrl: "./pavlovia-tasks.component.html", - styleUrls: ["./pavlovia-tasks.component.scss"], -}) -export class PavloviaTasksComponent implements OnInit { - displayedTaskColumns = ["name", "description", "url", "actions"]; - - customTasks: Observable; - - ngOnInit() { - this.pavloviaTasks = this.taskService.tasks; - } - - pavloviaTasks: Observable; - - get experimentalPavloviaTasks(): Observable { - return this.pavloviaTasks.pipe( - map((tasks) => - tasks - ? tasks.filter( - (task) => task.taskType === TaskType.EXPERIMENTAL && task.fromPlatform === Platform.PAVLOVIA - ) - : [] - ) - ); - } - - get NABPavloviaTasks(): Observable { - return this.pavloviaTasks.pipe( - map((tasks) => - tasks - ? tasks.filter((task) => task.taskType === TaskType.NAB && task.fromPlatform === Platform.PAVLOVIA) - : [] - ) - ); - } - - run(task: Task) { - const config: EmbeddedPageNavigationConfig = { - config: { - externalURL: task.externalURL, - task: task, - }, - mode: "test", - }; - this.router.navigate([`${RouteNames.PAVLOVIA}`], { state: config }); - } - - constructor(private taskService: TaskService, private router: Router) {} -} diff --git a/src/app/pages/admin/admin-dashboard/study-components/shared/view-components-table/view-components-table.component.html b/src/app/pages/admin/admin-dashboard/study-components/shared/view-components-table/view-components-table.component.html new file mode 100644 index 00000000..bdb4d683 --- /dev/null +++ b/src/app/pages/admin/admin-dashboard/study-components/shared/view-components-table/view-components-table.component.html @@ -0,0 +1,29 @@ + +

{{ tableTitle }}

+
+
+ +
{{ msgOnEmpty }}
+ + + + + + + + + + + + + + + +
{{ row.columnHeader }}{{ givenRow[row.columnKey] }}Actions +
+ +
+
diff --git a/src/app/pages/admin/admin-dashboard/study-components/shared/view-components-table/view-components-table.component.scss b/src/app/pages/admin/admin-dashboard/study-components/shared/view-components-table/view-components-table.component.scss new file mode 100644 index 00000000..60fd8b78 --- /dev/null +++ b/src/app/pages/admin/admin-dashboard/study-components/shared/view-components-table/view-components-table.component.scss @@ -0,0 +1,3 @@ +td { + white-space: normal; +} diff --git a/src/app/pages/admin/admin-dashboard/study-components/shared/view-components-table/view-components-table.component.spec.ts b/src/app/pages/admin/admin-dashboard/study-components/shared/view-components-table/view-components-table.component.spec.ts new file mode 100644 index 00000000..e943c062 --- /dev/null +++ b/src/app/pages/admin/admin-dashboard/study-components/shared/view-components-table/view-components-table.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ViewComponentsTableComponent } from './view-components-table.component'; + +describe('ViewComponentsTableComponent', () => { + let component: ViewComponentsTableComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ViewComponentsTableComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ViewComponentsTableComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/admin/admin-dashboard/study-components/shared/view-components-table/view-components-table.component.ts b/src/app/pages/admin/admin-dashboard/study-components/shared/view-components-table/view-components-table.component.ts new file mode 100644 index 00000000..015aa7fe --- /dev/null +++ b/src/app/pages/admin/admin-dashboard/study-components/shared/view-components-table/view-components-table.component.ts @@ -0,0 +1,53 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; + +export interface ViewComponentsTableModel { + tableConfig: { + columnHeader: string; + columnKey: string; + }[]; + tableData: T[]; + msgOnEmpty: string; + tableTitle: string; +} + +@Component({ + selector: 'app-view-components-table', + templateUrl: './view-components-table.component.html', + styleUrls: ['./view-components-table.component.scss'], +}) +export class ViewComponentsTableComponent implements OnInit { + @Input() + data: ViewComponentsTableModel; + + @Output() + rowRun: EventEmitter = new EventEmitter(); + + get rowData(): T[] { + return this.data?.tableData || []; + } + + get tableTitle(): string { + return this.data?.tableTitle; + } + + get displayedColumns(): string[] { + const columnHeaders = this.data.tableConfig.map((x) => x.columnHeader); + return [...columnHeaders, 'actions']; + } + + get tableconfig(): { columnHeader: string; columnKey: string }[] { + return this.data?.tableConfig || []; + } + + get msgOnEmpty(): string { + return this.data?.msgOnEmpty || 'No data'; + } + + onSelect(arg: T) { + this.rowRun.emit(arg); + } + + constructor() {} + + ngOnInit(): void {} +} diff --git a/src/app/pages/admin/admin-dashboard/study-components/study-components.component.html b/src/app/pages/admin/admin-dashboard/study-components/study-components.component.html index fcadc050..8c036650 100644 --- a/src/app/pages/admin/admin-dashboard/study-components/study-components.component.html +++ b/src/app/pages/admin/admin-dashboard/study-components/study-components.component.html @@ -1,11 +1,18 @@ - +
+ +
- +
+ +
- +
+ +
+
diff --git a/src/app/pages/admin/admin-dashboard/study-components/view-info-displays/view-info-displays.component.html b/src/app/pages/admin/admin-dashboard/study-components/view-info-displays/view-info-displays.component.html new file mode 100644 index 00000000..29c3acb7 --- /dev/null +++ b/src/app/pages/admin/admin-dashboard/study-components/view-info-displays/view-info-displays.component.html @@ -0,0 +1 @@ +

view-info-displays works!

diff --git a/src/app/pages/admin/admin-dashboard/study-components/view-info-displays/view-info-displays.component.scss b/src/app/pages/admin/admin-dashboard/study-components/view-info-displays/view-info-displays.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/pages/admin/admin-dashboard/study-components/view-info-displays/view-info-displays.component.spec.ts b/src/app/pages/admin/admin-dashboard/study-components/view-info-displays/view-info-displays.component.spec.ts new file mode 100644 index 00000000..5dab489b --- /dev/null +++ b/src/app/pages/admin/admin-dashboard/study-components/view-info-displays/view-info-displays.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ViewInfoDisplaysComponent } from './view-info-displays.component'; + +describe('ViewInfoDisplaysComponent', () => { + let component: ViewInfoDisplaysComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ViewInfoDisplaysComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ViewInfoDisplaysComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/admin/admin-dashboard/study-components/view-info-displays/view-info-displays.component.ts b/src/app/pages/admin/admin-dashboard/study-components/view-info-displays/view-info-displays.component.ts new file mode 100644 index 00000000..283a5c51 --- /dev/null +++ b/src/app/pages/admin/admin-dashboard/study-components/view-info-displays/view-info-displays.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-view-info-displays', + templateUrl: './view-info-displays.component.html', + styleUrls: ['./view-info-displays.component.scss'] +}) +export class ViewInfoDisplaysComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/src/app/pages/admin/admin-dashboard/study-components/view-pavlovia-tasks/pavlovia-tasks.component.html b/src/app/pages/admin/admin-dashboard/study-components/view-pavlovia-tasks/pavlovia-tasks.component.html new file mode 100644 index 00000000..2c22f925 --- /dev/null +++ b/src/app/pages/admin/admin-dashboard/study-components/view-pavlovia-tasks/pavlovia-tasks.component.html @@ -0,0 +1,12 @@ +
+ +
+
+ +
diff --git a/src/app/pages/admin/admin-dashboard/study-components/pavlovia-tasks/pavlovia-tasks.component.scss b/src/app/pages/admin/admin-dashboard/study-components/view-pavlovia-tasks/pavlovia-tasks.component.scss similarity index 100% rename from src/app/pages/admin/admin-dashboard/study-components/pavlovia-tasks/pavlovia-tasks.component.scss rename to src/app/pages/admin/admin-dashboard/study-components/view-pavlovia-tasks/pavlovia-tasks.component.scss diff --git a/src/app/pages/admin/admin-dashboard/study-components/pavlovia-tasks/pavlovia-tasks.component.spec.ts b/src/app/pages/admin/admin-dashboard/study-components/view-pavlovia-tasks/pavlovia-tasks.component.spec.ts similarity index 60% rename from src/app/pages/admin/admin-dashboard/study-components/pavlovia-tasks/pavlovia-tasks.component.spec.ts rename to src/app/pages/admin/admin-dashboard/study-components/view-pavlovia-tasks/pavlovia-tasks.component.spec.ts index 2ef2ed9f..a004d1b1 100644 --- a/src/app/pages/admin/admin-dashboard/study-components/pavlovia-tasks/pavlovia-tasks.component.spec.ts +++ b/src/app/pages/admin/admin-dashboard/study-components/view-pavlovia-tasks/pavlovia-tasks.component.spec.ts @@ -1,13 +1,13 @@ -import { OverlayModule } from "@angular/cdk/overlay"; -import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; -import { MatDialog } from "@angular/material/dialog"; -import { MatSnackBar } from "@angular/material/snack-bar"; -import { SnackbarService } from "src/app/services/snackbar.service"; +import { OverlayModule } from '@angular/cdk/overlay'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { MatDialog } from '@angular/material/dialog'; +import { MatSnackBar } from '@angular/material/snack-bar'; +import { SnackbarService } from 'src/app/services/snackbar.service'; -import { PavloviaTasksComponent } from "./pavlovia-tasks.component"; +import { PavloviaTasksComponent } from './pavlovia-tasks.component'; -describe("ManageCustomTasksComponent", () => { +describe('ManageCustomTasksComponent', () => { let component: PavloviaTasksComponent; let fixture: ComponentFixture; @@ -31,7 +31,7 @@ describe("ManageCustomTasksComponent", () => { fixture.detectChanges(); }); - it("should create", () => { + it('should create', () => { expect(component).toBeTruthy(); }); }); diff --git a/src/app/pages/admin/admin-dashboard/study-components/view-pavlovia-tasks/pavlovia-tasks.component.ts b/src/app/pages/admin/admin-dashboard/study-components/view-pavlovia-tasks/pavlovia-tasks.component.ts new file mode 100644 index 00000000..c2bceb48 --- /dev/null +++ b/src/app/pages/admin/admin-dashboard/study-components/view-pavlovia-tasks/pavlovia-tasks.component.ts @@ -0,0 +1,95 @@ +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { Platform, RouteNames, TaskType } from 'src/app/models/enums'; +import { Task } from 'src/app/models/Task'; +import { EmbeddedPageNavigationConfig } from 'src/app/pages/tasks/embedded-page/embedded-page.component'; +import { TaskService } from 'src/app/services/task.service'; +import { ViewComponentsTableModel } from '../shared/view-components-table/view-components-table.component'; + +@Component({ + selector: 'app-pavlovia-tasks', + templateUrl: './pavlovia-tasks.component.html', + styleUrls: ['./pavlovia-tasks.component.scss'], +}) +export class PavloviaTasksComponent implements OnInit { + ngOnInit() { + this.tasks = this.taskService.tasks; + } + + tasks: Observable; + + get NABPavloviaTasksForTable(): Observable> { + return this.tasks.pipe( + map((tasks) => + tasks + ? tasks.filter((task) => task.taskType === TaskType.NAB && task.fromPlatform === Platform.PAVLOVIA) + : [] + ), + map((tasks) => ({ + tableConfig: [ + { + columnHeader: 'Name', + columnKey: 'name', + }, + { + columnHeader: 'Description', + columnKey: 'description', + }, + { + columnHeader: 'URL', + columnKey: 'externalURL', + }, + ], + tableData: tasks, + msgOnEmpty: 'No pavlovia NAB Tasks', + tableTitle: 'Neuropsych Battery (NAB)', + })) + ); + } + + get experimentalPavloviaTasksForTable(): Observable> { + return this.tasks.pipe( + map((tasks) => + tasks + ? tasks.filter( + (task) => task.taskType === TaskType.EXPERIMENTAL && task.fromPlatform === Platform.PAVLOVIA + ) + : [] + ), + map((tasks) => ({ + tableConfig: [ + { + columnHeader: 'Name', + columnKey: 'name', + }, + { + columnHeader: 'Description', + columnKey: 'description', + }, + { + columnHeader: 'URL', + columnKey: 'externalURL', + }, + ], + tableData: tasks, + msgOnEmpty: 'No questionnaires', + tableTitle: 'Experimental Tasks', + })) + ); + } + + run(task: Task) { + const config: EmbeddedPageNavigationConfig = { + config: { + externalURL: task.externalURL, + task: task, + }, + mode: 'test', + }; + this.router.navigate([`${RouteNames.PAVLOVIA}`], { state: config }); + } + + constructor(private taskService: TaskService, private router: Router) {} +} diff --git a/src/app/pages/admin/admin-dashboard/study-components/view-questionnaires/manage-questionnaires.component.html b/src/app/pages/admin/admin-dashboard/study-components/view-questionnaires/manage-questionnaires.component.html new file mode 100644 index 00000000..c93bda8f --- /dev/null +++ b/src/app/pages/admin/admin-dashboard/study-components/view-questionnaires/manage-questionnaires.component.html @@ -0,0 +1,4 @@ + diff --git a/src/app/pages/admin/admin-dashboard/study-components/manage-questionnaires/manage-questionnaires.component.scss b/src/app/pages/admin/admin-dashboard/study-components/view-questionnaires/manage-questionnaires.component.scss similarity index 100% rename from src/app/pages/admin/admin-dashboard/study-components/manage-questionnaires/manage-questionnaires.component.scss rename to src/app/pages/admin/admin-dashboard/study-components/view-questionnaires/manage-questionnaires.component.scss diff --git a/src/app/pages/admin/admin-dashboard/study-components/view-questionnaires/manage-questionnaires.component.ts b/src/app/pages/admin/admin-dashboard/study-components/view-questionnaires/manage-questionnaires.component.ts new file mode 100644 index 00000000..09c3b990 --- /dev/null +++ b/src/app/pages/admin/admin-dashboard/study-components/view-questionnaires/manage-questionnaires.component.ts @@ -0,0 +1,60 @@ +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { Observable, Subscription } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { RouteNames, TaskType } from 'src/app/models/enums'; +import { Task } from 'src/app/models/Task'; +import { QuestionnaireNavigationConfig } from 'src/app/pages/tasks/questionnaire-reader/questionnaire-reader.component'; +import { TaskService } from 'src/app/services/task.service'; +import { ViewComponentsTableModel } from '../shared/view-components-table/view-components-table.component'; + +@Component({ + selector: 'app-manage-questionnaires', + templateUrl: './manage-questionnaires.component.html', + styleUrls: ['./manage-questionnaires.component.scss'], +}) +export class ManageQuestionnairesComponent implements OnInit { + constructor(private taskService: TaskService, private router: Router) {} + + subscriptions: Subscription[] = []; + + ngOnInit() { + this.tasks = this.taskService.tasks; + } + + tasks: Observable; + + get questionnairesForTable(): Observable> { + return this.tasks.pipe( + map((tasks) => (tasks ? tasks.filter((task) => task.taskType === TaskType.QUESTIONNAIRE) : [])), + map((tasks) => ({ + tableConfig: [ + { + columnHeader: 'Name', + columnKey: 'name', + }, + { + columnHeader: 'Description', + columnKey: 'description', + }, + ], + tableData: tasks, + msgOnEmpty: 'No questionnaires', + tableTitle: '', + })) + ); + } + + previewQuestionnaire(questionnaire: Task) { + const questionnaireConfig: QuestionnaireNavigationConfig = { + metadata: questionnaire.config, + mode: 'test', + }; + + this.router.navigate([`${RouteNames.QUESTIONNAIRE}`], { state: questionnaireConfig }); + } + + ngOnDestroy() { + this.subscriptions.forEach((sub) => sub.unsubscribe()); + } +} diff --git a/src/app/pages/admin/admin-dashboard/study-components/view-tasks/view-tasks.component.html b/src/app/pages/admin/admin-dashboard/study-components/view-tasks/view-tasks.component.html index 199fc155..c202206a 100644 --- a/src/app/pages/admin/admin-dashboard/study-components/view-tasks/view-tasks.component.html +++ b/src/app/pages/admin/admin-dashboard/study-components/view-tasks/view-tasks.component.html @@ -1,63 +1,9 @@ -
-

Psharplab Tasks

- -

Neuropsych Battery (NAB)

-
- -
- - - - - - - - - - - - - - - - - - -
Name{{ NABTask.name }}Description{{ NABTask.description }}Actions - -
-
+
+
- -
-

Experimental Tasks

-
- -
- - - - - - - - - - - - - - - - - -
Name{{ experimentTask.name }}Description{{ experimentTask.description }}Actions - -
-
+
+
diff --git a/src/app/pages/admin/admin-dashboard/study-components/view-tasks/view-tasks.component.ts b/src/app/pages/admin/admin-dashboard/study-components/view-tasks/view-tasks.component.ts index e5cd8c5a..7b533fa7 100644 --- a/src/app/pages/admin/admin-dashboard/study-components/view-tasks/view-tasks.component.ts +++ b/src/app/pages/admin/admin-dashboard/study-components/view-tasks/view-tasks.component.ts @@ -1,54 +1,65 @@ -import { Component, OnDestroy, OnInit } from "@angular/core"; -import { Router } from "@angular/router"; -import { TaskService } from "../../../../../services/task.service"; -import { Task } from "../../../../../models/Task"; -import { Observable, Subscription } from "rxjs"; -import { Platform, RouteNames, TaskType } from "../../../../../models/enums"; -import { map } from "rxjs/operators"; -import { HttpClient } from "@angular/common/http"; -import { TaskPlayerNavigationConfig } from "src/app/pages/tasks/task-playables/task-player/task-player.component"; +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { TaskService } from '../../../../../services/task.service'; +import { Task } from '../../../../../models/Task'; +import { Observable, Subscription } from 'rxjs'; +import { Platform, RouteNames, TaskType } from '../../../../../models/enums'; +import { map } from 'rxjs/operators'; +import { HttpClient } from '@angular/common/http'; +import { TaskPlayerNavigationConfig } from 'src/app/pages/tasks/task-playables/task-player/task-player.component'; +import { ViewComponentsTableModel } from '../shared/view-components-table/view-components-table.component'; @Component({ - selector: "app-view-tasks", - templateUrl: "./view-tasks.component.html", - styleUrls: ["./view-tasks.component.scss"], + selector: 'app-view-tasks', + templateUrl: './view-tasks.component.html', + styleUrls: ['./view-tasks.component.scss'], }) export class ViewTasksComponent implements OnInit, OnDestroy { - // contains Ids of completed tasks - completedTasks: number[] = []; - subscribers: Subscription[] = []; - displayedColumnsForStudies = ["name", "description", "route"]; - tasklist: Observable; - constructor(private router: Router, private taskService: TaskService, private http: HttpClient) {} + constructor(private router: Router, private taskService: TaskService) {} ngOnInit() { this.tasklist = this.taskService.tasks; } - run(task: Task) { - const navigationConfig: TaskPlayerNavigationConfig = { - metadata: task.config, - mode: "test", - }; - - this.router.navigate([`${RouteNames.TASKPLAYER}`], { state: navigationConfig }); - } - - get NABTask(): Observable { + get NABTasksForTable(): Observable> { return this.tasklist.pipe( map((tasks) => tasks ? tasks.filter((task) => task.taskType === TaskType.NAB && task.fromPlatform === Platform.PSHARPLAB) : [] - ) + ), + map((tasks) => ({ + tableConfig: [ + { + columnHeader: 'Name', + columnKey: 'name', + }, + { + columnHeader: 'Description', + columnKey: 'description', + }, + ], + tableData: tasks, + msgOnEmpty: 'No NAB Tasks', + tableTitle: 'Neuropsych Battery (NAB)', + })) ); } - get experimentalTasks(): Observable { + run(task: Task) { + const navigationConfig: TaskPlayerNavigationConfig = { + metadata: task.config, + mode: 'test', + }; + + this.router.navigate([`${RouteNames.TASKPLAYER}`], { state: navigationConfig }); + } + + get experimentalTasksForTable(): Observable> { return this.tasklist.pipe( map((tasks) => tasks @@ -56,16 +67,25 @@ export class ViewTasksComponent implements OnInit, OnDestroy { (task) => task.taskType === TaskType.EXPERIMENTAL && task.fromPlatform === Platform.PSHARPLAB ) : [] - ) + ), + map((tasks) => ({ + tableConfig: [ + { + columnHeader: 'Name', + columnKey: 'name', + }, + { + columnHeader: 'Description', + columnKey: 'description', + }, + ], + tableData: tasks, + msgOnEmpty: 'No NAB Tasks', + tableTitle: 'Experimental Tasks', + })) ); } - // returns true if the given task is complete, and false otherwise - taskIsComplete(task: Task): boolean { - if (!this.completedTasks || !task || !task.id) return false; - return this.completedTasks.includes(task.id) ? true : false; - } - ngOnDestroy() { this.subscribers.forEach((x) => x.unsubscribe()); } diff --git a/src/app/pages/admin/admin.module.ts b/src/app/pages/admin/admin.module.ts index 5a93d7d1..1114450a 100644 --- a/src/app/pages/admin/admin.module.ts +++ b/src/app/pages/admin/admin.module.ts @@ -1,22 +1,24 @@ -import { NgModule } from "@angular/core"; -import { CommonModule } from "@angular/common"; -import { AdminDashboardComponent } from "./admin-dashboard/admin-dashboard.component"; -import { AdminRoutingModule } from "./admin-routing.module"; -import { DataComponent } from "./admin-dashboard/data/data.component"; -import { ManageGuestsComponent } from "./admin-dashboard/manage-guests/manage-guests.component"; -import { StudiesComponent } from "./admin-dashboard/studies/studies.component"; -import { StudyComponentsComponent } from "./admin-dashboard/study-components/study-components.component"; -import { ViewTasksComponent } from "./admin-dashboard/study-components/view-tasks/view-tasks.component"; -import { MaterialModule } from "src/app/modules/material/material.module"; -import { SharedModule } from "../shared/shared.module"; -import { RouterModule } from "@angular/router"; -import { PavloviaTasksComponent } from "./admin-dashboard/study-components/pavlovia-tasks/pavlovia-tasks.component"; -import { ManageQuestionnairesComponent } from "./admin-dashboard/study-components/manage-questionnaires/manage-questionnaires.component"; -import { DataTableComponent } from "./admin-dashboard/data/data-table/data-table.component"; -import { ViewStudiesComponent } from "./admin-dashboard/studies/view-studies/view-studies.component"; -import { TaskModule } from "../tasks/task.module"; -import { FormsModule, ReactiveFormsModule } from "@angular/forms"; -import { CreateModifyStudyComponent } from "./admin-dashboard/studies/create-modify-study/create-modify-study.component"; +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { AdminDashboardComponent } from './admin-dashboard/admin-dashboard.component'; +import { AdminRoutingModule } from './admin-routing.module'; +import { DataComponent } from './admin-dashboard/data/data.component'; +import { ManageGuestsComponent } from './admin-dashboard/manage-guests/manage-guests.component'; +import { StudiesComponent } from './admin-dashboard/studies/studies.component'; +import { StudyComponentsComponent } from './admin-dashboard/study-components/study-components.component'; +import { ViewTasksComponent } from './admin-dashboard/study-components/view-tasks/view-tasks.component'; +import { MaterialModule } from 'src/app/modules/material/material.module'; +import { SharedModule } from '../shared/shared.module'; +import { RouterModule } from '@angular/router'; +import { PavloviaTasksComponent } from './admin-dashboard/study-components/view-pavlovia-tasks/pavlovia-tasks.component'; +import { ManageQuestionnairesComponent } from './admin-dashboard/study-components/view-questionnaires/manage-questionnaires.component'; +import { DataTableComponent } from './admin-dashboard/data/data-table/data-table.component'; +import { ViewStudiesComponent } from './admin-dashboard/studies/view-studies/view-studies.component'; +import { TaskModule } from '../tasks/task.module'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { CreateModifyStudyComponent } from './admin-dashboard/studies/create-modify-study/create-modify-study.component'; +import { ViewInfoDisplaysComponent } from './admin-dashboard/study-components/view-info-displays/view-info-displays.component'; +import { ViewComponentsTableComponent } from './admin-dashboard/study-components/shared/view-components-table/view-components-table.component'; @NgModule({ declarations: [ @@ -31,6 +33,8 @@ import { CreateModifyStudyComponent } from "./admin-dashboard/studies/create-mod PavloviaTasksComponent, ManageQuestionnairesComponent, DataTableComponent, + ViewInfoDisplaysComponent, + ViewComponentsTableComponent, ], imports: [ CommonModule, diff --git a/src/app/pages/participant/participant-dashboard/participant-studies/consent-dialog/consent-dialog.component.html b/src/app/pages/participant/participant-dashboard/participant-studies/consent-dialog/consent-dialog.component.html index 5fea00c1..0baebbbc 100644 --- a/src/app/pages/participant/participant-dashboard/participant-studies/consent-dialog/consent-dialog.component.html +++ b/src/app/pages/participant/participant-dashboard/participant-studies/consent-dialog/consent-dialog.component.html @@ -1,3 +1,3 @@ - + diff --git a/src/app/pages/shared/consent-component/consent-reader.component.html b/src/app/pages/shared/consent-component/consent-reader.component.html index f19dcc82..631028db 100644 --- a/src/app/pages/shared/consent-component/consent-reader.component.html +++ b/src/app/pages/shared/consent-component/consent-reader.component.html @@ -1,16 +1,16 @@ -
+
- Organizations + Organizations

- {{ consentNavigationConfig.metadata?.title }} + {{ readerMetadata.metadata?.title }}

- +

{{ block.caption }}  {{ wordBlock }} @@ -20,12 +20,12 @@

- {{ consentNavigationConfig.metadata?.secondTitle }} + {{ readerMetadata.metadata?.secondTitle }}

-
+

{{ block.caption }}

@@ -36,27 +36,27 @@

- {{ consentNavigationConfig.metadata?.endMessage }} + {{ readerMetadata.metadata?.endMessage }}
-
-
-
- + +
+
diff --git a/src/app/pages/tasks/info-display/info-display.component.scss b/src/app/pages/tasks/info-display/info-display.component.scss index e69de29b..8836730e 100644 --- a/src/app/pages/tasks/info-display/info-display.component.scss +++ b/src/app/pages/tasks/info-display/info-display.component.scss @@ -0,0 +1,16 @@ +.info-display-container { + padding: 2% 4%; +} + +.title-text { + color: #3f51b5; +} + +.sub-text { + color: #464646; +} + +.button-container { + display: flex; + justify-content: space-around; +} diff --git a/src/app/pages/tasks/info-display/info-display.component.ts b/src/app/pages/tasks/info-display/info-display.component.ts index e10e43bf..2564838f 100644 --- a/src/app/pages/tasks/info-display/info-display.component.ts +++ b/src/app/pages/tasks/info-display/info-display.component.ts @@ -1,17 +1,15 @@ -import { Component, OnDestroy, OnInit } from "@angular/core"; -import { Subject } from "rxjs"; -import { IOnComplete, Playable } from "../task-playables/playable"; -import { Navigation } from "../shared/navigation-buttons/navigation-buttons.component"; -import { TaskManagerService } from "src/app/services/task-manager.service"; -import { Router } from "@angular/router"; -import { UserService } from "src/app/services/user.service"; +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { TaskManagerService } from 'src/app/services/task-manager.service'; +import { Router } from '@angular/router'; +import { UserService } from 'src/app/services/user.service'; +import { AbstractBaseReaderComponent } from '../shared/base-reader'; +import { ParticipantRouteNames, RouteNames } from 'src/app/models/enums'; export interface InfoDisplayMetadata { shouldIncrementIndex: boolean; title?: string; - subtitle?: string; sections?: InfoDisplaySection[]; - buttons?: InfoDisplayButtonConfig[]; + buttons?: InfoDisplayButtonConfig; } export interface InfoDisplaySection { @@ -26,24 +24,43 @@ export interface InfoDisplayButtonConfig { export interface InfoDisplayNavigationConfig { metadata: InfoDisplayMetadata; - mode: "test" | "actual"; + mode: 'test' | 'actual'; } @Component({ - selector: "app-info-display", - templateUrl: "./info-display.component.html", - styleUrls: ["./info-display.component.scss"], + selector: 'app-info-display', + templateUrl: './info-display.component.html', + styleUrls: ['./info-display.component.scss'], }) -export class InfoDisplayComponent implements OnInit, OnDestroy { - infoDisplayMetadata: InfoDisplayMetadata; +export class InfoDisplayComponent implements OnInit, OnDestroy, AbstractBaseReaderComponent { + readerMetadata: InfoDisplayNavigationConfig; + + get shouldShowHomeButton(): boolean { + return this.readerMetadata?.metadata?.buttons?.displayHomeButton; + } + + get shouldShowContinueButton(): boolean { + return this.readerMetadata?.metadata?.buttons?.displayHomeButton; + } + + get title(): string { + return this.readerMetadata?.metadata?.title || ''; + } + + get sections(): InfoDisplaySection[] { + return this.readerMetadata?.metadata?.sections || []; + } constructor(private taskManager: TaskManagerService, private router: Router, private userService: UserService) { - const state = this.router.getCurrentNavigation().extras.state as InfoDisplayMetadata; + const state = this.router.getCurrentNavigation().extras.state as InfoDisplayNavigationConfig; if (state) { - this.infoDisplayMetadata = state; + this.readerMetadata = state; + const incrementIndex = this.readerMetadata.metadata?.shouldIncrementIndex; - if (!this.userService.isCrowdsourcedUser) { + // crowdsourced users cannot take breaks as they do not have accounts and so cannot sign back in. + // this is a safety check: realistically, crowdsourced users will not be shown info display slides + if (!this.userService.isCrowdsourcedUser && incrementIndex) { this.taskManager.setTaskAsComplete().subscribe((res) => {}); } } else { @@ -51,6 +68,14 @@ export class InfoDisplayComponent implements OnInit, OnDestroy { } } + onSubmit(arg: 'home' | 'continue') { + if (arg === 'home') { + this.router.navigate([ParticipantRouteNames.DASHBOARD_BASEROUTE]); + } else { + this.taskManager.next(); + } + } + ngOnInit(): void {} ngOnDestroy(): void {} diff --git a/src/app/pages/tasks/questionnaire-reader/questionnaire-reader.component.html b/src/app/pages/tasks/questionnaire-reader/questionnaire-reader.component.html index 61096e2c..6697504b 100644 --- a/src/app/pages/tasks/questionnaire-reader/questionnaire-reader.component.html +++ b/src/app/pages/tasks/questionnaire-reader/questionnaire-reader.component.html @@ -112,7 +112,7 @@