diff --git a/src/pages/ExamsPage/components/AddAllowanceModal.jsx b/src/pages/ExamsPage/components/AddAllowanceModal.jsx index dc03279..41929ab 100644 --- a/src/pages/ExamsPage/components/AddAllowanceModal.jsx +++ b/src/pages/ExamsPage/components/AddAllowanceModal.jsx @@ -188,7 +188,7 @@ const AddAllowanceModal = ({ isOpen, close }) => { value={form['allowance-type']} data-testid="allowance-type" > - + @@ -202,7 +202,7 @@ const AddAllowanceModal = ({ isOpen, close }) => { onChange={handleChange} data-testid="additional-time-minutes" /> - { additionalTimeError && { formatMessage(messages.addAllowanceMinutesErrorFeedback) } } + { additionalTimeError && { formatMessage(messages.allowanceMinutesErrorFeedback) } } ) : ( diff --git a/src/pages/ExamsPage/components/AddAllowanceModal.test.jsx b/src/pages/ExamsPage/components/AddAllowanceModal.test.jsx index e97cb1c..5680d1f 100644 --- a/src/pages/ExamsPage/components/AddAllowanceModal.test.jsx +++ b/src/pages/ExamsPage/components/AddAllowanceModal.test.jsx @@ -79,7 +79,7 @@ describe('AddAllowanceModal', () => { fireEvent.click(screen.getByTestId('create-allowance-stateful-button')); expect(screen.getByText('Enter learners')).toBeInTheDocument(); expect(screen.getByText('Select exams')).toBeInTheDocument(); - expect(screen.getByText('Enter minutes')).toBeInTheDocument(); + expect(screen.getByText('Enter minutes greater than 0')).toBeInTheDocument(); }); it('should show an alert if the request fails', () => { diff --git a/src/pages/ExamsPage/components/AllowanceList.jsx b/src/pages/ExamsPage/components/AllowanceList.jsx index 6e3f545..5e3f971 100644 --- a/src/pages/ExamsPage/components/AllowanceList.jsx +++ b/src/pages/ExamsPage/components/AllowanceList.jsx @@ -39,6 +39,7 @@ const AllowanceList = () => { examName, allowanceType: formatMessage(messages.allowanceTypeMinutes), extraTimeMins, + username, }; const user = acc.find(u => u.userId === userId); diff --git a/src/pages/ExamsPage/components/AllowanceList.test.jsx b/src/pages/ExamsPage/components/AllowanceList.test.jsx index a1091ca..666817e 100644 --- a/src/pages/ExamsPage/components/AllowanceList.test.jsx +++ b/src/pages/ExamsPage/components/AllowanceList.test.jsx @@ -38,6 +38,7 @@ jest.mock('../hooks', () => ({ useButtonStateFromRequestStatus: jest.fn(), useCreateAllowance: jest.fn(), useFilteredExamsData: jest.fn(), + useEditAllowance: jest.fn(), useDeleteAllowance: jest.fn(), })); diff --git a/src/pages/ExamsPage/components/AllowanceListActions.jsx b/src/pages/ExamsPage/components/AllowanceListActions.jsx index fd9cf63..cfc474a 100644 --- a/src/pages/ExamsPage/components/AllowanceListActions.jsx +++ b/src/pages/ExamsPage/components/AllowanceListActions.jsx @@ -1,17 +1,144 @@ import { PropTypes } from 'prop-types'; import { - ActionRow, + ActionRow, Alert, Button, + Form, Icon, IconButtonWithTooltip, ModalDialog, useToggle, } from '@openedx/paragon'; -import { DeleteOutline, EditOutline } from '@openedx/paragon/icons'; +import { DeleteOutline, EditOutline, Info } from '@openedx/paragon/icons'; import { useIntl } from '@edx/frontend-platform/i18n'; +import React, { useState } from 'react'; import messages from '../messages'; -import { useDeleteAllowance } from '../hooks'; +import { useDeleteAllowance, useEditAllowance } from '../hooks'; +import { useClearRequest, useRequestError } from '../../../data/redux/hooks'; +import * as constants from '../../../data/constants'; + +const EditModal = (isOpen, close, allowance, formatMessage) => { + const editAllowance = useEditAllowance(); + const [additionalTimeError, setAdditionalTimeError] = useState(false); + const requestError = useRequestError(constants.RequestKeys.createAllowance); + const resetRequestError = useClearRequest(constants.RequestKeys.createAllowance); + + const initialFormState = { + username: allowance.username, + 'exam-id': allowance.examId, + 'exam-name': allowance.examName, + }; + const [form, setForm] = useState(initialFormState); + + const handleChange = (event) => { + const { name, value } = event.target; + resetRequestError(); + setForm(prev => ({ ...prev, [name]: value })); + }; + + const onClose = () => { + resetRequestError(); + setForm(initialFormState); + close(); + }; + + const onSubmit = () => { + const extraTimeMins = +form['extra-time-mins'] || 0; + // todo: We should maybe move the handling of this validation to the backend, + // as this should also be handled for the add allowance modal. + const valid = ( + extraTimeMins + && extraTimeMins > 0 + ); + setAdditionalTimeError(!valid); + if (valid) { + const payload = { + username: form.username, + exam_id: form['exam-id'], + extra_time_mins: extraTimeMins, + }; + editAllowance(allowance.id, payload, () => { + setForm(initialFormState); + onClose(); + }); + } + }; + + return ( + + + + {formatMessage(messages.editAllowanceHeader)} + + + + { requestError + && ( + + { formatMessage(messages.addAllowanceFailedAlertHeader) } +

+ { requestError.detail } +

+
+ )} +
+ + { formatMessage(messages.allowanceUsernameField) } + + + + { formatMessage(messages.editAllowanceExamField) } + + + + { formatMessage(messages.allowanceTypeField) } + + {formatMessage(messages.allowanceAdditionalMinutesOption)} + + + + { formatMessage(messages.allowanceMinutesField) } + + { additionalTimeError && { formatMessage(messages.allowanceMinutesErrorFeedback) } } + +
+
+ + + + {formatMessage(messages.allowanceCancelButton)} + + + + +
+ ); +}; const DeleteModal = (isOpen, onCancel, onDelete, formatMessage) => ( ( - {formatMessage(messages.deleteAllowanceCancel)} + {formatMessage(messages.allowanceCancelButton)}