From 4ce3d658c9c72c8564ab4d0496aa49a484e11ecd Mon Sep 17 00:00:00 2001 From: warrensearle Date: Tue, 27 Sep 2022 17:40:10 +0100 Subject: [PATCH 01/25] Merge from tasks --- src/App.vue | 4 + src/components/Page/Form.vue | 79 +++ src/components/Page/FullScreenButton.vue | 36 ++ src/components/Page/TitleBar.vue | 5 + src/helpers/constants.js | 72 ++- src/helpers/exerciseHelper.js | 307 ++++++++- src/helpers/tableHelper.js | 54 ++ src/helpers/taskHelper.js | 54 ++ src/router.js | 181 ++++++ src/store.js | 19 + src/store/checks/collection.js | 36 ++ src/store/exercise/diversity/document.js | 19 + src/store/panellists/collection.js | 26 + src/store/panellists/document.js | 47 ++ src/store/panels/collection.js | 48 ++ src/store/panels/document.js | 101 +++ src/store/tasks/collection.js | 40 ++ src/store/tasks/document.js | 27 + src/store/ui.js | 21 + src/views/Exercise/Tasks/Task.vue | 19 + src/views/Exercise/Tasks/Task/Completed.vue | 69 ++ .../Exercise/Tasks/Task/Data/Activated.vue | 3 + .../Exercise/Tasks/Task/Data/Initialised.vue | 3 + .../Exercise/Tasks/Task/Finalised.old.vue | 367 +++++++++++ src/views/Exercise/Tasks/Task/Finalised.vue | 195 ++++++ .../Exercise/Tasks/Task/Finalised/List.vue | 93 +++ .../Tasks/Task/Finalised/SetPassMark.vue | 48 ++ .../Exercise/Tasks/Task/Finalised/View.vue | 377 +++++++++++ src/views/Exercise/Tasks/Task/Loading.vue | 72 +++ .../Tasks/Task/ModerationActivated.vue | 25 + .../Tasks/Task/ModerationInitialised.vue | 25 + src/views/Exercise/Tasks/Task/New.vue | 54 ++ src/views/Exercise/Tasks/Task/New/default.vue | 111 ++++ .../Exercise/Tasks/Task/New/dummyData.js | 422 +++++++++++++ .../Tasks/Task/New/qualifyingTest.vue | 19 + .../Exercise/Tasks/Task/New/stageOutcome.vue | 573 +++++++++++++++++ .../Exercise/Tasks/Task/Panel/Constants.js | 27 + src/views/Exercise/Tasks/Task/Panel/New.vue | 84 +++ .../Tasks/Task/Panel/Panellists/Edit.vue | 118 ++++ .../Tasks/Task/Panel/Panellists/View.vue | 72 +++ src/views/Exercise/Tasks/Task/Panel/View.vue | 519 +++++++++++++++ .../Tasks/Task/Panel/components/AddEdit.vue | 50 ++ .../Task/Panel/components/SelectPanel.vue | 48 ++ .../Tasks/Task/Panel/components/View.vue | 41 ++ .../Exercise/Tasks/Task/PanelsActivated.vue | 596 ++++++++++++++++++ .../Exercise/Tasks/Task/PanelsInitialised.vue | 356 +++++++++++ .../Exercise/Tasks/Task/StatusChanges.vue | 238 +++++++ .../Tasks/Task/StatusChanges/SetStatus.vue | 51 ++ .../Tasks/Task/StatusChanges/StatusTag.vue | 38 ++ .../Exercise/Tasks/Task/TestActivated.vue | 99 +++ .../Exercise/Tasks/Task/TestInitialised.vue | 82 +++ src/views/Exercise/Tasks/Task/helper.js | 37 ++ src/views/Panellists/List.vue | 98 +++ src/views/Panellists/Panellist.vue | 119 ++++ src/views/Panellists/components/AddEdit.vue | 52 ++ src/views/Panellists/components/View.vue | 41 ++ 56 files changed, 6412 insertions(+), 5 deletions(-) create mode 100644 src/components/Page/Form.vue create mode 100644 src/components/Page/FullScreenButton.vue create mode 100644 src/components/Page/TitleBar.vue create mode 100644 src/helpers/tableHelper.js create mode 100644 src/helpers/taskHelper.js create mode 100644 src/store/checks/collection.js create mode 100644 src/store/exercise/diversity/document.js create mode 100644 src/store/panellists/collection.js create mode 100644 src/store/panellists/document.js create mode 100644 src/store/panels/collection.js create mode 100644 src/store/panels/document.js create mode 100644 src/store/tasks/collection.js create mode 100644 src/store/tasks/document.js create mode 100644 src/store/ui.js create mode 100644 src/views/Exercise/Tasks/Task.vue create mode 100644 src/views/Exercise/Tasks/Task/Completed.vue create mode 100644 src/views/Exercise/Tasks/Task/Data/Activated.vue create mode 100644 src/views/Exercise/Tasks/Task/Data/Initialised.vue create mode 100644 src/views/Exercise/Tasks/Task/Finalised.old.vue create mode 100644 src/views/Exercise/Tasks/Task/Finalised.vue create mode 100644 src/views/Exercise/Tasks/Task/Finalised/List.vue create mode 100644 src/views/Exercise/Tasks/Task/Finalised/SetPassMark.vue create mode 100644 src/views/Exercise/Tasks/Task/Finalised/View.vue create mode 100644 src/views/Exercise/Tasks/Task/Loading.vue create mode 100644 src/views/Exercise/Tasks/Task/ModerationActivated.vue create mode 100644 src/views/Exercise/Tasks/Task/ModerationInitialised.vue create mode 100644 src/views/Exercise/Tasks/Task/New.vue create mode 100644 src/views/Exercise/Tasks/Task/New/default.vue create mode 100644 src/views/Exercise/Tasks/Task/New/dummyData.js create mode 100644 src/views/Exercise/Tasks/Task/New/qualifyingTest.vue create mode 100644 src/views/Exercise/Tasks/Task/New/stageOutcome.vue create mode 100644 src/views/Exercise/Tasks/Task/Panel/Constants.js create mode 100644 src/views/Exercise/Tasks/Task/Panel/New.vue create mode 100644 src/views/Exercise/Tasks/Task/Panel/Panellists/Edit.vue create mode 100644 src/views/Exercise/Tasks/Task/Panel/Panellists/View.vue create mode 100644 src/views/Exercise/Tasks/Task/Panel/View.vue create mode 100644 src/views/Exercise/Tasks/Task/Panel/components/AddEdit.vue create mode 100644 src/views/Exercise/Tasks/Task/Panel/components/SelectPanel.vue create mode 100644 src/views/Exercise/Tasks/Task/Panel/components/View.vue create mode 100644 src/views/Exercise/Tasks/Task/PanelsActivated.vue create mode 100644 src/views/Exercise/Tasks/Task/PanelsInitialised.vue create mode 100644 src/views/Exercise/Tasks/Task/StatusChanges.vue create mode 100644 src/views/Exercise/Tasks/Task/StatusChanges/SetStatus.vue create mode 100644 src/views/Exercise/Tasks/Task/StatusChanges/StatusTag.vue create mode 100644 src/views/Exercise/Tasks/Task/TestActivated.vue create mode 100644 src/views/Exercise/Tasks/Task/TestInitialised.vue create mode 100644 src/views/Exercise/Tasks/Task/helper.js create mode 100644 src/views/Panellists/List.vue create mode 100644 src/views/Panellists/Panellist.vue create mode 100644 src/views/Panellists/components/AddEdit.vue create mode 100644 src/views/Panellists/components/View.vue diff --git a/src/App.vue b/src/App.vue index 1c8ab3574..4aa181f92 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,6 +1,7 @@ + + diff --git a/src/components/Page/FullScreenButton.vue b/src/components/Page/FullScreenButton.vue new file mode 100644 index 000000000..91647b4b2 --- /dev/null +++ b/src/components/Page/FullScreenButton.vue @@ -0,0 +1,36 @@ + + + diff --git a/src/components/Page/TitleBar.vue b/src/components/Page/TitleBar.vue new file mode 100644 index 000000000..29732a00d --- /dev/null +++ b/src/components/Page/TitleBar.vue @@ -0,0 +1,5 @@ + diff --git a/src/helpers/constants.js b/src/helpers/constants.js index a350fbefa..004a51e44 100644 --- a/src/helpers/constants.js +++ b/src/helpers/constants.js @@ -1,7 +1,7 @@ import { STATUS, EXERCISE_STAGE, - APPLICATION_STATUS, + // APPLICATION_STATUS, SHORTLISTING, QUALIFYING_TEST, QUALIFYING_TEST_RESPONSE, @@ -16,10 +16,80 @@ const ADVERT_TYPES = { EXTERNAL: 'external', }; +// const EXERCISE_STAGE = { +// APPLIED: 'applied', +// SHORTLISTED: 'shortlisted', +// SELECTABLE: 'selectable', +// RECOMMENDED: 'recommended', +// HANDOVER: 'handover', +// }; + +const APPLICATION_STATUS = { + CRITICAL_ANALYSIS_PASSED: 'criticalAnalysisPassed', + CRITICAL_ANALYSIS_FAILED: 'criticalAnalysisFailed', + SITUATIONAL_JUDGEMENT_PASSED: 'situationalJudgementPassed', + SITUATIONAL_JUDGEMENT_FAILED: 'situationalJudgementFailed', + QUALIFYING_TEST_PASSED: 'qualifyingTestPassed', + QUALIFYING_TEST_FAILED: 'qualifyingTestFailed', + SCENARIO_TEST_PASSED: 'scenarioTestPassed', + SCENARIO_TEST_FAILED: 'scenarioTestFailed', + SIFT_PASSED: 'siftPassed', + SIFT_FAILED: 'siftFailed', + TELEPHONE_ASSESSMENT_PASSED: 'telephoneAssessmentPassed', + TELEPHONE_ASSESSMENT_FAILED: 'telephoneAssessmentFailed', + ELIGIBILITY_SCC_PASSED: 'eligibilitySCCPassed', + // SELECTION_INVITED: 'selectionInvited', + REJECTED_INELIGIBLE_STATUTORY: 'rejectedIneligibleStatutory', + REJECTED_INELIGIBLE_ADDITIONAL: 'rejectedIneligibleAdditional', + REJECTED_CHARACTER: 'rejectedCharacter', + WITHDRAWN: 'withdrawn', + SELECTION_PASSED: 'selectionPassed', + SELECTION_FAILED: 'selectionFailed', + PASSED_RECOMMENDED: 'passedRecommended', + PASSED_NOT_RECOMMENDED: 'passedNotRecommended', + RECOMMENDED_IMMEDIATE: 'recommendedImmediate', + RECOMMENDED_FUTURE: 'recommendedFuture', + RECONSIDER: 'reconsider', + SECOND_STAGE_INVITED: 'secondStageInvited', +}; + +const APPLICATION_SUCCESS_STATUSES = [ + APPLICATION_STATUS.CRITICAL_ANALYSIS_PASSED, + APPLICATION_STATUS.SITUATIONAL_JUDGEMENT_PASSED, + APPLICATION_STATUS.QUALIFYING_TEST_PASSED, + APPLICATION_STATUS.SCENARIO_TEST_PASSED, + APPLICATION_STATUS.SIFT_PASSED, + APPLICATION_STATUS.TELEPHONE_ASSESSMENT_PASSED, + APPLICATION_STATUS.ELIGIBILITY_SCC_PASSED, + APPLICATION_STATUS.SELECTION_PASSED, + APPLICATION_STATUS.PASSED_RECOMMENDED, + APPLICATION_STATUS.RECOMMENDED_IMMEDIATE, + APPLICATION_STATUS.RECOMMENDED_FUTURE, + APPLICATION_STATUS.SECOND_STAGE_INVITED, +]; + +const TASK_TYPE = { + SIFT: 'sift', + SELECTION: 'selection', + SCENARIO: 'scenarioTest', + CRITICAL_ANALYSIS: 'criticalAnalysis', + SITUATIONAL_JUDGEMENT: 'situationalJudgement', + QUALIFYING_TEST: 'qualifyingTest', + TELEPHONE_ASSESSMENT: 'telephoneAssessment', + ELIGIBILITY_SCC: 'eligibilitySCC', + CHARACTER_AND_SELECTION_SCC: 'characterAndSelectionSCC', + STATUTORY_CONSULTATION: 'statutoryConsultation', + SHORTLISTING_OUTCOME: 'shortlistingOutcome', + WELSH_ASSESSMENT: 'welshAssessment', + SELECTION_OUTCOME: 'selectionOutcome', +}; + export { STATUS, EXERCISE_STAGE, APPLICATION_STATUS, + APPLICATION_SUCCESS_STATUSES, + TASK_TYPE, SHORTLISTING, QUALIFYING_TEST, QUALIFYING_TEST_RESPONSE, diff --git a/src/helpers/exerciseHelper.js b/src/helpers/exerciseHelper.js index 619d9fabb..d7688b396 100644 --- a/src/helpers/exerciseHelper.js +++ b/src/helpers/exerciseHelper.js @@ -1,6 +1,8 @@ /*eslint func-style: ["error", "declaration"]*/ import clone from 'clone'; -import { ADVERT_TYPES } from '@/helpers/constants'; +import { ADVERT_TYPES, EXERCISE_STAGE, APPLICATION_STATUS, SHORTLISTING, TASK_TYPE } from '@/helpers/constants'; +import exerciseTimeline from '../helpersTMP/Timeline/exerciseTimeline'; +import createTimeline from '@jac-uk/jac-kit/helpers/Timeline/createTimeline'; /** Used in Admin:- APPLICATION_STEPS, @@ -19,11 +21,21 @@ hasSelfAssessment, exerciseApplicationParts, configuredApplicationParts, applicationContentList, -unselectedApplicationParts, +unselectedApplicationParts */ export { APPLICATION_STEPS, + CAPABILITIES, + SELECTION_CATEGORIES, + TASK_STATUS, + TASK_TYPE, + STAGE_TASKS, + getTimelineTasks, + getTaskTypes, + taskEntryStatus, + previousTaskType, + emptyScoreSheet, exerciseStates, exerciseAdvertTypes, applicationContentSteps, @@ -44,6 +56,10 @@ export { isLegal, isNonLegal, isTribunal, + isWelshAdministrationRequired, + isSpeakWelshRequired, + isReadWriteWelshRequired, + isApplyingForWelshPost, currentState, applicationContentList, exerciseApplicationParts, // TODO review naming of applicationParts methods :) @@ -57,7 +73,11 @@ export { isApplicationComplete, hasApplicationProcess, applicationCounts, - applicationRecordCounts + applicationRecordCounts, + availableStages, + availableStatuses, + getPreviousStage, + getNextStage }; // const EXERCISE_STATES = ['draft', 'ready', 'approved', 'shortlisting', 'selection', 'recommendation', 'handover', 'archived']; @@ -103,6 +123,124 @@ const APPLICATION_PARTS = [ 'additionalInfo', ]; +const CAPABILITIES = ['L&J', 'PQ', 'L', 'EJ', 'PBK', 'ACI', 'WCO', 'MWE', 'OVERALL']; +const SELECTION_CATEGORIES = ['leadership', 'roleplay', 'situational', 'interview', 'overall']; + +const STAGE_TASKS = { + shortlisting: [ + TASK_TYPE.CRITICAL_ANALYSIS, + TASK_TYPE.SITUATIONAL_JUDGEMENT, + TASK_TYPE.SCENARIO, + TASK_TYPE.TELEPHONE_ASSESSMENT, + TASK_TYPE.SIFT, + TASK_TYPE.ELIGIBILITY_SCC, + TASK_TYPE.SHORTLISTING_OUTCOME, + ], + selection: [ + TASK_TYPE.SELECTION, + TASK_TYPE.STATUTORY_CONSULTATION, + TASK_TYPE.CHARACTER_AND_SELECTION_SCC, + TASK_TYPE.SELECTION_OUTCOME, + ], +}; + +const TASK_STATUS = { + DATA_INITIALISED: 'dataInitialised', + DATA_ACTIVATED: 'dataActivated', + TEST_INITIALISED: 'testInitialised', + TEST_ACTIVATED: 'testActivated', + PANELS_INITIALISED: 'panelsInitialised', + PANELS_ACTIVATED: 'panelsActivated', + MODERATION_INITIALISED: 'moderationInitialised', + MODERATION_ACTIVATED: 'moderationActivated', + STATUS_CHANGES: 'statusChanges', + FINALISED: 'finalised', + CHECKS: 'checks', + COMPLETED: 'completed', +}; + +/** + * get task types in sequence + * - look at exercise shortlisting and timeline + * - return array of tasks + * get previous task + * get next task + * get outcome statuses + */ + +function getTimelineTasks(exercise, taskType) { + const timeline = createTimeline(exerciseTimeline(exercise)); + const timelineTasks = timeline.filter(item => item.taskType && (!taskType || item.taskType === taskType)); + return timelineTasks; +} + +function getTaskTypes(exercise, stage) { + let taskTypes = getTimelineTasks(exercise).map(item => item.taskType).filter((value, index, thisArray) => thisArray.indexOf(value) === index); + if (stage) { + taskTypes = taskTypes.filter(taskType => STAGE_TASKS[stage].indexOf(taskType) >= 0); + } + const indexCA = taskTypes.indexOf(TASK_TYPE.CRITICAL_ANALYSIS); + const indexSJ = taskTypes.indexOf(TASK_TYPE.SITUATIONAL_JUDGEMENT); + if (indexCA >= 0 && indexSJ >= 0) { + const indexQT = Math.max(indexCA, indexSJ); + taskTypes.splice(indexQT + 1, 0, TASK_TYPE.QUALIFYING_TEST); + } + return taskTypes; +} + +function previousTaskType(exercise, type) { + let prevTaskType = ''; + if (!exercise) return prevTaskType; + const taskTypes = getTaskTypes(exercise); + const currentIndex = taskTypes.indexOf(type); + if (currentIndex > 0) { + if ([TASK_TYPE.CRITICAL_ANALYSIS, TASK_TYPE.SITUATIONAL_JUDGEMENT, TASK_TYPE.QUALIFYING_TEST].indexOf(type) >= 0) { + for (let i = currentIndex; i >= 0; --i) { + if ([TASK_TYPE.CRITICAL_ANALYSIS, TASK_TYPE.SITUATIONAL_JUDGEMENT, TASK_TYPE.QUALIFYING_TEST].indexOf(taskTypes[i]) < 0) { + prevTaskType = taskTypes[i]; + break; + } + } + } else { + prevTaskType = taskTypes[currentIndex - 1]; + } + } + return prevTaskType; +} + +function taskEntryStatus(exercise, type) { + let status = ''; + if (!exercise) return status; + const prevTaskType = previousTaskType(exercise, type); + if (prevTaskType) { + status = `${prevTaskType}Passed`; + } + return status; +} + +// merit list helpers +function emptyScoreSheet({ type, selectedCapabilities }) { + let capabilities = CAPABILITIES; + if (selectedCapabilities) { + capabilities = CAPABILITIES.filter(cap => selectedCapabilities.indexOf(cap) >= 0); + } + // TODO ensure this is specific to exercise + const fullScoreSheet = { + sift: { + scoreSheet: capabilities.reduce((acc, curr) => (acc[curr] = '', acc), {}), + }, + selection: { + scoreSheet: { + leadership: capabilities.reduce((acc, curr) => (acc[curr] = '', acc), {}), + roleplay: capabilities.reduce((acc, curr) => (acc[curr] = '', acc), {}), + interview: capabilities.reduce((acc, curr) => (acc[curr] = '', acc), {}), + overall: capabilities.reduce((acc, curr) => (acc[curr] = '', acc), {}), + }, + }, + }; + return type ? fullScoreSheet[type] : fullScoreSheet; +} + // application helpers function applicationCurrentStep(exercise, application) { if (!application._processing) { return null; } @@ -299,6 +437,18 @@ function isNonLegal(data) { function isTribunal(data) { return data.isCourtOrTribunal === 'tribunal'; } +function isWelshAdministrationRequired(exercise) { + return exercise.welshRequirementType.includes('welsh-administration-questions'); +} +function isSpeakWelshRequired(exercise) { + return exercise.welshRequirementType.includes('welsh-speaking'); +} +function isReadWriteWelshRequired(exercise) { + return exercise.welshRequirementType.includes('welsh-reading-writing'); +} +function isApplyingForWelshPost(exercise, application) { + return exercise.welshRequirement && application.applyingForWelshPost; +} function currentState(data) { // default to registration if (data._applicationContent && data._applicationContent._currentStep) { if (APPLICATION_STEPS.indexOf(data._applicationContent._currentStep.step) >= 0) { @@ -479,7 +629,6 @@ function isApplicationComplete(vacancy, application) { if (!requiredParts) return false; const partsToComplete = Object.keys(requiredParts).filter(part => requiredParts[part] === true); const incompleteParts = partsToComplete.filter(part => application.progress[part] !== true); - // console.log('incompleteParts', incompleteParts); return incompleteParts.length === 0; } @@ -489,3 +638,153 @@ function hasApplicationProcess(exercise) { const applicationSteps = configuredApplicationContentSteps(exercise); return applicationSteps.length >= 1; } + +function availableStages(exercise) { + const stages = []; + if (!exercise) return stages; + stages.push(EXERCISE_STAGE.APPLIED); + stages.push(EXERCISE_STAGE.SHORTLISTED); + stages.push(EXERCISE_STAGE.SELECTABLE); + stages.push(EXERCISE_STAGE.RECOMMENDED); + stages.push(EXERCISE_STAGE.HANDOVER); + return stages; +} + +function getPreviousStage(exercise, currentStage) { + switch (currentStage) { + case EXERCISE_STAGE.APPLIED: + return ''; + case EXERCISE_STAGE.SHORTLISTED: + return EXERCISE_STAGE.APPLIED; + case EXERCISE_STAGE.SELECTABLE: + return EXERCISE_STAGE.SHORTLISTED; + case EXERCISE_STAGE.RECOMMENDED: + return EXERCISE_STAGE.SELECTABLE; + case EXERCISE_STAGE.HANDOVER: + return EXERCISE_STAGE.RECOMMENDED; + } +} + +function getNextStage(exercise, currentStage, newStatus) { + switch (currentStage) { + case EXERCISE_STAGE.APPLIED: // TODO make this specific to exercise + if (newStatus === APPLICATION_STATUS.SIFT_PASSED) return EXERCISE_STAGE.SHORTLISTED; + break; + case EXERCISE_STAGE.SHORTLISTED: + if (newStatus === APPLICATION_STATUS.SELECTION_PASSED) return EXERCISE_STAGE.SELECTABLE; + break; + case EXERCISE_STAGE.SELECTABLE: + if (newStatus === APPLICATION_STATUS.PASSED_RECOMMENDED) return EXERCISE_STAGE.RECOMMENDED; + break; + case EXERCISE_STAGE.RECOMMENDED: + if (newStatus === APPLICATION_STATUS.RECOMMENDED_IMMEDIATE) return EXERCISE_STAGE.HANDOVER; + if (newStatus === APPLICATION_STATUS.RECOMMENDED_FUTURE) return EXERCISE_STAGE.HANDOVER; + break; + } + return currentStage; +} + +function availableStatuses(exercise, stage) { + let statuses = []; + switch (stage) { + case EXERCISE_STAGE.APPLIED: + statuses = [ + ...shortlistingStatuses(exercise), + // APPLICATION_STATUS.SELECTION_INVITED, + APPLICATION_STATUS.ELIGIBILITY_SCC_PASSED, + APPLICATION_STATUS.REJECTED_INELIGIBLE_STATUTORY, + APPLICATION_STATUS.REJECTED_INELIGIBLE_ADDITIONAL, + APPLICATION_STATUS.REJECTED_CHARACTER, + APPLICATION_STATUS.WITHDRAWN, + ]; + return statuses; + case EXERCISE_STAGE.SHORTLISTED: + statuses = [ + APPLICATION_STATUS.SELECTION_PASSED, + APPLICATION_STATUS.SELECTION_FAILED, + APPLICATION_STATUS.WITHDRAWN, + ]; + return statuses; + case EXERCISE_STAGE.SELECTABLE: + statuses = [ + APPLICATION_STATUS.PASSED_RECOMMENDED, + APPLICATION_STATUS.PASSED_NOT_RECOMMENDED, + APPLICATION_STATUS.WITHDRAWN, + ]; + return statuses; + case EXERCISE_STAGE.RECOMMENDED: + statuses = [ + APPLICATION_STATUS.REJECTED_INELIGIBLE_STATUTORY, + APPLICATION_STATUS.REJECTED_INELIGIBLE_ADDITIONAL, + APPLICATION_STATUS.REJECTED_CHARACTER, + APPLICATION_STATUS.RECOMMENDED_IMMEDIATE, + APPLICATION_STATUS.RECOMMENDED_FUTURE, + APPLICATION_STATUS.RECONSIDER, + APPLICATION_STATUS.SECOND_STAGE_INVITED, + APPLICATION_STATUS.WITHDRAWN, + ]; + return statuses; + case EXERCISE_STAGE.HANDOVER: + statuses = [ + APPLICATION_STATUS.RECOMMENDED_IMMEDIATE, + APPLICATION_STATUS.RECOMMENDED_FUTURE, + APPLICATION_STATUS.WITHDRAWN, + ]; + return statuses; + default: + statuses = [ // TODO make this specific to exercise + ...shortlistingStatuses(exercise), + APPLICATION_STATUS.SELECTION_INVITED, + APPLICATION_STATUS.REJECTED_INELIGIBLE_STATUTORY, + APPLICATION_STATUS.REJECTED_INELIGIBLE_ADDITIONAL, + APPLICATION_STATUS.REJECTED_CHARACTER, + APPLICATION_STATUS.SELECTION_PASSED, + APPLICATION_STATUS.SELECTION_FAILED, + APPLICATION_STATUS.PASSED_RECOMMENDED, + APPLICATION_STATUS.PASSED_NOT_RECOMMENDED, + APPLICATION_STATUS.RECOMMENDED_IMMEDIATE, + APPLICATION_STATUS.RECOMMENDED_FUTURE, + APPLICATION_STATUS.RECONSIDER, + APPLICATION_STATUS.SECOND_STAGE_INVITED, + APPLICATION_STATUS.WITHDRAWN, + ]; + return statuses; + } +} + +function shortlistingStatuses(exercise) { + const statuses = []; + if (exercise && exercise.shortlistingMethods && exercise.shortlistingMethods.length) { + if (exercise.shortlistingMethods.indexOf(SHORTLISTING.SITUATIONAL_JUDGEMENT_QUALIFYING_TEST) >= 0) { + statuses.push(APPLICATION_STATUS.SITUATIONAL_JUDGEMENT_PASSED); + statuses.push(APPLICATION_STATUS.SITUATIONAL_JUDGEMENT_FAILED); + } + if (exercise.shortlistingMethods.indexOf(SHORTLISTING.CRITICAL_ANALYSIS_QUALIFYING_TEST) >= 0) { + statuses.push(APPLICATION_STATUS.CRITICAL_ANALYSIS_PASSED); + statuses.push(APPLICATION_STATUS.CRITICAL_ANALYSIS_FAILED); + } + if ( + exercise.shortlistingMethods.indexOf(SHORTLISTING.SITUATIONAL_JUDGEMENT_QUALIFYING_TEST) >= 0 && + exercise.shortlistingMethods.indexOf(SHORTLISTING.CRITICAL_ANALYSIS_QUALIFYING_TEST) >= 0 + ) { + statuses.push(APPLICATION_STATUS.QUALIFYING_TEST_PASSED); + statuses.push(APPLICATION_STATUS.QUALIFYING_TEST_FAILED); + } + if (exercise.shortlistingMethods.indexOf(SHORTLISTING.SCENARIO_TEST_QUALIFYING_TEST) >= 0) { + statuses.push(APPLICATION_STATUS.SCENARIO_TEST_PASSED); + statuses.push(APPLICATION_STATUS.SCENARIO_TEST_FAILED); + } + if ( + exercise.shortlistingMethods.indexOf(SHORTLISTING.NAME_BLIND_PAPER_SIFT) >= 0 || + exercise.shortlistingMethods.indexOf(SHORTLISTING.PAPER_SIFT) >= 0 + ) { + statuses.push(APPLICATION_STATUS.SIFT_PASSED); + statuses.push(APPLICATION_STATUS.SIFT_FAILED); + } + if (exercise.shortlistingMethods.indexOf(SHORTLISTING.TELEPHONE_ASSESSMENT) >= 0) { + statuses.push(APPLICATION_STATUS.TELEPHONE_ASSESSMENT_PASSED); + statuses.push(APPLICATION_STATUS.TELEPHONE_ASSESSMENT_FAILED); + } + } + return statuses; +} diff --git a/src/helpers/tableHelper.js b/src/helpers/tableHelper.js new file mode 100644 index 000000000..c460006ee --- /dev/null +++ b/src/helpers/tableHelper.js @@ -0,0 +1,54 @@ +/*eslint func-style: ["error", "declaration"]*/ + +export { + getTableData +}; + +function getValueAtPath(data, path) { + if (path.indexOf('.') >= 0) { + const parts = path.split('.'); + let value = data; + parts.forEach(part => value = value[part]); + return value; + } else { + return data[path]; + } +} + +function getTableData(rows, columns, state) { + let returnData = JSON.parse(JSON.stringify(rows)); + if (state.searchTerm) { + const searchTerm = state.searchTerm.toLowerCase(); + returnData = returnData.filter(row => { + let isMatch = false; + columns.forEach(column => { + if (column.sort) { // making use of `sort` field for the searchable fields + if (getValueAtPath(row, column.sort).toLowerCase().indexOf(searchTerm) >= 0) isMatch = true; + } + }); + return isMatch; + }); + } + if (state.where && state.where.length) { + state.where.forEach(where => { + switch (where.comparator) { + case '==': + returnData = returnData.filter(row => row[where.field] === where.value); + break; + case 'in': + returnData = returnData.filter(row => where.value.indexOf(row[where.field]) >= 0); + break; + } + }); + } + if (state.orderBy) { + const orderBy = state.orderBy; + const direction = state.direction; + returnData.sort((a, b) => { + if (a[orderBy] < b[orderBy]) return direction === 'asc' ? -1 : 1; + if (a[orderBy] > b[orderBy]) return direction === 'asc' ? 1 : -1; + return 0; + }); + } + return returnData; +} diff --git a/src/helpers/taskHelper.js b/src/helpers/taskHelper.js new file mode 100644 index 000000000..ca529c925 --- /dev/null +++ b/src/helpers/taskHelper.js @@ -0,0 +1,54 @@ +/*eslint func-style: ["error", "declaration"]*/ + +export { + GRADES, + GRADE_VALUES, + getScoreSheetTotal +}; + +const MARKING_TYPE = { + GROUP: 'group', + NUMBER: 'number', + GRADE: 'grade', +}; +const GRADES = ['A', 'B', 'C', 'D']; +const GRADE_VALUES = { + 'A': 4, + 'B': 3, + 'C': 2, + 'D': 1, +}; + +function getScoreSheetTotal(markingScheme, scoreSheet) { + let score = 0; + if (!markingScheme) return score; + if (!scoreSheet) return score; + markingScheme.forEach(item => { + if (item.type === MARKING_TYPE.GROUP) { + item.children.forEach(child => { + score += getScoreSheetItemTotal(child, scoreSheet[item.ref]); + }); + } else { + score += getScoreSheetItemTotal(item, scoreSheet); + } + }); + return score; +} + +function getScoreSheetItemTotal(item, scoreSheet) { + if (!item.excludeFromScore) { + switch (item.type) { + case MARKING_TYPE.GRADE: + if (scoreSheet[item.ref] && GRADE_VALUES[scoreSheet[item.ref]]) { + return GRADE_VALUES[scoreSheet[item.ref]]; + } + break; + case MARKING_TYPE.NUMBER: + if (scoreSheet[item.ref]) { + return scoreSheet[item.ref]; + } + break; + } + } + return 0; +} diff --git a/src/router.js b/src/router.js index 92011ca83..214922280 100644 --- a/src/router.js +++ b/src/router.js @@ -91,6 +91,26 @@ import QualifyingTestResponse from '@/views/Exercise/Tasks/QualifyingTests/Quali import QualifyingTestResponseView from '@/views/Exercise/Tasks/QualifyingTests/QualifyingTest/Response/View'; import QualifyingTestsCover from '@/views/Exercise/Tasks/QualifyingTests/Cover'; +// Exercise task +import ExerciseTask from '@/views/Exercise/Tasks/Task'; +import ExerciseTaskLoading from '@/views/Exercise/Tasks/Task/Loading'; +import ExerciseTaskNew from '@/views/Exercise/Tasks/Task/New'; +import ExerciseTaskDataInitialised from '@/views/Exercise/Tasks/Task/Data/Initialised'; +import ExerciseTaskDataActivated from '@/views/Exercise/Tasks/Task/Data/Activated'; +import ExerciseTaskTestInitialised from '@/views/Exercise/Tasks/Task/TestInitialised'; +import ExerciseTaskTestActivated from '@/views/Exercise/Tasks/Task/TestActivated'; +import ExerciseTaskPanelsInitialised from '@/views/Exercise/Tasks/Task/PanelsInitialised'; +import ExerciseTaskPanelsActivated from '@/views/Exercise/Tasks/Task/PanelsActivated'; +import ExerciseTaskModerationInitialised from '@/views/Exercise/Tasks/Task/ModerationInitialised'; +import ExerciseTaskModerationActivated from '@/views/Exercise/Tasks/Task/ModerationActivated'; +import ExerciseTaskStatusChanges from '@/views/Exercise/Tasks/Task/StatusChanges'; +import ExerciseTaskFinalised from '@/views/Exercise/Tasks/Task/Finalised'; +import ExerciseTaskFinalisedList from '@/views/Exercise/Tasks/Task/Finalised/List'; +import ExerciseTaskFinalisedViewScore from '@/views/Exercise/Tasks/Task/Finalised/View'; +import ExerciseTaskCompleted from '@/views/Exercise/Tasks/Task/Completed'; +import ExerciseTaskPanelNew from '@/views/Exercise/Tasks/Task/Panel/New'; +import ExerciseTaskPanelView from '@/views/Exercise/Tasks/Task/Panel/View'; + // Exercise stages import ExerciseStages from '@/views/Exercise/Stages'; import ExerciseStagesReviewList from '@/views/Exercise/Stages/ReviewList'; @@ -936,6 +956,167 @@ const router = new Router({ }, ], }, + { + path: ':type', + component: ExerciseTask, + props: true, + children: [ + { + path: '', + redirect: 'new', + }, + { + path: 'loading', + component: ExerciseTaskLoading, + name: 'exercise-task-loading', + meta: { + requiresAuth: true, + title: 'Loading | Exercise task', + }, + }, + { + path: 'new', + component: ExerciseTaskNew, + name: 'exercise-task-new', + meta: { + requiresAuth: true, + title: 'New | Exercise task', + }, + }, + { + path: 'data-initialised', + component: ExerciseTaskDataInitialised, + name: 'exercise-task-dataInitialised', + meta: { + requiresAuth: true, + title: 'Data Initialised | Exercise task', + }, + }, + { + path: 'data-activated', + component: ExerciseTaskDataActivated, + name: 'exercise-task-dataActivated', + meta: { + requiresAuth: true, + title: 'Data Activated | Exercise task', + }, + }, + { + path: 'test-initialised', + component: ExerciseTaskTestInitialised, + name: 'exercise-task-testInitialised', + meta: { + requiresAuth: true, + title: 'Test Initialised | Exercise task', + }, + }, + { + path: 'test-activated', + component: ExerciseTaskTestActivated, + name: 'exercise-task-testActivated', + meta: { + requiresAuth: true, + title: 'Test Activated | Exercise task', + }, + }, + { + path: 'panels-initialised', + component: ExerciseTaskPanelsInitialised, + name: 'exercise-task-panelsInitialised', + meta: { + requiresAuth: true, + title: 'Panels Initialised | Exercise task', + }, + }, + { + path: 'panels-activated', + component: ExerciseTaskPanelsActivated, + name: 'exercise-task-panelsActivated', + meta: { + requiresAuth: true, + title: 'Panels Activated | Exercise task', + }, + }, + { + path: 'moderation-initialised', + component: ExerciseTaskModerationInitialised, + name: 'exercise-task-moderationInitialised', + meta: { + requiresAuth: true, + title: 'Moderation Initialised | Exercise task', + }, + }, + { + path: 'moderation-activated', + component: ExerciseTaskModerationActivated, + name: 'exercise-task-moderationActivated', + meta: { + requiresAuth: true, + title: 'Moderation Activated | Exercise task', + }, + }, + { + path: 'status-changes', + component: ExerciseTaskStatusChanges, + name: 'exercise-task-statusChanges', + meta: { + requiresAuth: true, + title: 'Status Changes | Exercise task', + }, + }, + { + path: 'finalised', + component: ExerciseTaskFinalised, + children: [ + { + path: '', + name: 'exercise-task-finalised', + component: ExerciseTaskFinalisedList, + meta: { + requiresAuth: true, + title: 'Finalised | Exercise task', + }, + }, + { + path: ':score', + component: ExerciseTaskFinalisedViewScore, + name: 'exercise-task-finalised-score', + meta: { + requiresAuth: true, + title: 'ViewScore | Finalised | Exercise task', + }, + }, + ], + }, + { + path: 'completed', + component: ExerciseTaskCompleted, + name: 'exercise-task-completed', + meta: { + requiresAuth: true, + title: 'Completed | Exercise task', + }, + }, + { + path: 'panel/new', + component: ExerciseTaskPanelNew, + name: 'exercise-task-panel-new', + meta: { + requiresAuth: true, + title: 'Create Panel | Exercise Task', + }, + }, + { + path: 'panel/:panelId', + component: ExerciseTaskPanelView, + name: 'exercise-task-panel', + meta: { + requiresAuth: true, + title: 'Panel | Exercise Task', + }, + }, + ], + }, ], }, { diff --git a/src/store.js b/src/store.js index 2f6e06679..e797001c8 100644 --- a/src/store.js +++ b/src/store.js @@ -5,6 +5,7 @@ Vue.use(Vuex); firestoreOptions.wait = true; // Vuex modules +import ui from '@/store/ui'; import auth from '@/store/auth'; import services from '@/store/services'; import exerciseCollection from '@/store/exercise/collection'; @@ -33,12 +34,23 @@ import qualifyingTestResponses from '@/store/qualifyingTest/qualifyingTestRespon import connectionMonitor from '@/store/connectionMonitor'; import qualifyingTestReports from '@/store/qualifyingTestReports/collection'; import qualifyingTestReport from '@/store/qualifyingTestReports/document'; + +import tasks from '@/store/tasks/collection'; +import task from '@/store/tasks/document'; import panels from '@/store/panels'; +// NEW: import panels from '@/store/panels/collection'; +import panel from '@/store/panels/document'; +import panellists from '@/store/panellists/collection'; +import panellist from '@/store/panellists/document'; +import checks from '@/store/checks/collection'; + +import exerciseDiversity from '@/store/exercise/diversity/document'; const store = new Vuex.Store({ // Don't use strict mode in production for performance reasons (https://vuex.vuejs.org/guide/strict.html) strict: process.env.NODE_ENV !== 'production', modules: { + ui, auth, services, exerciseCollection, @@ -65,8 +77,15 @@ const store = new Vuex.Store({ connectionMonitor, qualifyingTestReports, qualifyingTestReport, + tasks, + task, panels, + panel, + panellists, + panellist, + checks, characterChecks, + exerciseDiversity, }, state: { packageVersion: process.env.PACKAGE_VERSION || '0', diff --git a/src/store/checks/collection.js b/src/store/checks/collection.js new file mode 100644 index 000000000..8b6aa9eb7 --- /dev/null +++ b/src/store/checks/collection.js @@ -0,0 +1,36 @@ +export default { + namespaced: true, + actions: { + bind: ({ commit }, params) => { + commit('setLoaded', true); + commit('setParams', params); + }, + unbind: ({ commit }) => { + commit('setLoaded', false); + commit('setParams', {}); + }, + create: async ({ commit }, { data }) => { + commit('addRecord', data); + }, + }, + mutations: { + setLoaded(state, value) { + state.loaded = value; + }, + setParams(state, params) { + state.params = params; + }, + addRecord(state, record) { + state.records.push(record); + }, + }, + state: { + records: [ + { id: 1, type: '100%', created: Date.now(), createdBy: 'Jane Smith', status: 'pass', note: 'All present and correct' }, + { id: 2, type: '10%', created: Date.now(), createdBy: 'John Johnson', status: 'fail', note: 'Not sure about M Howley :)' }, + { id: 3, type: 'Custom', created: Date.now(), createdBy: 'Matt Howley', status: null, note: '' }, + ], + params: {}, + loaded: false, + }, +}; diff --git a/src/store/exercise/diversity/document.js b/src/store/exercise/diversity/document.js new file mode 100644 index 000000000..4291afd69 --- /dev/null +++ b/src/store/exercise/diversity/document.js @@ -0,0 +1,19 @@ +import { firestore } from '@/firebase'; +import { firestoreAction } from 'vuexfire'; +import vuexfireSerialize from '@jac-uk/jac-kit/helpers/vuexfireSerialize'; + +export default { + namespaced: true, + actions: { + bind: firestoreAction(({ bindFirestoreRef }, exerciseId) => { + const firestoreRef = firestore.doc(`exercises/${exerciseId}/data/diversity`); + return bindFirestoreRef('record', firestoreRef, { serialize: vuexfireSerialize }); + }), + unbind: firestoreAction(({ unbindFirestoreRef }) => { + return unbindFirestoreRef('record'); + }), + }, + state: { + record: null, + }, +}; diff --git a/src/store/panellists/collection.js b/src/store/panellists/collection.js new file mode 100644 index 000000000..3efda948f --- /dev/null +++ b/src/store/panellists/collection.js @@ -0,0 +1,26 @@ +// eslint-disable-next-line +import firebase from '@firebase/app'; +import { firestore } from '@/firebase'; +import { firestoreAction } from 'vuexfire'; +import vuexfireSerialize from '@jac-uk/jac-kit/helpers/vuexfireSerialize'; +import tableQuery from '@jac-uk/jac-kit/components/Table/tableQuery'; + +const collectionRef = firestore.collection('panellists'); + +export default { + namespaced: true, + actions: { + bind: firestoreAction(({ bindFirestoreRef, state }, params) => { + let firestoreRef = collectionRef; + firestoreRef = tableQuery(state.records, firestoreRef, params); + return bindFirestoreRef('records', firestoreRef, { serialize: vuexfireSerialize }); + }), + unbind: firestoreAction(({ unbindFirestoreRef }) => { + return unbindFirestoreRef('records'); + }), + }, + state: { + records: [], + }, + getters: { }, +}; diff --git a/src/store/panellists/document.js b/src/store/panellists/document.js new file mode 100644 index 000000000..fa527c36f --- /dev/null +++ b/src/store/panellists/document.js @@ -0,0 +1,47 @@ +import firebase from '@firebase/app'; +import { firestore } from '@/firebase'; +import { firestoreAction } from 'vuexfire'; +import vuexfireSerialize from '@jac-uk/jac-kit/helpers/vuexfireSerialize'; +import clone from 'clone'; + +const collection = firestore.collection('panellists'); + +export default { + namespaced: true, + actions: { + bind: firestoreAction(({ bindFirestoreRef }, id) => { + const firestoreRef = collection.doc(id); + return bindFirestoreRef('record', firestoreRef, { serialize: vuexfireSerialize }); + }), + unbind: firestoreAction(({ unbindFirestoreRef }) => { + return unbindFirestoreRef('record'); + }), + create: async (context, data) => { + const ref = collection.doc(); + data.created = firebase.firestore.FieldValue.serverTimestamp(); + data.lastUpdated = firebase.firestore.FieldValue.serverTimestamp(); + await ref.set(data, { merge: true }); + return ref.id; + }, + update: async (context, { data, id }) => { + const ref = collection.doc(id); + data.lastUpdated = firebase.firestore.FieldValue.serverTimestamp(); + await ref.set(data, { merge: true }); + }, + delete: async (context, id) => { + await collection.doc(id).delete(); + }, + }, + state: { + record: null, + }, + getters: { + id: (state) => { + if (state.record === null) return null; + return state.record.id; + }, + data: (state) => () => { + return clone(state.record); + }, + }, +}; diff --git a/src/store/panels/collection.js b/src/store/panels/collection.js new file mode 100644 index 000000000..c9098d3a4 --- /dev/null +++ b/src/store/panels/collection.js @@ -0,0 +1,48 @@ +// eslint-disable-next-line +import firebase from '@firebase/app'; +import { firestore } from '@/firebase'; +import { firestoreAction } from 'vuexfire'; +import vuexfireSerialize from '@jac-uk/jac-kit/helpers/vuexfireSerialize'; +import tableQuery from '@jac-uk/jac-kit/components/Table/tableQuery'; + +const collectionRef = firestore.collection('panels'); + +export default { + namespaced: true, + actions: { + bind: firestoreAction(({ bindFirestoreRef, state }, params) => { + let firestoreRef = collectionRef.where('exerciseId', '==', params.exerciseId); + if (params.type) { + firestoreRef = firestoreRef.where('type', '==', params.type); + } + firestoreRef = tableQuery(state.records, firestoreRef, params); + return bindFirestoreRef('records', firestoreRef, { serialize: vuexfireSerialize }); + }), + unbind: firestoreAction(({ unbindFirestoreRef }) => { + return unbindFirestoreRef('records'); + }), + bindApplicationsWithoutPanels: firestoreAction(({ bindFirestoreRef, state }, params) => { + console.log('params', params); + let firestoreRef = firestore.collection('applicationRecords') + .where('exercise.id', '==', params.exerciseId) + .where('active', '==', true) + .where(`${params.type}.panelId`, '==', null); + if (params.hasOwnProperty('stage') && params.stage) { + firestoreRef = firestoreRef.where('stage', '==', params.stage); + } + if (params.hasOwnProperty('status') && params.status) { + firestoreRef = firestoreRef.where('status', '==', params.status); + } + firestoreRef = tableQuery(state.applicationsWithoutPanels, firestoreRef, params); + return bindFirestoreRef('applicationsWithoutPanels', firestoreRef, { serialize: vuexfireSerialize }); + }), + unbindApplicationsWithoutPanels: firestoreAction(({ unbindFirestoreRef }) => { + return unbindFirestoreRef('applicationsWithoutPanels'); + }), + }, + state: { + records: [], + applicationsWithoutPanels: [], + }, + getters: { }, +}; diff --git a/src/store/panels/document.js b/src/store/panels/document.js new file mode 100644 index 000000000..d22bb7d86 --- /dev/null +++ b/src/store/panels/document.js @@ -0,0 +1,101 @@ +import firebase from '@firebase/app'; +import { firestore } from '@/firebase'; +import { firestoreAction } from 'vuexfire'; +import vuexfireSerialize from '@jac-uk/jac-kit/helpers/vuexfireSerialize'; +import tableQuery from '@jac-uk/jac-kit/components/Table/tableQuery'; +import clone from 'clone'; + +const collection = firestore.collection('panels'); + +export default { + namespaced: true, + actions: { + bind: firestoreAction(({ bindFirestoreRef }, id) => { + const firestoreRef = collection.doc(id); + return bindFirestoreRef('record', firestoreRef, { serialize: vuexfireSerialize }); + }), + unbind: firestoreAction(({ unbindFirestoreRef }) => { + return unbindFirestoreRef('record'); + }), + bindApplications: firestoreAction(({ bindFirestoreRef, state }, params) => { + let firestoreRef = firestore.collection('applicationRecords') + .where('exercise.id', '==', params.exerciseId) + .where(`${params.type}.panelId`, '==', params.panelId); + firestoreRef = tableQuery(state.applications, firestoreRef, params); + return bindFirestoreRef('applications', firestoreRef, { serialize: vuexfireSerialize }); + }), + unbindApplications: firestoreAction(({ unbindFirestoreRef }) => { + return unbindFirestoreRef('applications'); + }), + bindPanellists: firestoreAction(({ bindFirestoreRef }, params) => { + let firestoreRef = firestore.collection('panellists'); + if (params && params.ids) { + firestoreRef = firestoreRef.where(firebase.firestore.FieldPath.documentId(), 'in', params.ids); + } + return bindFirestoreRef('panellists', firestoreRef, { serialize: vuexfireSerialize }); + }), + unbindPanellists: firestoreAction(({ unbindFirestoreRef }) => { + return unbindFirestoreRef('panellists'); + }), + create: async (context, data) => { + const ref = collection.doc(); + data.created = firebase.firestore.FieldValue.serverTimestamp(); + data.lastUpdated = firebase.firestore.FieldValue.serverTimestamp(); + await ref.set(data, { merge: true }); + }, + update: async (context, { data, id }) => { + const ref = collection.doc(id); + data.lastUpdated = firebase.firestore.FieldValue.serverTimestamp(); + await ref.update(data); + }, + delete: async (context, id) => { + await collection.doc(id).delete(); + }, + addApplications: async (context, { panelId, type, applicationIds }) => { + const batch = firestore.batch(); + applicationIds.forEach(applicationId => { + const ref = firestore.collection('applicationRecords').doc(applicationId); + const data = {}; + data[`${type}.panelId`] = panelId; + batch.update(ref, data); + }); + await batch.commit(); + await context.dispatch('update', { + id: panelId, + data: { + applicationIds: firebase.firestore.FieldValue.arrayUnion(...applicationIds), + }, + }); + }, + removeApplications: async (context, { applicationIds }) => { + const batch = firestore.batch(); + applicationIds.forEach(applicationId => { + const ref = firestore.collection('applicationRecords').doc(applicationId); + const data = {}; + data[`${context.state.record.type}.panelId`] = null; + batch.update(ref, data); + }); + await batch.commit(); + await context.dispatch('update', { + id: context.state.record.id, + data: { + applicationIds: firebase.firestore.FieldValue.arrayRemove(...applicationIds), + }, + }); + }, + }, + state: { + record: null, + applications: [], + panellists: [], + }, + getters: { + id: (state) => { + if (state.record === null) return null; + return state.record.id; + }, + data: (state) => () => { + return clone(state.record); + }, + }, +}; diff --git a/src/store/tasks/collection.js b/src/store/tasks/collection.js new file mode 100644 index 000000000..31b08d361 --- /dev/null +++ b/src/store/tasks/collection.js @@ -0,0 +1,40 @@ +import { firestore } from '@/firebase'; +import { firestoreAction } from 'vuexfire'; +import vuexfireSerialize from '@jac-uk/jac-kit/helpers/vuexfireSerialize'; + +export default { + namespaced: true, + actions: { + bind: firestoreAction(({ bindFirestoreRef, commit }, params) => { + commit('setLoaded', true); + commit('setParams', params); + const firestoreRef = firestore.collection(`exercises/${params.exerciseId}/tasks`); + return bindFirestoreRef('records', firestoreRef, { serialize: vuexfireSerialize }); + }), + unbind: firestoreAction(({ unbindFirestoreRef, commit }) => { + commit('setLoaded', false); + commit('setParams', {}); + return unbindFirestoreRef('records'); + }), + }, + mutations: { + setLoaded(state, value) { + state.loaded = value; + }, + setParams(state, params) { + state.params = params; + }, + }, + getters: { + getTask: (state) => (type) => { + if (state.records.length === 0) return null; + const result = state.records.filter(task => task.id === type); + return result[0]; + }, + }, + state: { + records: [], + params: {}, + loaded: false, + }, +}; diff --git a/src/store/tasks/document.js b/src/store/tasks/document.js new file mode 100644 index 000000000..4a126d03c --- /dev/null +++ b/src/store/tasks/document.js @@ -0,0 +1,27 @@ +import firebase from '@firebase/app'; +import { firestore } from '@/firebase'; +import { firestoreAction } from 'vuexfire'; +import vuexfireSerialize from '@jac-uk/jac-kit/helpers/vuexfireSerialize'; + +// TODO delete this. it is not being used! + +export default { + namespaced: true, + actions: { + bind: firestoreAction(({ bindFirestoreRef }, { exerciseId, type }) => { + const firestoreRef = firestore.doc(`exercises/${exerciseId}/tasks/${type}`); + return bindFirestoreRef('record', firestoreRef, { serialize: vuexfireSerialize }); + }), + unbind: firestoreAction(({ unbindFirestoreRef }) => { + return unbindFirestoreRef('record'); + }), + update: async (context, { exerciseId, type, data }) => { + const ref = firestore.doc(`exercises/${exerciseId}/tasks/${type}`); + data.lastUpdated = firebase.firestore.FieldValue.serverTimestamp(); + await ref.update(data); + }, + }, + state: { + record: null, + }, +}; diff --git a/src/store/ui.js b/src/store/ui.js new file mode 100644 index 000000000..a17a55d79 --- /dev/null +++ b/src/store/ui.js @@ -0,0 +1,21 @@ +const module = { + namespaced: true, + state: { + fullScreen: false, + }, + mutations: { + setFullScreen(state, value) { + state.fullScreen = value; + }, + }, + actions: { + enterFullScreen({ commit }) { + commit('setFullScreen', true); + }, + exitFullScreen({ commit }) { + commit('setFullScreen', false); + }, + }, +}; + +export default module; diff --git a/src/views/Exercise/Tasks/Task.vue b/src/views/Exercise/Tasks/Task.vue new file mode 100644 index 000000000..ba997cb07 --- /dev/null +++ b/src/views/Exercise/Tasks/Task.vue @@ -0,0 +1,19 @@ + + + diff --git a/src/views/Exercise/Tasks/Task/Completed.vue b/src/views/Exercise/Tasks/Task/Completed.vue new file mode 100644 index 000000000..b882b35cf --- /dev/null +++ b/src/views/Exercise/Tasks/Task/Completed.vue @@ -0,0 +1,69 @@ + + + diff --git a/src/views/Exercise/Tasks/Task/Data/Activated.vue b/src/views/Exercise/Tasks/Task/Data/Activated.vue new file mode 100644 index 000000000..5ed7c047b --- /dev/null +++ b/src/views/Exercise/Tasks/Task/Data/Activated.vue @@ -0,0 +1,3 @@ + diff --git a/src/views/Exercise/Tasks/Task/Data/Initialised.vue b/src/views/Exercise/Tasks/Task/Data/Initialised.vue new file mode 100644 index 000000000..5ed7c047b --- /dev/null +++ b/src/views/Exercise/Tasks/Task/Data/Initialised.vue @@ -0,0 +1,3 @@ + diff --git a/src/views/Exercise/Tasks/Task/Finalised.old.vue b/src/views/Exercise/Tasks/Task/Finalised.old.vue new file mode 100644 index 000000000..39c7b4191 --- /dev/null +++ b/src/views/Exercise/Tasks/Task/Finalised.old.vue @@ -0,0 +1,367 @@ + + + diff --git a/src/views/Exercise/Tasks/Task/Finalised.vue b/src/views/Exercise/Tasks/Task/Finalised.vue new file mode 100644 index 000000000..61c7ba0cb --- /dev/null +++ b/src/views/Exercise/Tasks/Task/Finalised.vue @@ -0,0 +1,195 @@ + + + diff --git a/src/views/Exercise/Tasks/Task/Finalised/List.vue b/src/views/Exercise/Tasks/Task/Finalised/List.vue new file mode 100644 index 000000000..3a2668ea7 --- /dev/null +++ b/src/views/Exercise/Tasks/Task/Finalised/List.vue @@ -0,0 +1,93 @@ + + + diff --git a/src/views/Exercise/Tasks/Task/Finalised/SetPassMark.vue b/src/views/Exercise/Tasks/Task/Finalised/SetPassMark.vue new file mode 100644 index 000000000..58f1d2e8e --- /dev/null +++ b/src/views/Exercise/Tasks/Task/Finalised/SetPassMark.vue @@ -0,0 +1,48 @@ + + + diff --git a/src/views/Exercise/Tasks/Task/Finalised/View.vue b/src/views/Exercise/Tasks/Task/Finalised/View.vue new file mode 100644 index 000000000..e25e56dfa --- /dev/null +++ b/src/views/Exercise/Tasks/Task/Finalised/View.vue @@ -0,0 +1,377 @@ + + + + + diff --git a/src/views/Exercise/Tasks/Task/Loading.vue b/src/views/Exercise/Tasks/Task/Loading.vue new file mode 100644 index 000000000..336548198 --- /dev/null +++ b/src/views/Exercise/Tasks/Task/Loading.vue @@ -0,0 +1,72 @@ + + + diff --git a/src/views/Exercise/Tasks/Task/ModerationActivated.vue b/src/views/Exercise/Tasks/Task/ModerationActivated.vue new file mode 100644 index 000000000..b1c45970a --- /dev/null +++ b/src/views/Exercise/Tasks/Task/ModerationActivated.vue @@ -0,0 +1,25 @@ + + + diff --git a/src/views/Exercise/Tasks/Task/ModerationInitialised.vue b/src/views/Exercise/Tasks/Task/ModerationInitialised.vue new file mode 100644 index 000000000..bff20de44 --- /dev/null +++ b/src/views/Exercise/Tasks/Task/ModerationInitialised.vue @@ -0,0 +1,25 @@ + + + diff --git a/src/views/Exercise/Tasks/Task/New.vue b/src/views/Exercise/Tasks/Task/New.vue new file mode 100644 index 000000000..5b0f57c8a --- /dev/null +++ b/src/views/Exercise/Tasks/Task/New.vue @@ -0,0 +1,54 @@ + + + diff --git a/src/views/Exercise/Tasks/Task/New/default.vue b/src/views/Exercise/Tasks/Task/New/default.vue new file mode 100644 index 000000000..9ccb1c609 --- /dev/null +++ b/src/views/Exercise/Tasks/Task/New/default.vue @@ -0,0 +1,111 @@ + + + diff --git a/src/views/Exercise/Tasks/Task/New/dummyData.js b/src/views/Exercise/Tasks/Task/New/dummyData.js new file mode 100644 index 000000000..769b398b4 --- /dev/null +++ b/src/views/Exercise/Tasks/Task/New/dummyData.js @@ -0,0 +1,422 @@ +/*eslint func-style: ["error", "declaration"]*/ + +export { + randomNumber, + randomArrayItem, + generateApplications +}; + +function randomNumber(min, max) { + return Math.floor(Math.random() * (max - min + 1) + min); +} + +function randomArrayItem(arr) { + return arr[randomNumber(1, arr.length) - 1]; +} + +function getMaleFirstName() { + return randomArrayItem(malefirstNames); +} + +function getFemaleFirstName() { + return randomArrayItem(femalefirstNames); +} + +function getLastName() { + return randomArrayItem(lastNames); +} + +function getMaleTitle() { + return randomArrayItem(maleTitles); +} + +function getFemaleTitle() { + return randomArrayItem(femaleTitles); +} + +function generateApplications(exerciseRef, quantity) { + const applications = []; + for (let i = 0, len = quantity; i < len; ++i) { + let firstName = ''; + let title = ''; + if (Math.random() < 0.5 ) { + firstName = getMaleFirstName(); + title = getMaleTitle(); + } else { + firstName = getFemaleFirstName(); + title = getFemaleTitle(); + } + const lastName = getLastName(); + applications.push({ + id: `${i}`, + referenceNumber: getApplicationReferenceNumber(exerciseRef, i + 1), + personalDetails: { + title, + firstName, + lastName, + fullName: `${firstName} ${lastName}`, + email: getEmail(firstName, lastName), + dateOfBirth: '1976-02-08', + phone: '012345 678910', + nationalInsuranceNumber: 'QQ 12 34 56 C', + citizenship: 'uk', + reasonableAdjustments: false, + }, + userId: `${i}`, + }); + } + return applications; +} + +function getEmail(firstName, lastName, domain) { + if (domain) { + return `${firstName.toLowerCase()}.${lastName.toLowerCase()}@${domain}`; + } else { + return `${firstName.toLowerCase()}.${lastName.toLowerCase()}@sharklasers.com`; + } +} + +function getApplicationReferenceNumber(exerciseReferenceNumber, index) { + const characters = 'abcdefghijklmnopqrstuvwxyz'; + let randomCharacters = ''; + for (let i = 0, len = 3; i < len; i++) { + randomCharacters += characters.charAt(Math.floor(Math.random() * characters.length)); + } + return `${exerciseReferenceNumber}-${ randomCharacters }${(10000 + index).toString().substr(1)}`; +} + +const maleTitles = [ + 'Mr', + 'Dr', + 'Rt. Hon.', +]; + +const femaleTitles = [ + 'Mrs', + 'Ms', + 'Miss', + 'Dr', + 'Rt. Hon.', +]; + +const femalefirstNames = [ + 'Abigail', + 'Alexandra', + 'Alison', + 'Amanda', + 'Amelia', + 'Amy', + 'Andrea', + 'Angela', + 'Anna', + 'Anne', + 'Audrey', + 'Ava', + 'Bella', + 'Bernadette', + 'Carol', + 'Caroline', + 'Carolyn', + 'Chloe', + 'Claire', + 'Deirdre', + 'Diana', + 'Diane', + 'Donna', + 'Dorothy', + 'Elizabeth', + 'Ella', + 'Emily', + 'Emma', + 'Faith', + 'Felicity', + 'Fiona', + 'Gabrielle', + 'Grace', + 'Hannah', + 'Heather', + 'Irene', + 'Jan', + 'Jane', + 'Jasmine', + 'Jennifer', + 'Jessica', + 'Joan', + 'Joanne', + 'Julia', + 'Karen', + 'Katherine', + 'Kimberly', + 'Kylie', + 'Lauren', + 'Leah', + 'Lillian', + 'Lily', + 'Lisa', + 'Madeleine', + 'Maria', + 'Mary', + 'Megan', + 'Melanie', + 'Michelle', + 'Molly', + 'Natalie', + 'Nicola', + 'Olivia', + 'Penelope', + 'Pippa', + 'Rachel', + 'Rebecca', + 'Rose', + 'Ruth', + 'Sally', + 'Samantha', + 'Sarah', + 'Sonia', + 'Sophie', + 'Stephanie', + 'Sue', + 'Theresa', + 'Tracey', + 'Una', + 'Vanessa', + 'Victoria', + 'Virginia', + 'Wanda', + 'Wendy', + 'Yvonne', + 'Zoe', +]; + +const malefirstNames = [ + 'Adam', + 'Adrian', + 'Alan', + 'Alexander', + 'Andrew', + 'Anthony', + 'Austin', + 'Benjamin', + 'Blake', + 'Boris', + 'Brandon', + 'Brian', + 'Cameron', + 'Carl', + 'Charles', + 'Christian', + 'Christopher', + 'Colin', + 'Connor', + 'Dan', + 'David', + 'Dominic', + 'Dylan', + 'Edward', + 'Eric', + 'Evan', + 'Frank', + 'Gavin', + 'Gordon', + 'Harry', + 'Ian', + 'Isaac', + 'Jack', + 'Jacob', + 'Jake', + 'James', + 'Jason', + 'Joe', + 'John', + 'Jonathan', + 'Joseph', + 'Joshua', + 'Julian', + 'Justin', + 'Keith', + 'Kevin', + 'Leonard', + 'Liam', + 'Lucas', + 'Luke', + 'Matt', + 'Max', + 'Michael', + 'Nathan', + 'Neil', + 'Nicholas', + 'Oliver', + 'Owen', + 'Paul', + 'Peter', + 'Phil', + 'Piers', + 'Richard', + 'Robert', + 'Ryan', + 'Sam', + 'Sean', + 'Sebastian', + 'Simon', + 'Stephen', + 'Steven', + 'Stewart', + 'Thomas', + 'Tim', + 'Trevor', + 'Victor', + 'Warren', + 'William', +]; + +const lastNames = [ + 'Abraham', + 'Allan', + 'Alsop', + 'Anderson', + 'Arnold', + 'Avery', + 'Bailey', + 'Baker', + 'Ball', + 'Bell', + 'Berry', + 'Black', + 'Blake', + 'Bond', + 'Bower', + 'Brown', + 'Buckland', + 'Burgess', + 'Butler', + 'Cameron', + 'Campbell', + 'Carr', + 'Chapman', + 'Churchill', + 'Clark', + 'Clarkson', + 'Coleman', + 'Cornish', + 'Davidson', + 'Davies', + 'Dickens', + 'Dowd', + 'Duncan', + 'Dyer', + 'Edmunds', + 'Ellison', + 'Ferguson', + 'Fisher', + 'Forsyth', + 'Fraser', + 'Gibson', + 'Gill', + 'Glover', + 'Graham', + 'Grant', + 'Gray', + 'Greene', + 'Hamilton', + 'Hardacre', + 'Harris', + 'Hart', + 'Hemmings', + 'Henderson', + 'Hill', + 'Hodges', + 'Howard', + 'Hudson', + 'Hughes', + 'Hunter', + 'Ince', + 'Jackson', + 'James', + 'Johnston', + 'Jones', + 'Kelly', + 'Kerr', + 'King', + 'Knox', + 'Lambert', + 'Langdon', + 'Lawrence', + 'Lee', + 'Lewis', + 'Lyman', + 'MacDonald', + 'Mackay', + 'Mackenzie', + 'MacLeod', + 'Manning', + 'Marshall', + 'Martin', + 'Mathis', + 'May', + 'McDonald', + 'McLean', + 'McGrath', + 'Metcalfe', + 'Miller', + 'Mills', + 'Mitchell', + 'Morgan', + 'Morrison', + 'Murray', + 'Nash', + 'Newman', + 'Nolan', + 'North', + 'Ogden', + 'Oliver', + 'Paige', + 'Parr', + 'Parsons', + 'Paterson', + 'Payne', + 'Peake', + 'Peters', + 'Piper', + 'Poole', + 'Powell', + 'Pullman', + 'Quinn', + 'Rampling', + 'Randall', + 'Rees', + 'Reid', + 'Roberts', + 'Robertson', + 'Ross', + 'Russell', + 'Rutherford', + 'Sanderson', + 'Scott', + 'Sharp', + 'Short', + 'Simpson', + 'Skinner', + 'Slater', + 'Smith', + 'Springer', + 'Stewart', + 'Sutherland', + 'Taylor', + 'Terry', + 'Thomson', + 'Tucker', + 'Turner', + 'Underwood', + 'Vance', + 'Vaughan', + 'Walker', + 'Wallace', + 'Walsh', + 'Watson', + 'Welch', + 'White', + 'Wilkins', + 'Wilson', + 'Wright', + 'Young', +]; diff --git a/src/views/Exercise/Tasks/Task/New/qualifyingTest.vue b/src/views/Exercise/Tasks/Task/New/qualifyingTest.vue new file mode 100644 index 000000000..d0442f660 --- /dev/null +++ b/src/views/Exercise/Tasks/Task/New/qualifyingTest.vue @@ -0,0 +1,19 @@ + + + diff --git a/src/views/Exercise/Tasks/Task/New/stageOutcome.vue b/src/views/Exercise/Tasks/Task/New/stageOutcome.vue new file mode 100644 index 000000000..e056d6a21 --- /dev/null +++ b/src/views/Exercise/Tasks/Task/New/stageOutcome.vue @@ -0,0 +1,573 @@ + + + + + diff --git a/src/views/Exercise/Tasks/Task/Panel/Constants.js b/src/views/Exercise/Tasks/Task/Panel/Constants.js new file mode 100644 index 000000000..c319e4744 --- /dev/null +++ b/src/views/Exercise/Tasks/Task/Panel/Constants.js @@ -0,0 +1,27 @@ +const ROLES = { + CHAIR: 'chair', + INDEPENDENT: 'independent', + JUDICIAL: 'judicial', + OTHER: 'other', +}; + +const PANEL_TYPES = { + SIFT: 'sift', + SELECTION: 'selection', + SCENARIO: 'scenario', +}; + +const PANEL_STATUS = { + // TODO include all statuses + DRAFT: 'draft', + APPROVED: 'approved', + PROCESSING: 'processing', + CREATED: 'created', + SUBMITTED: 'submitted', +}; + +export { + ROLES, + PANEL_TYPES, + PANEL_STATUS +}; diff --git a/src/views/Exercise/Tasks/Task/Panel/New.vue b/src/views/Exercise/Tasks/Task/Panel/New.vue new file mode 100644 index 000000000..cee0dd288 --- /dev/null +++ b/src/views/Exercise/Tasks/Task/Panel/New.vue @@ -0,0 +1,84 @@ + + + diff --git a/src/views/Exercise/Tasks/Task/Panel/Panellists/Edit.vue b/src/views/Exercise/Tasks/Task/Panel/Panellists/Edit.vue new file mode 100644 index 000000000..babe0b16b --- /dev/null +++ b/src/views/Exercise/Tasks/Task/Panel/Panellists/Edit.vue @@ -0,0 +1,118 @@ + + + + + diff --git a/src/views/Exercise/Tasks/Task/Panel/Panellists/View.vue b/src/views/Exercise/Tasks/Task/Panel/Panellists/View.vue new file mode 100644 index 000000000..536f82046 --- /dev/null +++ b/src/views/Exercise/Tasks/Task/Panel/Panellists/View.vue @@ -0,0 +1,72 @@ + + + + + diff --git a/src/views/Exercise/Tasks/Task/Panel/View.vue b/src/views/Exercise/Tasks/Task/Panel/View.vue new file mode 100644 index 000000000..6089204ec --- /dev/null +++ b/src/views/Exercise/Tasks/Task/Panel/View.vue @@ -0,0 +1,519 @@ + + + + + diff --git a/src/views/Exercise/Tasks/Task/Panel/components/AddEdit.vue b/src/views/Exercise/Tasks/Task/Panel/components/AddEdit.vue new file mode 100644 index 000000000..f9a868d1b --- /dev/null +++ b/src/views/Exercise/Tasks/Task/Panel/components/AddEdit.vue @@ -0,0 +1,50 @@ + + + diff --git a/src/views/Exercise/Tasks/Task/Panel/components/SelectPanel.vue b/src/views/Exercise/Tasks/Task/Panel/components/SelectPanel.vue new file mode 100644 index 000000000..919c3ea80 --- /dev/null +++ b/src/views/Exercise/Tasks/Task/Panel/components/SelectPanel.vue @@ -0,0 +1,48 @@ + + + diff --git a/src/views/Exercise/Tasks/Task/Panel/components/View.vue b/src/views/Exercise/Tasks/Task/Panel/components/View.vue new file mode 100644 index 000000000..69a22c606 --- /dev/null +++ b/src/views/Exercise/Tasks/Task/Panel/components/View.vue @@ -0,0 +1,41 @@ + + + diff --git a/src/views/Exercise/Tasks/Task/PanelsActivated.vue b/src/views/Exercise/Tasks/Task/PanelsActivated.vue new file mode 100644 index 000000000..228f99f59 --- /dev/null +++ b/src/views/Exercise/Tasks/Task/PanelsActivated.vue @@ -0,0 +1,596 @@ + + + diff --git a/src/views/Exercise/Tasks/Task/PanelsInitialised.vue b/src/views/Exercise/Tasks/Task/PanelsInitialised.vue new file mode 100644 index 000000000..31283266e --- /dev/null +++ b/src/views/Exercise/Tasks/Task/PanelsInitialised.vue @@ -0,0 +1,356 @@ + + + diff --git a/src/views/Exercise/Tasks/Task/StatusChanges.vue b/src/views/Exercise/Tasks/Task/StatusChanges.vue new file mode 100644 index 000000000..09abf9715 --- /dev/null +++ b/src/views/Exercise/Tasks/Task/StatusChanges.vue @@ -0,0 +1,238 @@ + + + diff --git a/src/views/Exercise/Tasks/Task/StatusChanges/SetStatus.vue b/src/views/Exercise/Tasks/Task/StatusChanges/SetStatus.vue new file mode 100644 index 000000000..f4c7b715a --- /dev/null +++ b/src/views/Exercise/Tasks/Task/StatusChanges/SetStatus.vue @@ -0,0 +1,51 @@ + + + diff --git a/src/views/Exercise/Tasks/Task/StatusChanges/StatusTag.vue b/src/views/Exercise/Tasks/Task/StatusChanges/StatusTag.vue new file mode 100644 index 000000000..1a63167b4 --- /dev/null +++ b/src/views/Exercise/Tasks/Task/StatusChanges/StatusTag.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/src/views/Exercise/Tasks/Task/TestActivated.vue b/src/views/Exercise/Tasks/Task/TestActivated.vue new file mode 100644 index 000000000..f96c95885 --- /dev/null +++ b/src/views/Exercise/Tasks/Task/TestActivated.vue @@ -0,0 +1,99 @@ + + + diff --git a/src/views/Exercise/Tasks/Task/TestInitialised.vue b/src/views/Exercise/Tasks/Task/TestInitialised.vue new file mode 100644 index 000000000..23398c030 --- /dev/null +++ b/src/views/Exercise/Tasks/Task/TestInitialised.vue @@ -0,0 +1,82 @@ + + + diff --git a/src/views/Exercise/Tasks/Task/helper.js b/src/views/Exercise/Tasks/Task/helper.js new file mode 100644 index 000000000..03a8fb308 --- /dev/null +++ b/src/views/Exercise/Tasks/Task/helper.js @@ -0,0 +1,37 @@ +/*eslint func-style: ["error", "declaration"]*/ +import store from '@/store.js'; + +export { + beforeRouteEnter, + btnNext, + getExpectedRouteName +}; + +function beforeRouteEnter(to, from, next) { + const expectedRouteName = getExpectedRouteName(to.params.type); + if (to.name === expectedRouteName) { + next(); + } else { + next({ + name: expectedRouteName, + params: to.params, + }); + } +} + +async function btnNext() { + this.$router.push({ + name: 'exercise-task-loading', + params: this.$route.params, + }); +} + +function getExpectedRouteName(type) { + if (!store.state.tasks.loaded) return 'exercise-task-loading'; + const task = store.getters['tasks/getTask'](type); + if (task && task.status) { + return `exercise-task-${task.status}`; + } else { + return 'exercise-task-new'; + } +} diff --git a/src/views/Panellists/List.vue b/src/views/Panellists/List.vue new file mode 100644 index 000000000..bed77f034 --- /dev/null +++ b/src/views/Panellists/List.vue @@ -0,0 +1,98 @@ + + + diff --git a/src/views/Panellists/Panellist.vue b/src/views/Panellists/Panellist.vue new file mode 100644 index 000000000..bf28e3a96 --- /dev/null +++ b/src/views/Panellists/Panellist.vue @@ -0,0 +1,119 @@ + + + diff --git a/src/views/Panellists/components/AddEdit.vue b/src/views/Panellists/components/AddEdit.vue new file mode 100644 index 000000000..acbc52eff --- /dev/null +++ b/src/views/Panellists/components/AddEdit.vue @@ -0,0 +1,52 @@ + + + diff --git a/src/views/Panellists/components/View.vue b/src/views/Panellists/components/View.vue new file mode 100644 index 000000000..e8d40da36 --- /dev/null +++ b/src/views/Panellists/components/View.vue @@ -0,0 +1,41 @@ + + + From 436113de5a7b0f92c8fa8329a7d48c2cb03d5e25 Mon Sep 17 00:00:00 2001 From: warrensearle Date: Tue, 27 Sep 2022 18:09:05 +0100 Subject: [PATCH 02/25] Remove unused code --- .../Exercise/Tasks/Task/Finalised.old.vue | 367 ----------- src/views/Exercise/Tasks/Task/New.vue | 23 +- .../Exercise/Tasks/Task/New/dummyData.js | 422 ------------- .../Exercise/Tasks/Task/New/stageOutcome.vue | 573 ------------------ 4 files changed, 9 insertions(+), 1376 deletions(-) delete mode 100644 src/views/Exercise/Tasks/Task/Finalised.old.vue delete mode 100644 src/views/Exercise/Tasks/Task/New/dummyData.js delete mode 100644 src/views/Exercise/Tasks/Task/New/stageOutcome.vue diff --git a/src/views/Exercise/Tasks/Task/Finalised.old.vue b/src/views/Exercise/Tasks/Task/Finalised.old.vue deleted file mode 100644 index 39c7b4191..000000000 --- a/src/views/Exercise/Tasks/Task/Finalised.old.vue +++ /dev/null @@ -1,367 +0,0 @@ - - - diff --git a/src/views/Exercise/Tasks/Task/New.vue b/src/views/Exercise/Tasks/Task/New.vue index 5b0f57c8a..385470cde 100644 --- a/src/views/Exercise/Tasks/Task/New.vue +++ b/src/views/Exercise/Tasks/Task/New.vue @@ -13,13 +13,11 @@ import { beforeRouteEnter, getExpectedRouteName } from './helper'; import { TASK_TYPE } from '@/helpers/constants'; import defaultView from './New/default'; import qualifyingTest from './New/qualifyingTest'; -import stageOutcome from './New/stageOutcome'; export default { components: { defaultView, qualifyingTest, - stageOutcome, }, beforeRouteEnter: beforeRouteEnter, props: { @@ -28,21 +26,9 @@ export default { type: String, }, }, - created() { - const task = this.$store.getters['tasks/getTask'](this.type); - if (task && task.status) { - this.$router.replace({ - name: getExpectedRouteName(this.type), - params: this.$route.params, - }); - } - }, computed: { newView() { switch (this.type) { - case TASK_TYPE.SHORTLISTING_OUTCOME: - case TASK_TYPE.SELECTION_OUTCOME: - return 'stageOutcome'; case TASK_TYPE.QUALIFYING_TEST: return 'qualifyingTest'; default: @@ -50,5 +36,14 @@ export default { } }, }, + created() { + const task = this.$store.getters['tasks/getTask'](this.type); + if (task && task.status) { + this.$router.replace({ + name: getExpectedRouteName(this.type), + params: this.$route.params, + }); + } + }, }; diff --git a/src/views/Exercise/Tasks/Task/New/dummyData.js b/src/views/Exercise/Tasks/Task/New/dummyData.js deleted file mode 100644 index 769b398b4..000000000 --- a/src/views/Exercise/Tasks/Task/New/dummyData.js +++ /dev/null @@ -1,422 +0,0 @@ -/*eslint func-style: ["error", "declaration"]*/ - -export { - randomNumber, - randomArrayItem, - generateApplications -}; - -function randomNumber(min, max) { - return Math.floor(Math.random() * (max - min + 1) + min); -} - -function randomArrayItem(arr) { - return arr[randomNumber(1, arr.length) - 1]; -} - -function getMaleFirstName() { - return randomArrayItem(malefirstNames); -} - -function getFemaleFirstName() { - return randomArrayItem(femalefirstNames); -} - -function getLastName() { - return randomArrayItem(lastNames); -} - -function getMaleTitle() { - return randomArrayItem(maleTitles); -} - -function getFemaleTitle() { - return randomArrayItem(femaleTitles); -} - -function generateApplications(exerciseRef, quantity) { - const applications = []; - for (let i = 0, len = quantity; i < len; ++i) { - let firstName = ''; - let title = ''; - if (Math.random() < 0.5 ) { - firstName = getMaleFirstName(); - title = getMaleTitle(); - } else { - firstName = getFemaleFirstName(); - title = getFemaleTitle(); - } - const lastName = getLastName(); - applications.push({ - id: `${i}`, - referenceNumber: getApplicationReferenceNumber(exerciseRef, i + 1), - personalDetails: { - title, - firstName, - lastName, - fullName: `${firstName} ${lastName}`, - email: getEmail(firstName, lastName), - dateOfBirth: '1976-02-08', - phone: '012345 678910', - nationalInsuranceNumber: 'QQ 12 34 56 C', - citizenship: 'uk', - reasonableAdjustments: false, - }, - userId: `${i}`, - }); - } - return applications; -} - -function getEmail(firstName, lastName, domain) { - if (domain) { - return `${firstName.toLowerCase()}.${lastName.toLowerCase()}@${domain}`; - } else { - return `${firstName.toLowerCase()}.${lastName.toLowerCase()}@sharklasers.com`; - } -} - -function getApplicationReferenceNumber(exerciseReferenceNumber, index) { - const characters = 'abcdefghijklmnopqrstuvwxyz'; - let randomCharacters = ''; - for (let i = 0, len = 3; i < len; i++) { - randomCharacters += characters.charAt(Math.floor(Math.random() * characters.length)); - } - return `${exerciseReferenceNumber}-${ randomCharacters }${(10000 + index).toString().substr(1)}`; -} - -const maleTitles = [ - 'Mr', - 'Dr', - 'Rt. Hon.', -]; - -const femaleTitles = [ - 'Mrs', - 'Ms', - 'Miss', - 'Dr', - 'Rt. Hon.', -]; - -const femalefirstNames = [ - 'Abigail', - 'Alexandra', - 'Alison', - 'Amanda', - 'Amelia', - 'Amy', - 'Andrea', - 'Angela', - 'Anna', - 'Anne', - 'Audrey', - 'Ava', - 'Bella', - 'Bernadette', - 'Carol', - 'Caroline', - 'Carolyn', - 'Chloe', - 'Claire', - 'Deirdre', - 'Diana', - 'Diane', - 'Donna', - 'Dorothy', - 'Elizabeth', - 'Ella', - 'Emily', - 'Emma', - 'Faith', - 'Felicity', - 'Fiona', - 'Gabrielle', - 'Grace', - 'Hannah', - 'Heather', - 'Irene', - 'Jan', - 'Jane', - 'Jasmine', - 'Jennifer', - 'Jessica', - 'Joan', - 'Joanne', - 'Julia', - 'Karen', - 'Katherine', - 'Kimberly', - 'Kylie', - 'Lauren', - 'Leah', - 'Lillian', - 'Lily', - 'Lisa', - 'Madeleine', - 'Maria', - 'Mary', - 'Megan', - 'Melanie', - 'Michelle', - 'Molly', - 'Natalie', - 'Nicola', - 'Olivia', - 'Penelope', - 'Pippa', - 'Rachel', - 'Rebecca', - 'Rose', - 'Ruth', - 'Sally', - 'Samantha', - 'Sarah', - 'Sonia', - 'Sophie', - 'Stephanie', - 'Sue', - 'Theresa', - 'Tracey', - 'Una', - 'Vanessa', - 'Victoria', - 'Virginia', - 'Wanda', - 'Wendy', - 'Yvonne', - 'Zoe', -]; - -const malefirstNames = [ - 'Adam', - 'Adrian', - 'Alan', - 'Alexander', - 'Andrew', - 'Anthony', - 'Austin', - 'Benjamin', - 'Blake', - 'Boris', - 'Brandon', - 'Brian', - 'Cameron', - 'Carl', - 'Charles', - 'Christian', - 'Christopher', - 'Colin', - 'Connor', - 'Dan', - 'David', - 'Dominic', - 'Dylan', - 'Edward', - 'Eric', - 'Evan', - 'Frank', - 'Gavin', - 'Gordon', - 'Harry', - 'Ian', - 'Isaac', - 'Jack', - 'Jacob', - 'Jake', - 'James', - 'Jason', - 'Joe', - 'John', - 'Jonathan', - 'Joseph', - 'Joshua', - 'Julian', - 'Justin', - 'Keith', - 'Kevin', - 'Leonard', - 'Liam', - 'Lucas', - 'Luke', - 'Matt', - 'Max', - 'Michael', - 'Nathan', - 'Neil', - 'Nicholas', - 'Oliver', - 'Owen', - 'Paul', - 'Peter', - 'Phil', - 'Piers', - 'Richard', - 'Robert', - 'Ryan', - 'Sam', - 'Sean', - 'Sebastian', - 'Simon', - 'Stephen', - 'Steven', - 'Stewart', - 'Thomas', - 'Tim', - 'Trevor', - 'Victor', - 'Warren', - 'William', -]; - -const lastNames = [ - 'Abraham', - 'Allan', - 'Alsop', - 'Anderson', - 'Arnold', - 'Avery', - 'Bailey', - 'Baker', - 'Ball', - 'Bell', - 'Berry', - 'Black', - 'Blake', - 'Bond', - 'Bower', - 'Brown', - 'Buckland', - 'Burgess', - 'Butler', - 'Cameron', - 'Campbell', - 'Carr', - 'Chapman', - 'Churchill', - 'Clark', - 'Clarkson', - 'Coleman', - 'Cornish', - 'Davidson', - 'Davies', - 'Dickens', - 'Dowd', - 'Duncan', - 'Dyer', - 'Edmunds', - 'Ellison', - 'Ferguson', - 'Fisher', - 'Forsyth', - 'Fraser', - 'Gibson', - 'Gill', - 'Glover', - 'Graham', - 'Grant', - 'Gray', - 'Greene', - 'Hamilton', - 'Hardacre', - 'Harris', - 'Hart', - 'Hemmings', - 'Henderson', - 'Hill', - 'Hodges', - 'Howard', - 'Hudson', - 'Hughes', - 'Hunter', - 'Ince', - 'Jackson', - 'James', - 'Johnston', - 'Jones', - 'Kelly', - 'Kerr', - 'King', - 'Knox', - 'Lambert', - 'Langdon', - 'Lawrence', - 'Lee', - 'Lewis', - 'Lyman', - 'MacDonald', - 'Mackay', - 'Mackenzie', - 'MacLeod', - 'Manning', - 'Marshall', - 'Martin', - 'Mathis', - 'May', - 'McDonald', - 'McLean', - 'McGrath', - 'Metcalfe', - 'Miller', - 'Mills', - 'Mitchell', - 'Morgan', - 'Morrison', - 'Murray', - 'Nash', - 'Newman', - 'Nolan', - 'North', - 'Ogden', - 'Oliver', - 'Paige', - 'Parr', - 'Parsons', - 'Paterson', - 'Payne', - 'Peake', - 'Peters', - 'Piper', - 'Poole', - 'Powell', - 'Pullman', - 'Quinn', - 'Rampling', - 'Randall', - 'Rees', - 'Reid', - 'Roberts', - 'Robertson', - 'Ross', - 'Russell', - 'Rutherford', - 'Sanderson', - 'Scott', - 'Sharp', - 'Short', - 'Simpson', - 'Skinner', - 'Slater', - 'Smith', - 'Springer', - 'Stewart', - 'Sutherland', - 'Taylor', - 'Terry', - 'Thomson', - 'Tucker', - 'Turner', - 'Underwood', - 'Vance', - 'Vaughan', - 'Walker', - 'Wallace', - 'Walsh', - 'Watson', - 'Welch', - 'White', - 'Wilkins', - 'Wilson', - 'Wright', - 'Young', -]; diff --git a/src/views/Exercise/Tasks/Task/New/stageOutcome.vue b/src/views/Exercise/Tasks/Task/New/stageOutcome.vue deleted file mode 100644 index e056d6a21..000000000 --- a/src/views/Exercise/Tasks/Task/New/stageOutcome.vue +++ /dev/null @@ -1,573 +0,0 @@ - - - - - From b8ffe20e39782834da13039f12703b32abdcac85 Mon Sep 17 00:00:00 2001 From: warrensearle Date: Thu, 13 Oct 2022 09:28:07 +0100 Subject: [PATCH 03/25] QT platform integration --- src/filters.js | 92 ++++++++++- src/helpersTMP/Timeline/exerciseTimeline.js | 4 + src/router.js | 6 + src/styles/main.scss | 4 + src/views/Exercise/Tasks.vue | 136 +++++++++------- src/views/Exercise/Tasks/Index.vue | 28 ++++ .../Exercise/Tasks/QualifyingTests/Cover.vue | 148 ++++++++++++------ .../Exercise/Tasks/Task/TestActivated.vue | 56 ++++--- .../Exercise/Tasks/Task/TestInitialised.vue | 52 +++--- 9 files changed, 364 insertions(+), 162 deletions(-) create mode 100644 src/views/Exercise/Tasks/Index.vue diff --git a/src/filters.js b/src/filters.js index 03067d747..40cee803e 100644 --- a/src/filters.js +++ b/src/filters.js @@ -1,5 +1,5 @@ import * as filters from '@jac-uk/jac-kit/filters/filters'; -import { ADVERT_TYPES } from '@/helpers/constants'; +import { ADVERT_TYPES, EXERCISE_STAGE, APPLICATION_STATUS, TASK_TYPE } from '@/helpers/constants'; const lookup = (value) => { let returnValue; @@ -29,7 +29,7 @@ const lookup = (value) => { selfAssessmentCompetencies: 'Self assessment with competencies', additionalInfo: 'Additional Information', - // exercise stages + // exercise states registration: 'Registration', shortlisting: 'Shortlisting', selection: 'Selection', @@ -41,6 +41,12 @@ const lookup = (value) => { shortlisted: 'Shortlisted', selected: 'Selected', recommended: 'Recommended', + passedCA: 'Passed CA', + failedCA: 'Failed CA', + passedSJ: 'Passed SJ', + failedSJ: 'Failed SJ', + passedScenario: 'Passed Scenario Test', + failedScenario: 'Failed Scenario Test', // Editable Application helpers date: 'Date', @@ -60,8 +66,34 @@ const lookup = (value) => { 'online-and-judge-led': 'Yes - online resources and judge-led discussion group course', 'online-only': 'Yes - online resources only', + // Panel types + 'sift': 'Sift', + 'scenario': 'Scenario', + + // Panel roles + 'chair': 'Chair', + 'independent': 'Independent', + 'judicial': 'Judicial', + + // Selection categories + 'leadership': 'Strategic Leadership Questions', + 'roleplay': 'Role Play', + 'situational': 'Situational Questions', + 'interview': 'Interview', + 'overall': 'Overall', + + // Capabilities + 'L': 'Leadership', + 'EJ': 'Exercising Judgement', + 'PBK': 'Possessing and Building Knowledge', + 'ACI': 'Assimilating and Clarifying Information', + 'WCO': 'Working and Communicating with Others', + 'MWE': 'Managing Work Efficiently', + 'OVERALL': 'Overall', + 'L&J': 'Legal & Judicial Skills', + 'PQ': 'Personal Qualities', + // 'xxx': 'xxx', - archived: 'Archived', }; lookup[ADVERT_TYPES.LISTING] = 'Listing'; @@ -69,6 +101,60 @@ const lookup = (value) => { lookup[ADVERT_TYPES.FULL] = 'Full'; lookup[ADVERT_TYPES.EXTERNAL] = 'External'; + lookup[EXERCISE_STAGE.APPLIED] = 'Applied'; + lookup[EXERCISE_STAGE.SHORTLISTED] = 'Shortlisted'; + lookup[EXERCISE_STAGE.SELECTABLE] = 'Selectable'; + lookup[EXERCISE_STAGE.RECOMMENDED] = 'Recommended'; + lookup[EXERCISE_STAGE.HANDOVER] = 'Handover'; + + lookup[APPLICATION_STATUS.PASSED_NOT_RECOMMENDED] = 'Passed but not recommended to SCC'; + lookup[APPLICATION_STATUS.PASSED_RECOMMENDED] = 'Passed and recommended to SCC'; + lookup[APPLICATION_STATUS.QUALIFYING_TEST_FAILED] = 'Failed qualifying test'; + lookup[APPLICATION_STATUS.QUALIFYING_TEST_PASSED] = 'Passed qualifying test'; + lookup[APPLICATION_STATUS.RECOMMENDED_FUTURE] = 'Recommended for future appointment'; + lookup[APPLICATION_STATUS.RECOMMENDED_IMMEDIATE] = 'Recommended for immediate appointment'; + lookup[APPLICATION_STATUS.RECONSIDER] = 'SCC to reconsider'; + lookup[APPLICATION_STATUS.REJECTED_CHARACTER] = 'Rejected on character'; + lookup[APPLICATION_STATUS.REJECTED_INELIGIBLE_ADDITIONAL] = 'Rejected as ineligible (ASC)'; + lookup[APPLICATION_STATUS.REJECTED_INELIGIBLE_STATUTORY] = 'Rejected as ineligible (statutory requirements)'; + lookup[APPLICATION_STATUS.SCENARIO_TEST_FAILED] = 'Failed scenario test'; + lookup[APPLICATION_STATUS.SCENARIO_TEST_PASSED] = 'Passed scenario test'; + lookup[APPLICATION_STATUS.SECOND_STAGE_INVITED] = 'Invited to EMP second stage assessment'; + lookup[APPLICATION_STATUS.SELECTION_FAILED] = 'Failed selection'; + lookup[APPLICATION_STATUS.SELECTION_INVITED] = 'Invited to selection'; + lookup[APPLICATION_STATUS.SELECTION_PASSED] = 'Passed selection'; + lookup[APPLICATION_STATUS.SIFT_FAILED] = 'Failed sift'; + lookup[APPLICATION_STATUS.SIFT_PASSED] = 'Passed sift'; + lookup[APPLICATION_STATUS.WITHDRAWN] = 'Withdrawn'; + + lookup[`${TASK_TYPE.CRITICAL_ANALYSIS}`] = 'Critical Analysis Test'; + lookup[`${TASK_TYPE.CRITICAL_ANALYSIS}Passed`] = 'Passed CA'; + lookup[`${TASK_TYPE.CRITICAL_ANALYSIS}Failed`] = 'Failed CA'; + lookup[`${TASK_TYPE.SITUATIONAL_JUDGEMENT}`] = 'Situational Judgement Test'; + lookup[`${TASK_TYPE.SITUATIONAL_JUDGEMENT}Passed`] = 'Passed SJ'; + lookup[`${TASK_TYPE.SITUATIONAL_JUDGEMENT}Failed`] = 'Failed SJ'; + lookup[`${TASK_TYPE.QUALIFYING_TEST}`] = 'CA + SJ Scoring'; + lookup[`${TASK_TYPE.QUALIFYING_TEST}Passed`] = 'Passed CA + SJ'; + lookup[`${TASK_TYPE.QUALIFYING_TEST}Failed`] = 'Failed CA + SJ'; + lookup[`${TASK_TYPE.SCENARIO}`] = 'Scenario Test'; + lookup[`${TASK_TYPE.SCENARIO}Passed`] = 'Passed scenario test'; + lookup[`${TASK_TYPE.SCENARIO}Failed`] = 'Failed scenario test'; + lookup[TASK_TYPE.TELEPHONE_ASSESSMENT] = 'Telephone Assessment'; + lookup[`${TASK_TYPE.TELEPHONE_ASSESSMENT}Passed`] = 'Passed telephone assessment'; + lookup[`${TASK_TYPE.TELEPHONE_ASSESSMENT}Failed`] = 'Failed telephone assessment'; + lookup[TASK_TYPE.ELIGIBILITY_SCC] = 'Eligibility SCC'; + lookup[`${TASK_TYPE.ELIGIBILITY_SCC}Passed`] = 'Passed eligibility SCC'; + lookup[`${TASK_TYPE.ELIGIBILITY_SCC}Failed`] = 'Failed eligibility SCC'; + lookup[TASK_TYPE.CHARACTER_AND_SELECTION_SCC] = 'Character and Selection SCC'; + lookup[`${TASK_TYPE.CHARACTER_AND_SELECTION_SCC}Passed`] = 'Passed character and selection SCC'; + lookup[`${TASK_TYPE.CHARACTER_AND_SELECTION_SCC}Failed`] = 'Failed character and selection SCC'; + lookup[TASK_TYPE.STATUTORY_CONSULTATION] = 'Statutory Consultation'; + lookup[`${TASK_TYPE.STATUTORY_CONSULTATION}Passed`] = 'Passed statutory consultation'; + lookup[`${TASK_TYPE.STATUTORY_CONSULTATION}Failed`] = 'Failed statutory consultation'; + lookup[TASK_TYPE.SHORTLISTING_OUTCOME] = 'Shortlisting Outcome'; + lookup[TASK_TYPE.WELSH_ASSESSMENT] = 'Welsh Assessment'; + lookup[TASK_TYPE.SELECTION_OUTCOME] = 'Selection Outcome'; + returnValue = lookup[value]; if (!returnValue) { diff --git a/src/helpersTMP/Timeline/exerciseTimeline.js b/src/helpersTMP/Timeline/exerciseTimeline.js index be030a526..434a7ea2c 100644 --- a/src/helpersTMP/Timeline/exerciseTimeline.js +++ b/src/helpersTMP/Timeline/exerciseTimeline.js @@ -1,4 +1,5 @@ import { isDate, formatDate } from '../date'; +import { TASK_TYPE } from '../../helpers/constants'; const getDateString = (date, format) => { return isDate(date) ? formatDate(date, format) : null; @@ -124,6 +125,7 @@ const exerciseTimeline = (data) => { entry: 'Situational judgement qualifying test (QT)', date: getDateAndTime(data.situationalJudgementTestDate, data.situationalJudgementTestStartTime), dateString: getDateAndTimeString(data.situationalJudgementTestDate, data.situationalJudgementTestStartTime, data.situationalJudgementTestEndTime), + taskType: TASK_TYPE.SITUATIONAL_JUDGEMENT, } ); } @@ -145,6 +147,7 @@ const exerciseTimeline = (data) => { entry: 'Critical analysis qualifying test (QT)', date: getDateAndTime(data.criticalAnalysisTestDate, data.criticalAnalysisTestStartTime), dateString: getDateAndTimeString(data.criticalAnalysisTestDate, data.criticalAnalysisTestStartTime, data.criticalAnalysisTestEndTime), + taskType: TASK_TYPE.CRITICAL_ANALYSIS, } ); } @@ -166,6 +169,7 @@ const exerciseTimeline = (data) => { entry: 'Scenario test', date: getDateAndTime(data.scenarioTestDate, data.scenarioTestStartTime), dateString: getDateAndTimeString(data.scenarioTestDate, data.scenarioTestStartTime, data.scenarioTestEndTime), + taskType: TASK_TYPE.SCENARIO, } ); } diff --git a/src/router.js b/src/router.js index 214922280..ae031d919 100644 --- a/src/router.js +++ b/src/router.js @@ -68,6 +68,7 @@ import QualifyingTestReportViewScore from '@/views/Exercise/Reports/QualifyingTe // Exercise tasks import ExerciseTasks from '@/views/Exercise/Tasks'; +// import ExerciseTasksIndex from '@/views/Exercise/Tasks/Index'; import ExerciseTasksIndependentAssessments from '@/views/Exercise/Tasks/IndependentAssessments'; import ExerciseTasksCharacterChecks from '@/views/Exercise/Tasks/CharacterChecks'; import ExerciseTasksCharacterChecksEdit from '@/views/Exercise/Tasks/CharacterChecksEdit'; @@ -620,6 +621,11 @@ const router = new Router({ { path: '', redirect: 'qualifying-tests', + // component: ExerciseTasksIndex, + // meta: { + // requiresAuth: true, + // title: 'Exercise Tasks', + // }, }, { path: 'equal-merit-tie-breakers', diff --git a/src/styles/main.scss b/src/styles/main.scss index 5e9879657..4b8f3a9da 100644 --- a/src/styles/main.scss +++ b/src/styles/main.scss @@ -6,3 +6,7 @@ @import "print"; @import "@jac-uk/jac-kit/helpers/css/global.scss"; + +.text--error { + color: $govuk-error-colour; +} diff --git a/src/views/Exercise/Tasks.vue b/src/views/Exercise/Tasks.vue index 2c87bb4f2..2c6d51532 100644 --- a/src/views/Exercise/Tasks.vue +++ b/src/views/Exercise/Tasks.vue @@ -13,75 +13,103 @@ diff --git a/src/views/Exercise/Tasks/Index.vue b/src/views/Exercise/Tasks/Index.vue new file mode 100644 index 000000000..9b6072c7d --- /dev/null +++ b/src/views/Exercise/Tasks/Index.vue @@ -0,0 +1,28 @@ + + + diff --git a/src/views/Exercise/Tasks/QualifyingTests/Cover.vue b/src/views/Exercise/Tasks/QualifyingTests/Cover.vue index a652037e0..e7e49e20d 100644 --- a/src/views/Exercise/Tasks/QualifyingTests/Cover.vue +++ b/src/views/Exercise/Tasks/QualifyingTests/Cover.vue @@ -1,60 +1,98 @@