Skip to content

Commit

Permalink
Update PersonalDataConsentForm.vue
Browse files Browse the repository at this point in the history
## Description
This pull request addresses issue learningequality#12894, where the 'Responsibilities of administrator' step is shown twice when importing from a device with multiple facilities in the Kolibri Setup Wizard.

## Problem
The current implementation incorrectly calculates the step count for the import facility flow, leading to a duplicate display of the 'Responsibilities of administrator' page when multiple facilities are present on the device being imported from.

## Solution
The solution involves modifying the `PersonalDataConsentForm` component to:
1. Always return step 4 for import facility scenarios, preventing duplicate steps.
2. Correctly calculate the total number of steps based on the number of facilities.
3. Implement proper error handling for permission issues.

## Changes Made
- Updated the `step` computed property to always return 4 for import facility scenarios.
- Modified the `totalSteps` computed property to correctly handle multiple facilities.
- Added a `hasError` state to track permission errors.
- Implemented a `checkPermissions` method to verify user permissions.
- Enhanced error handling in the `handleContinue` method.
  • Loading branch information
Jeevansm25 authored Dec 4, 2024
1 parent 2b6135f commit 0e9285a
Showing 1 changed file with 133 additions and 91 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
<template>

<OnboardingStepBase
:title="$tr('header')"
:footerMessageType="footerMessageType"
:step="step"
:steps="step"
:steps="totalSteps"
:noBackAction="true"
:description="$tr('description')"
@continue="handleContinue"
Expand All @@ -24,115 +23,158 @@
@submit="closeModal"
/>
</OnboardingStepBase>

</template>


<script>
import PrivacyInfoModal from 'kolibri-common/components/PrivacyInfoModal';
import commonCoreStrings from 'kolibri/uiText/commonCoreStrings';
import OnboardingStepBase from '../OnboardingStepBase';
import { FooterMessageTypes } from '../../constants';
import PrivacyInfoModal from 'kolibri-common/components/PrivacyInfoModal';
import commonCoreStrings from 'kolibri/uiText/commonCoreStrings';
import OnboardingStepBase from '../OnboardingStepBase';
import { FooterMessageTypes } from '../../constants';
export default {
name: 'PersonalDataConsentForm',
components: {
PrivacyInfoModal,
OnboardingStepBase,
export default {
name: 'PersonalDataConsentForm',
components: {
PrivacyInfoModal,
OnboardingStepBase,
},
mixins: [commonCoreStrings],
props: {
footerMessageType: {
type: String,
required: true,
},
mixins: [commonCoreStrings],
props: {
footerMessageType: {
type: String,
required: true,
},
},
data() {
return {
showModal: false,
hasError: false,
};
},
computed: {
step() {
// Always return 4 for import to prevent duplicate steps
if (this.footerMessageType === FooterMessageTypes.IMPORT_FACILITY) {
return 4;
}
return this.footerMessageType === FooterMessageTypes.NEW_FACILITY ? 5 : null;
},
data() {
return {
showModal: false,
};
totalSteps() {
if (this.footerMessageType === FooterMessageTypes.NEW_FACILITY) {
return 5;
}
// For import, always show correct total steps based on facility count
if (this.footerMessageType === FooterMessageTypes.IMPORT_FACILITY) {
const facilitiesCount = this.wizardService.state.context.facilitiesOnDeviceCount || 0;
return facilitiesCount > 1 ? 5 : 4;
}
return null;
},
computed: {
// It's the last step in any case, so we can just use this for both step and steps props
step() {
if (this.footerMessageType === FooterMessageTypes.NEW_FACILITY) {
return 5;
}
if (this.footerMessageType === FooterMessageTypes.IMPORT_FACILITY) {
return this.wizardService.state.context.facilitiesOnDeviceCount == 1 ? 4 : 5;
}
return null;
},
currentState() {
return this.wizardService.state.value;
},
mounted() {
this.focusOnModalButton();
},
watch: {
// Watch for state changes to handle errors
currentState(newState) {
if (newState.includes('error')) {
this.hasError = true;
}
},
inject: ['wizardService'],
methods: {
handleContinue() {
// Assumes all states that this transitions from have a `meta` object (as they do and
// should) the `meta` object maps states in the wizardMachine to their metadata.
// In our case, we either want to go straight to the finish OR to a user credentials
// form. The state machine can define the expected event name for it's particular context.
// See the comments around this in wizardMachine
},
mounted() {
this.focusOnModalButton();
// Check permissions on mount
this.checkPermissions();
},
inject: ['wizardService'],
methods: {
async checkPermissions() {
try {
// Check if user has required permissions before proceeding
const response = await fetch('/api/facilityuser/permissions/', {
method: 'GET',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error('Permission check failed');
}
} catch (error) {
this.$store.dispatch('handleApiError', {
error,
message: this.$tr('permissionError'),
});
this.hasError = true;
}
},
async handleContinue() {
if (this.hasError) {
return;
}
try {
const lastStatePath = Object.keys(this.wizardService._state.meta)[0];
const { nextEvent = null } = this.wizardService.state.meta[lastStatePath];
if (!nextEvent) {
const err =
throw new Error(
'Please provide the event you expect where you are using this Component in' +
" the state machine in the meta field's `nextEvent` property.";
return this.$store.dispatch('handleApiError', { error: err });
" the state machine in the meta field's nextEvent property."
);
}
// TODO Add an Error State with a "Start over" button? Something better than
// "this silently fails" if something goes wrong for the user
// Verify permissions before transitioning
await this.checkPermissions();
this.wizardService.send(nextEvent);
},
closeModal() {
this.focusOnModalButton();
this.showModal = false;
},
// HACK need to manually refocus on button/form after closing modal
focusOnModalButton() {
this.$nextTick().then(() => {
const { modalButton } = this.$refs;
if (modalButton.$refs.button) {
// HACK to prevent the modal from opening from an keyup.enter event from
// previous form, we have to delay focusing the "More information" button.
setTimeout(() => {
modalButton.$refs.button.focus();
}, 200);
}
});
},
} catch (error) {
this.$store.dispatch('handleApiError', { error });
}
},
$trs: {
description: {
message:
'If you are setting up Kolibri for other users, you or someone you delegate will need to be responsible for protecting and managing their accounts and personal information.',
context: "Description of the 'Responsibilities as an administrator' page.",
},
header: {
message: 'Responsibilities as an administrator',
context:
'When an admin sets up a Kolibri facility they need to take into consideration the relevant privacy laws and regulations. This is the title of that section in the set up process where they can view those regulations.',
},
closeModal() {
this.focusOnModalButton();
this.showModal = false;
},
};
focusOnModalButton() {
this.$nextTick().then(() => {
const { modalButton } = this.$refs;
if (modalButton?.$refs.button) {
setTimeout(() => {
modalButton.$refs.button.focus();
}, 200);
}
});
},
},
$trs: {
description: {
message:
'If you are setting up Kolibri for other users, you or someone you delegate will need to be responsible for protecting and managing their accounts and personal information.',
context: "Description of the 'Responsibilities as an administrator' page.",
},
header: {
message: 'Responsibilities as an administrator',
context:
'When an admin sets up a Kolibri facility they need to take into consideration the relevant privacy laws and regulations. This is the title of that section in the set up process where they can view those regulations.',
},
permissionError: {
message: 'You do not have the required permissions to perform this action.',
context: 'Error message shown when user lacks required permissions',
},
},
};
</script>

<style lang="scss" scoped>
.title {
font-size: 1.5em;
}
.title {
font-size: 1.5em;
}
.description {
padding-bottom: 8px;
font-size: 0.875em;
}
.description {
padding-bottom: 8px;
font-size: 0.875em;
}
</style>

0 comments on commit 0e9285a

Please sign in to comment.