diff --git a/src/App.jsx b/src/App.jsx index 76f3b9ea..9a3d2f00 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,3 +1,4 @@ +import React from 'react'; import { Routes, Route } from 'react-router-dom'; import { AuthenticatedPageRoute, @@ -5,9 +6,7 @@ import { } from '@edx/frontend-platform/react'; import { useIntl } from '@edx/frontend-platform/i18n'; -import PeerAssessmentView from 'views/AssessmentView/PeerAssessmentView'; -import SelfAssessmentView from 'views/AssessmentView/SelfAssessmentView'; -import StudentTrainingView from 'views/AssessmentView/StudentTrainingView'; +import AssessmentView from 'views/AssessmentView'; import SubmissionView from 'views/SubmissionView'; import XBlockView from 'views/XBlockView'; import GradeView from 'views/GradeView'; @@ -15,12 +14,42 @@ import GradeView from 'views/GradeView'; import AppContainer from 'components/AppContainer'; import ModalContainer from 'components/ModalContainer'; +import { useRefreshPageData } from 'hooks/app'; +import { useRefreshUpstream } from 'hooks/modal'; import { useUpdateTestProgressKey } from 'hooks/test'; import messages from './messages'; import routes from './routes'; -const RouterRoot = () => { +const App = () => { + /* + const { body } = document; + const refreshPageData = useRefreshPageData(); + const refreshUpstream = useRefreshUpstream(); + React.useEffect(() => { + const resizeEvent = () => { + // const { clientHeight, scrollHeight, offsetHeight } = body; + const height = body.scrollHeight; + if (document.referrer !== '' && height !== 0) { + window.parent.postMessage( + { type: 'ora-resize', payload: { height } }, + document.referrer, + ); + } + }; + resizeEvent(); + window.addEventListener('resize', resizeEvent); + }, [body.scrollHeight]); + + React.useEffect(() => { + window.addEventListener('message', (event) => { + if (event.data.type === 'plugin.modal-close') { + refreshPageData(); + } + }); + }, []); + */ + const { formatMessage } = useIntl(); // test @@ -69,9 +98,9 @@ const RouterRoot = () => { */ const baseRoutes = [ appRoute(routes.xblock, XBlockView), - modalRoute(routes.peerAssessment, PeerAssessmentView, 'Assess your peers'), - modalRoute(routes.selfAssessment, SelfAssessmentView, 'Assess yourself'), - modalRoute(routes.studentTraining, StudentTrainingView, 'Practice grading'), + modalRoute(routes.peerAssessment, AssessmentView, 'Assess your peers'), + modalRoute(routes.selfAssessment, AssessmentView, 'Assess yourself'), + modalRoute(routes.studentTraining, AssessmentView, 'Practice grading'), modalRoute(routes.submission, SubmissionView, 'Your response'), modalRoute(routes.graded, GradeView, 'My Grade'), } />, @@ -85,4 +114,4 @@ const RouterRoot = () => { ); }; -export default RouterRoot; +export default App; diff --git a/src/components/FileUpload/hooks.js b/src/components/FileUpload/hooks.js index 29203c81..9c06adb4 100644 --- a/src/components/FileUpload/hooks.js +++ b/src/components/FileUpload/hooks.js @@ -15,16 +15,14 @@ export const useUploadConfirmModalHooks = ({ closeHandler, uploadHandler, }) => { - const [description, setDescription] = useKeyedState( - stateKeys.description, - '' - ); + const [description, setDescription] = useKeyedState(stateKeys.description, ''); const [shouldShowError, setShouldShowError] = useKeyedState( stateKeys.shouldShowError, - false + false, ); const confirmUploadClickHandler = () => { + console.log({ confirmUploadClick: { description } }); if (description !== '') { uploadHandler(file, description); } else { @@ -54,13 +52,15 @@ export const useFileUploadHooks = ({ onFileUploaded }) => { const [uploadArgs, setUploadArgs] = useKeyedState(stateKeys.uploadArgs, {}); const [isModalOpen, setIsModalOpen] = useKeyedState( stateKeys.isModalOpen, - false + false, ); - const confirmUpload = useCallback(async () => { + const confirmUpload = useCallback(async (file, description) => { + console.log({ confirmUpload: { file, description } }); setIsModalOpen(false); if (onFileUploaded) { - await onFileUploaded(uploadArgs); + console.log({ uploadArgs }); + await onFileUploaded({ ...uploadArgs, description }); } setUploadArgs({}); }, [uploadArgs, onFileUploaded, setIsModalOpen, setUploadArgs]); @@ -75,7 +75,7 @@ export const useFileUploadHooks = ({ onFileUploaded }) => { setIsModalOpen(true); setUploadArgs({ fileData, handleError, requestConfig }); }, - [setIsModalOpen, setUploadArgs] + [setIsModalOpen, setUploadArgs], ); return { diff --git a/src/components/FileUpload/index.jsx b/src/components/FileUpload/index.jsx index e5e4196a..5113dda9 100644 --- a/src/components/FileUpload/index.jsx +++ b/src/components/FileUpload/index.jsx @@ -7,7 +7,9 @@ import { useIntl } from '@edx/frontend-platform/i18n'; import { nullMethod } from 'utils'; import { useFileUploadEnabled } from 'hooks/app'; +import { useViewStep } from 'hooks/routing'; import FilePreview from 'components/FilePreview'; +import { stepNames } from 'constants'; import UploadConfirmModal from './UploadConfirmModal'; import ActionCell from './ActionCell'; @@ -35,11 +37,10 @@ const FileUpload = ({ isModalOpen, onProcessUpload, uploadArgs, - } = useFileUploadHooks({ - onFileUploaded, - }); + } = useFileUploadHooks({ onFileUploaded }); + const viewStep = useViewStep(); - if ( !useFileUploadEnabled() ) { + if (!useFileUploadEnabled() || viewStep === stepNames.studentTraining) { return null; } diff --git a/src/components/ModalActions/hooks/useActiveSubmissionConfig.js b/src/components/ModalActions/hooks/useActiveSubmissionConfig.js index 394e10e5..b1c21dba 100644 --- a/src/components/ModalActions/hooks/useActiveSubmissionConfig.js +++ b/src/components/ModalActions/hooks/useActiveSubmissionConfig.js @@ -9,13 +9,13 @@ const useActiveSubmissionConfig = ({ formatMessage, }) => { const saveAndClose = React.useCallback( - () => (options.saveResponse ? options.saveResponse().then(closeModal) : null), + () => (options.finishLater ? options.finishLater().then(closeModal) : null), [options, closeModal], ); if (!options) { return null; } - const { submit, submitStatus, saveResponseStatus } = options; + const { submit, submitStatus, finishLaterStatus } = options; return { primary: { @@ -28,9 +28,9 @@ const useActiveSubmissionConfig = ({ }, secondary: { onClick: saveAndClose, - state: saveResponseStatus, + state: finishLaterStatus, labels: { - [MutationStatus.idle]: formatMessage(messages.finishLater), + default: formatMessage(messages.finishLater), [MutationStatus.loading]: formatMessage(messages.savingResponse), }, }, diff --git a/src/components/ModalActions/hooks/useFinishedStateActions.js b/src/components/ModalActions/hooks/useFinishedStateActions.js index f53de520..d9e17e77 100644 --- a/src/components/ModalActions/hooks/useFinishedStateActions.js +++ b/src/components/ModalActions/hooks/useFinishedStateActions.js @@ -66,6 +66,7 @@ const useFinishedStateActions = () => { } console.log("?"); // submission finished state + console.log({ startStepAction }); return { primary: startStepAction, secondary: finishLaterAction }; }; diff --git a/src/components/ModalActions/hooks/useModalActionConfig.js b/src/components/ModalActions/hooks/useModalActionConfig.js index 7f4e1e4a..68b9216f 100644 --- a/src/components/ModalActions/hooks/useModalActionConfig.js +++ b/src/components/ModalActions/hooks/useModalActionConfig.js @@ -19,9 +19,11 @@ const useModalActionConfig = ({ options }) => { const exitAction = useExitAction(); - // console.log({ useModalActionConfig: { step, globalState, hasSubmitted } }); // finished state if (hasSubmitted) { + if (globalState.activeStepState !== stepStates.inProgress) { + return { primary: exitAction }; + } return finishedStateActions; } diff --git a/src/components/PageDataProvider.jsx b/src/components/PageDataProvider.jsx deleted file mode 100644 index b684ecca..00000000 --- a/src/components/PageDataProvider.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import { useIsPageDataLoaded } from 'hooks/app'; - -const PageDataProvider = ({ children }) => (useIsPageDataLoaded() ? children : null); -PageDataProvider.propTypes = { children: PropTypes.node.isRequired }; - -export default PageDataProvider; diff --git a/src/components/ProgressBar/ProgressStep.jsx b/src/components/ProgressBar/ProgressStep.jsx index 39a23a2c..2370dd81 100644 --- a/src/components/ProgressBar/ProgressStep.jsx +++ b/src/components/ProgressBar/ProgressStep.jsx @@ -46,7 +46,7 @@ const ProgressStep = ({ } else if (isComplete) { iconSrc = CheckCircle; if (step === stepNames.done && myGrade) { - subLabel = `${myGrade.earned} / ${myGrade.possible}`; + subLabel = `${myGrade.stepScore.earned} / ${myGrade.stepScore.possible}`; } } return ( diff --git a/src/components/ProgressBar/index.jsx b/src/components/ProgressBar/index.jsx index db5c8122..eaed274b 100644 --- a/src/components/ProgressBar/index.jsx +++ b/src/components/ProgressBar/index.jsx @@ -50,20 +50,20 @@ export const ProgressBar = ({ className }) => { return null; } - const stepEl = (curStep) => - stepLabels[curStep] ? ( + const stepEl = (curStep) => stepLabels[curStep] + ? ( ) : null; return ( - -
+ +
{stepOrders.map(stepEl)}
diff --git a/src/components/Rubric/Rubric.scss b/src/components/Rubric/Rubric.scss index 87077ca9..18c47605 100644 --- a/src/components/Rubric/Rubric.scss +++ b/src/components/Rubric/Rubric.scss @@ -61,7 +61,8 @@ } .rubric-body { - overflow-y: scroll; + overflow-y: hide; + padding: map-get($spacers, 3); } .rubric-footer { diff --git a/src/components/Rubric/index.jsx b/src/components/Rubric/index.jsx index d244e789..f58c8a9d 100644 --- a/src/components/Rubric/index.jsx +++ b/src/components/Rubric/index.jsx @@ -23,13 +23,14 @@ export const Rubric = ({ isCollapsible }) => { return ( {formatMessage(messages.rubric)}} - className='rubric-card' + className="rubric-card" + defaultOpen > - + {!isCollapsible && ( <>

{formatMessage(messages.rubric)}

-
+
)} {criteria.map((criterion) => ( diff --git a/src/components/StatusAlert/messages.js b/src/components/StatusAlert/messages.js index 7b47b396..24b0327d 100644 --- a/src/components/StatusAlert/messages.js +++ b/src/components/StatusAlert/messages.js @@ -272,4 +272,3 @@ export default { }, ...messages, }; - diff --git a/src/components/StatusAlert/useStatusAlertData.jsx b/src/components/StatusAlert/useStatusAlertData.jsx index 444f671b..36e726ae 100644 --- a/src/components/StatusAlert/useStatusAlertData.jsx +++ b/src/components/StatusAlert/useStatusAlertData.jsx @@ -52,6 +52,7 @@ const useStatusAlertData = ({ const { formatMessage } = useIntl(); const { activeStepName, + activeStepState, cancellationInfo, stepState, } = useGlobalState({ step }); @@ -102,6 +103,15 @@ const useStatusAlertData = ({ heading: messages.headings[viewStep].submitted, ...alertTypes.success, })); + if (activeStepState !== stepStates.inProgress) { + out.push(alertConfig({ + message: messages.alerts[activeStepName][activeStepState], + heading: messages.headings[activeStepName][activeStepState], + actions: [ + , + ], + })); + } if (activeStepName === stepNames.staff) { out.push(alertConfig({ message: messages.alerts[activeStepName].staffAssessment, @@ -141,6 +151,12 @@ const useStatusAlertData = ({ heading: messages.headings.peer.finished, })]; } + if (stepName === stepNames.staff) { + return [alertConfig({ + message: messages.alerts[activeStepName].staffAssessment, + heading: messages.headings[activeStepName].staffAssessment, + })]; + } return [alertConfig({ message: messages.alerts[stepName][stepState], heading: messages.headings[stepName][stepState], diff --git a/src/components/StepProgressIndicator/index.jsx b/src/components/StepProgressIndicator/index.jsx index d1789713..d20a55b4 100644 --- a/src/components/StepProgressIndicator/index.jsx +++ b/src/components/StepProgressIndicator/index.jsx @@ -20,14 +20,12 @@ import messages from './messages'; import './index.scss'; const StepProgressIndicator = ({ step }) => { - console.log("Step Progress Indicator"); const { formatMessage } = useIntl(); const configInfo = useAssessmentStepConfig(); const stepInfo = useStepInfo(); const globalState = useGlobalState(); const hasSubmitted = useHasSubmitted(); const { activeStepName } = globalState; - console.log({ globalState, stepInfo }); const loadNextAction = useLoadNextAction(); if (![stepNames.peer, stepNames.studentTraining].includes(step)) { return null; diff --git a/src/data/redux/app/reducer.js b/src/data/redux/app/reducer.js index 26ca7890..3bbec275 100644 --- a/src/data/redux/app/reducer.js +++ b/src/data/redux/app/reducer.js @@ -7,6 +7,7 @@ const initialState = { submittedAssessment: null, showTrainingError: false, }, + response: null, formFields: { criteria: [], overallFeedback: '', @@ -26,8 +27,9 @@ const app = createSlice({ reducers: { loadAssessment: (state, { payload }) => ({ ...state, - assessment: { ...initialState.assessment, submittedAssessment: payload }, + assessment: { ...initialState.assessment, submittedAssessment: payload.data }, }), + loadResponse: (state, { payload }) => ({ ...state, response: payload }), setHasSubmitted: (state, { payload }) => ({ ...state, hasSubmitted: payload, diff --git a/src/data/redux/app/selectors/index.js b/src/data/redux/app/selectors/index.js index 5824b0bd..69c4493c 100644 --- a/src/data/redux/app/selectors/index.js +++ b/src/data/redux/app/selectors/index.js @@ -12,6 +12,8 @@ const selectors = { formFieldsData(state).criteria[criterionIndex]?.selectedOption ), + response: createSelector(rootSelector, ({ response }) => response), + hasSubmitted: createSelector(rootSelector, ({ hasSubmitted }) => hasSubmitted), overallFeedback: (state) => formFieldsData(state).overallFeedback, diff --git a/src/data/redux/hooks/app.js b/src/data/redux/hooks/app.js index f0b71f24..c2522e7c 100644 --- a/src/data/redux/hooks/app.js +++ b/src/data/redux/hooks/app.js @@ -12,6 +12,7 @@ export const useHasSubmitted = () => useSelector(selectors.hasSubmitted); export const useShowValidation = () => useSelector(selectors.showValidation); export const useShowTrainingError = () => useSelector(selectors.showTrainingError); export const useOverallFeedbackValue = () => useSelector(selectors.overallFeedback); +export const useResponse = () => useSelector(selectors.response); /* special selectors */ export const useCriterionOption = criterionIndex => ( @@ -31,6 +32,7 @@ export const useSetShowTrainingError = () => useActionHook(actions.setShowTraini export const useSetFormFields = () => useActionHook(actions.setFormFields); export const useResetAssessment = () => useActionHook(actions.resetAssessment); export const useSetOverallFeedback = () => useActionHook(actions.setOverallFeedback); +export const useSetResponse = () => useActionHook(actions.loadResponse); export const useSetCriterionFeedback = (criterionIndex) => { const dispatch = useDispatch(); diff --git a/src/data/services/lms/api.ts b/src/data/services/lms/api.ts index f6f4909f..860e8dec 100644 --- a/src/data/services/lms/api.ts +++ b/src/data/services/lms/api.ts @@ -1,44 +1,54 @@ -import { queryKeys } from './constants'; +import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; +// import { queryKeys } from './constants'; import { AssessmentData } from './types'; +import * as urls from './urls'; -export const submitAssessment = (data: AssessmentData) => { - // TODO: submit rubric - console.log({ submitAssessment: data }); - let resolvePromise; - const promise = new Promise((resolve) => { - resolvePromise = resolve; - }); - setTimeout(() => { - console.log('assessment submitted'); - resolvePromise(data); - console.log("Should have resolved"); - }, 1000); - return promise; +export const useSubmitAssessment = () => { + const url = urls.useSubmitAssessmentUrl(); + return (data: AssessmentData) => { + console.log({ submitAssessment: data }); + return getAuthenticatedHttpClient().post(url, data); + }; }; -export const submitResponse = (data: any) => { - console.log({ submitResponse: data }); - let resolvePromise; - const promise = new Promise((resolve) => { - resolvePromise = resolve; - }); - setTimeout(() => { - console.log('response submitted'); - resolvePromise(null); - console.log("Should have resolved"); - }, 1000); - return promise; +export const useSubmitResponse = () => { + const url = urls.useSubmitUrl(); + return (data: any) => { + console.log({ submitResponse: data }); + return getAuthenticatedHttpClient().post(url, { submission: data }); + }; }; -export const saveResponse = (data: any) => { - console.log({ save: data }); - // TODO: save response for later - return new Promise((resolve) => { - setTimeout(() => { - console.log('response saved'); - resolve(null); - }, 1000); - }); +export const useSaveDraft = () => { + const url = urls.useSaveDraftUrl(); + return (data: any) => { + console.log({ save: data }); + return getAuthenticatedHttpClient().post(url, { response: data }); + }; +}; + +export const useAddFile = () => { + const url = urls.useAddFileUrl(); + const responseUrl = urls.useUploadResponseUrl(); + return (data: any, description: string) => { + const { post } = getAuthenticatedHttpClient(); + const file = { + fileDescription: description, + fileName: data.name, + fileSize: data.size, + contentType: data.type, + }; + console.log({ addFile: { data, description, file } }); + return post(url, file) + .then(response => post(responseUrl, { fileIndex: response.data.fileIndex, success: true })) + }; +}; + +export const useDeleteFile = () => { + const url = urls.useDeleteFileUrl(); + return (fileIndex) => { + return getAuthenticatedHttpClient().post(url, { fileIndex }); + }; }; export const fakeProgress = async (requestConfig) => { diff --git a/src/data/services/lms/hooks/actions/files.ts b/src/data/services/lms/hooks/actions/files.ts index 6c655afe..307194eb 100644 --- a/src/data/services/lms/hooks/actions/files.ts +++ b/src/data/services/lms/hooks/actions/files.ts @@ -1,7 +1,13 @@ import * as zip from '@zip.js/zip.js'; import FileSaver from 'file-saver'; +import { getAuthenticatedHttpClient } from '@edx/frontend-platform'; +import { useMutation } from '@tanstack/react-query'; + import { queryKeys } from 'constants'; +import * as api from 'data/services/lms/api'; +import { useTestDataPath } from 'hooks/test'; + import fakeData from '../../fakeData'; import { UploadedFile } from '../../types'; @@ -99,43 +105,36 @@ export const downloadBlobs = async (files: UploadedFile[]) => { return { blobs, files }; }; -export const useUploadFiles = () => - useCreateMutationAction(async (data: any) => { +export const useUploadFiles = () => { + const testDataPath = useTestDataPath(); + const addFile = api.useAddFile(); + const apiFn = (data) => { + const { fileData, requestConfig, description } = data; + const file = fileData.getAll('file')[0]; + console.log({ file }); + return addFile(file, description); + }; + const mockFn = (data, description) => { const { fileData, requestConfig } = data; - const files = fileData.getAll('file'); - // TODO: upload files - /* - * const addFileResponse = await post(`{xblock_id}/handler/file/add`, file); - * const uploadResponse = await(post(response.fileUrl, file)); - * post(`${xblock_id}/handler/download_url', (response)); - */ - await fakeProgress(requestConfig); - return Promise.resolve(); + return fakeProgress(requestConfig); + }; + return useMutation({ + mutationFn: testDataPath ? mockFn : apiFn, }); +}; -export const useDeleteFile = () => - useCreateMutationAction(async (fileIndex, queryClient) => { - await new Promise((resolve) => - setTimeout(() => { - fakeData.pageData.shapes.emptySubmission.submission.response = { - ...fakeData.pageData.shapes.emptySubmission.submission.response, - uploaded_files: [ - ...fakeData.pageData.shapes.emptySubmission.submission.response.uploaded_files.filter( - (_, index) => index !== fileIndex - ), - ], - } as any; - resolve(null); - }, 1000) - ); - - console.log("invalidate pageData"); - queryClient.invalidateQueries([queryKeys.pageData, false]); - return Promise.resolve( - fakeData.pageData.shapes.emptySubmission.submission.response - .uploaded_files - ); +export const useDeleteFile = () => { + const testDataPath = useTestDataPath(); + const deleteFile = api.useDeleteFile(); + const apiFn = (index) => { + console.log({ deleteFile: index }); + return deleteFile(index); + }; + const mockFn = (data) => Promise.resolve(data); + return useMutation({ + mutationFn: testDataPath ? mockFn : apiFn, }); +}; export const useDownloadFiles = () => useCreateMutationAction( diff --git a/src/data/services/lms/hooks/actions/index.ts b/src/data/services/lms/hooks/actions/index.ts index 44d01332..4ab01fe9 100644 --- a/src/data/services/lms/hooks/actions/index.ts +++ b/src/data/services/lms/hooks/actions/index.ts @@ -22,7 +22,7 @@ const apiLog = (apiMethod, name) => (data) => apiMethod(data).then(response => { export const useSubmitAssessment = ({ onSuccess } = {}) => { const testDataPath = useTestDataPath(); - const apiFn = api.submitAssessment; + const apiFn = api.useSubmitAssessment(); const mockFn = React.useCallback((data) => Promise.resolve(data), []); return useMutation({ mutationFn: apiLog(testDataPath ? mockFn : apiFn, 'submittedAssessment'), @@ -43,7 +43,7 @@ export const useSubmitResponse = ({ onSuccess } = {}) => { queryClient.setQueryData([queryKeys.pageData], state); return Promise.resolve(state); }, [queryClient, step]); - const apiFn = api.submitAssessment; + const apiFn = api.useSubmitResponse(); return useMutation({ mutationFn: apiLog(testDataPath ? mockFn : apiFn, 'submittedResponse'), @@ -51,21 +51,27 @@ export const useSubmitResponse = ({ onSuccess } = {}) => { }); }; -export const useSaveDraftResponse = ({ onSuccess } = {}) => { +export const useFinishLater = () => { // const queryClient = useQueryClient(); const testDataPath = useTestDataPath(); - - const apiFn = apiLog(api.saveResponse, 'saveDraftResponse'); - + const apiFn = api.useSaveDraft(); const mockFn = React.useCallback((data) => Promise.resolve(data), []); + return useMutation({ + mutationFn: apiLog(testDataPath ? mockFn : apiFn, 'savedDraftResponse'), + }); +}; +export const useSaveDraftResponse = () => { + // const queryClient = useQueryClient(); + const testDataPath = useTestDataPath(); + const apiFn = api.useSaveDraft(); + const mockFn = React.useCallback((data) => Promise.resolve(data), []); return useMutation({ mutationFn: apiLog(testDataPath ? mockFn : apiFn, 'savedDraftResponse'), - onSuccess, }); }; export const useRefreshPageData = () => { const queryClient = useQueryClient(); - return () => queryClient.invalidateQueries({ queryKey: queryKeys.pageData }); + return () => queryClient.invalidateQueries({ queryKey: [queryKeys.pageData] }); }; diff --git a/src/data/services/lms/hooks/data.ts b/src/data/services/lms/hooks/data.ts index 4a44f0ee..d112beec 100644 --- a/src/data/services/lms/hooks/data.ts +++ b/src/data/services/lms/hooks/data.ts @@ -55,6 +55,7 @@ export const useORAConfig = (): types.QueryData => { ({ data }) => camelCaseObject(data) ); }, + staleTime: Infinity, }); }; @@ -78,23 +79,28 @@ export const usePageData = () => { const progressKey = testProgressKey || params.progressKey || defaultViewProgressKeys[viewKey]; const queryFn = React.useCallback(() => { - console.log({ testDataPath }); if (testDataPath) { console.log("page data fake data"); return Promise.resolve(camelCaseObject(loadState({ view, progressKey }))); } const url = (hasSubmitted || view === stepNames.xblock) - ? `${pageDataUrl}` - : `${pageDataUrl}/${view}`; + ? pageDataUrl() + : pageDataUrl(viewStep); + console.log({ url, hasSubmitted, view }); console.log("page data real data"); - return getAuthenticatedHttpClient().post(url, {}).then( - ({ data }) => camelCaseObject(data) - ); - }, [testDataPath, view, progressKey, testProgressKey]); + console.log({ pageDataUrl: url }); + return getAuthenticatedHttpClient().post(url, {}) + .then(({ data }) => camelCaseObject(data)) + .then(data => { + console.log({ pageData: data }); + return data; + }); + }, [testDataPath, view, progressKey, testProgressKey, hasSubmitted]); return useQuery({ queryKey: [queryKeys.pageData, testDataPath], queryFn, + staleTime: Infinity, }); }; diff --git a/src/data/services/lms/hooks/selectors.test.ts b/src/data/services/lms/hooks/selectors.test.ts deleted file mode 100644 index 0a9bb0f6..00000000 --- a/src/data/services/lms/hooks/selectors.test.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { when } from 'jest-when'; -import { keyStore } from '@edx/react-unit-test-utils'; - -import * as data from './data'; -import * as selectors from './selectors'; - -const statusData = { - isLoading: 'is-loading', - isFetching: 'is-fetching', - isInitialLoading: 'is-initial-loading', - status: 'status', - error: 'error', -}; - -const testValue = 'some-test-data'; - -const dataKeys = keyStore(data); -const selectorKeys = keyStore(selectors); - -const mockHook = (module, key, returnValue) => { - const spy = jest.spyOn(module, key); - when(spy).calledWith().mockReturnValueOnce(returnValue); -}; - -describe('lms data selector hooks', () => { - const mockORAConfig = (returnValue) => { - mockHook(data, dataKeys.useORAConfig, returnValue); - }; - const mockORAData = (returnValue) => { - mockHook(selectors, selectorKeys.useORAConfigData, returnValue); - }; - describe('ORA Config selectors', () => { - describe('useORAConfigDataStatus', () => { - it('returns status data from useORAConfig call', () => { - mockORAConfig({ - ...statusData, - other: 'field', - and: 'another one', - }); - expect(selectors.useORAConfigDataStatus()).toEqual(statusData); - }); - }); - describe('useIsORAConfigLoaded', () => { - it('returns true if ORAConfig.status is "success"', () => { - mockORAConfig({ ...statusData, status: 'success' }); - expect(selectors.useIsORAConfigLoaded()).toEqual(true); - }); - it('returns false if ORAConfig.status is not "success"', () => { - mockORAConfig({ ...statusData, status: 'random' }); - expect(selectors.useIsORAConfigLoaded()).toEqual(false); - }); - }); - describe('useORAConfigData', () => { - it('returns data from ORAConfig', () => { - mockORAConfig({ ...statusData, data: testValue }); - expect(selectors.useORAConfigData()).toEqual(testValue); - }); - }); - describe('useSubmissionConfig', () => { - it('returns submissionConfig from ORAConfigData', () => { - mockORAData({ submissionConfig: testValue }); - expect(selectors.useSubmissionConfig()).toEqual(testValue); - }); - }); - describe('useAssessmentStepConfig', () => { - it('returns assessmentSteps from ORAConfigData', () => { - mockORAData({ assessmentSteps: testValue }); - expect(selectors.useAssessmentStepConfig()).toEqual(testValue); - }); - }); - describe('useRubricConfig', () => { - it('returns rubric from ORAConfigData', () => { - mockORAData({ rubric: testValue }); - expect(selectors.useRubricConfig()).toEqual(testValue); - }); - }); - describe('useLeaderboardConfig', () => { - it('returns rubric from ORAConfigData', () => { - mockORAData({ leaderboardConfig: testValue }); - expect(selectors.useLeaderboardConfig()).toEqual(testValue); - }); - }); - }); - describe('Page Data selectors', () => { - const mockPageDataQuery = (returnValue) => { - mockHook(data, dataKeys.usePageData, returnValue); - }; - const mockPageData = (returnValue) => { - mockHook(selectors, selectorKeys.usePageData, returnValue); - }; - describe('usePageDataStatus', () => { - it('returns status data from useORAConfig call', () => { - mockPageDataQuery({ - ...statusData, - other: 'field', - and: 'another one', - }); - expect(selectors.usePageDataStatus()).toEqual(statusData); - }); - }); - describe('useIsPageDataLoaded', () => { - it('returns true if PageData.status is "success"', () => { - mockPageDataQuery({ ...statusData, status: 'success' }); - expect(selectors.useIsPageDataLoaded()).toEqual(true); - }); - it('returns false if PageData.status is not "success"', () => { - mockPageDataQuery({ ...statusData, status: 'random' }); - expect(selectors.useIsPageDataLoaded()).toEqual(false); - }); - }); - describe('usePageData', () => { - it('returns data from PageData query', () => { - mockPageDataQuery({ ...statusData, data: testValue }); - expect(selectors.usePageData()).toEqual(testValue); - }); - }); - describe('useSubmissionTeamInfo', () => { - it('returns submission team info from PageData', () => { - mockPageData({ submission: { teamInfo: testValue } }); - expect(selectors.useSubmissionTeamInfo()).toEqual(testValue); - }); - }); - describe('useSubmissionStatus', () => { - it('returns hasCancelled, hasReceivedGraded, and hasSubmitted', () => { - const submissionStatus = { - hasCancelled: 'has-cancelled', - hasReceivedGrade: 'has-received-grade', - hasSubmitted: 'has-submitted', - }; - mockPageData({ submission: { ...submissionStatus, other: 'fields' } }); - expect(selectors.useSubmissionStatus()).toEqual(submissionStatus); - }); - }); - describe('useSubmissionResponse', () => { - it('returns submission response from PageData', () => { - mockPageData({ submission: { response: testValue } }); - expect(selectors.useSubmissionResponse()).toEqual(testValue); - }) - }); - }); -}); diff --git a/src/data/services/lms/hooks/selectors/index.ts b/src/data/services/lms/hooks/selectors/index.ts index 5e55fd40..5add47fc 100644 --- a/src/data/services/lms/hooks/selectors/index.ts +++ b/src/data/services/lms/hooks/selectors/index.ts @@ -44,7 +44,7 @@ export const useStepState = ({ step = null } = {}) => { return hasReceivedFinalGrade ? stepStates.done : stepStates.notAvailable; } - if (stepName === stepNames.peer && stepInfo?.peer?.isWaitingForSubmissions) { + if (activeStepName === stepNames.peer && stepInfo?.peer?.isWaitingForSubmissions) { return stepStates.waiting; } // For Assessment steps diff --git a/src/data/services/lms/hooks/selectors/oraConfig.ts b/src/data/services/lms/hooks/selectors/oraConfig.ts index 15bee7f8..81142209 100644 --- a/src/data/services/lms/hooks/selectors/oraConfig.ts +++ b/src/data/services/lms/hooks/selectors/oraConfig.ts @@ -53,7 +53,7 @@ export const useEmptyRubric = () => { return React.useMemo(() => ({ criteria: rubric.criteria.map((criterion) => ({ selectedOption: null, - feedback: criterion.feedbackEnabled ? '' : null, + feedback: '', })), overallFeedback: '', }), [rubric.criteria]); diff --git a/src/data/services/lms/hooks/selectors/pageData.ts b/src/data/services/lms/hooks/selectors/pageData.ts index c551d1f7..7f3959cc 100644 --- a/src/data/services/lms/hooks/selectors/pageData.ts +++ b/src/data/services/lms/hooks/selectors/pageData.ts @@ -13,14 +13,20 @@ export const usePageDataStatus = () => { return { isLoading: queryStatus.isLoading, isFetching: queryStatus.isFetching, + isRefetching: queryStatus.isRefetching, isInitialLoading: queryStatus.isInitialLoading, + isStale: queryStatus.isStale, status: queryStatus.status, error: queryStatus.error, }; }; -export const useIsPageDataLoaded = (): boolean => ( - data.usePageData().status === 'success' -); +export const useIsPageDataLoaded = (): boolean => { + const pageData = data.usePageData(); + console.log({ rawPageData: pageData }); + const { isRefetching, isStale, status } = pageData; + console.log({ isStale, isRefetching }); + return status === 'success' && !isRefetching; +}; export const usePageData = (): types.PageData => { const pageData = data.usePageData()?.data; if (process.env.NODE_ENV === 'development') { diff --git a/src/data/services/lms/urls.js b/src/data/services/lms/urls.js index dbe789db..a446c3fd 100644 --- a/src/data/services/lms/urls.js +++ b/src/data/services/lms/urls.js @@ -10,22 +10,25 @@ const useBaseUrl = () => { return `${getConfig().LMS_BASE_URL}/courses/${courseId}/xblock/${xblockId}/handler`; }; -export const useORAConfigUrl = () => { - const baseUrl = useBaseUrl(); - return `${baseUrl}/get_block_info`; -}; +export const useSaveDraftUrl = () => `${useBaseUrl()}/submission/draft`; +export const useSubmitUrl = () => `${useBaseUrl()}/submission/submit`; +export const useSubmitAssessmentUrl = () => `${useBaseUrl()}/assessment/submit`; +export const useORAConfigUrl = () => `${useBaseUrl()}/get_block_info`; +export const useGetPeerUrl = () => `${useBaseUrl()}/assessment/get_peer`; +export const useAddFileUrl = () => `${useBaseUrl()}/file/add`; +export const useUploadResponseUrl = () => `${useBaseUrl()}/file/upload_response`; +export const useDeleteFileUrl = () => `${useBaseUrl()}/file/delete`; export const useViewUrl = () => { const { xblockId, courseId } = useParams(); return ({ view }) => `${getConfig().BASE_URL}/${stepRoutes[view]}/${courseId}/${xblockId}`; }; -export const usePageDataUrl = (step) => { +export const usePageDataUrl = () => { const baseUrl = useBaseUrl(); - if ( [stepNames.submission, stepNames.peer].includes(step) ) { - return `${baseUrl}/get_learner_data/${step}`; - } - return `${baseUrl}/get_learner_data`; + return (step) => (step + ? `${baseUrl}/get_learner_data/${step}` + : `${baseUrl}/get_learner_data/`); }; export default StrictDict({ diff --git a/src/hooks/actions/useStartStepAction.js b/src/hooks/actions/useStartStepAction.js index 7110f2eb..63cf5f1b 100644 --- a/src/hooks/actions/useStartStepAction.js +++ b/src/hooks/actions/useStartStepAction.js @@ -3,7 +3,7 @@ import { useNavigate, useParams } from 'react-router-dom'; import { useIntl } from '@edx/frontend-platform/i18n'; import { stepNames, stepRoutes } from 'constants'; -import { useRefreshPageData, useActiveStepName } from 'hooks/app'; +import { useRefreshPageData, useActiveStepName, useSetResponse } from 'hooks/app'; import { useSetHasSubmitted, useSetShowValidation } from 'hooks/assessment'; import messages from './messages'; @@ -14,6 +14,7 @@ const useStartStepAction = (viewStep) => { const refreshPageData = useRefreshPageData(); const setHasSubmitted = useSetHasSubmitted(); const setShowValidation = useSetShowValidation(); + const setResponse = useSetResponse(); const stepName = useActiveStepName(); @@ -23,10 +24,12 @@ const useStartStepAction = (viewStep) => { } const onClick = () => { - navigate(`/${stepRoutes[stepName]}/${courseId}/${xblockId}`); - refreshPageData(); + console.log("Load next page"); setHasSubmitted(false); setShowValidation(false); + setResponse(null); + navigate(`/${stepRoutes[stepName]}/${courseId}/${xblockId}`); + refreshPageData(); }; const startMessages = { diff --git a/src/hooks/app.js b/src/hooks/app.js index 90a787f8..3c41c58f 100644 --- a/src/hooks/app.js +++ b/src/hooks/app.js @@ -6,6 +6,8 @@ export const { useHasSubmitted, useSetHasSubmitted, useSetShowTrainingError, + useResponse, + useSetResponse, } = reduxHooks; export const { @@ -31,10 +33,11 @@ export const { } = lmsSelectors; export const { + useFinishLater, useDeleteFile, useDownloadFiles, useRefreshPageData, - useSaveResponse, + useSaveDraftResponse, useSubmitResponse, useUploadFiles, } = lmsActions; diff --git a/src/hooks/assessment.js b/src/hooks/assessment.js index 9dd94cc9..bacac0ce 100644 --- a/src/hooks/assessment.js +++ b/src/hooks/assessment.js @@ -72,6 +72,12 @@ export const useCheckTrainingSelection = () => { export const useInitializeAssessment = () => { const emptyRubric = lmsSelectors.useEmptyRubric(); const setFormFields = reduxHooks.useSetFormFields(); + const setResponse = reduxHooks.useSetResponse(); + const response = lmsSelectors.useResponseData(); + React.useEffect(() => { + setResponse(response); + }, []); + return React.useCallback(() => { setFormFields(emptyRubric); }, []); @@ -135,6 +141,8 @@ export const useResetAssessment = () => { export const { useHasSubmitted, + useResponse, + useSetResponse, useSetHasSubmitted, useSetShowValidation, useShowValidation, diff --git a/src/hooks/modal.js b/src/hooks/modal.js index 53332970..7fb35148 100644 --- a/src/hooks/modal.js +++ b/src/hooks/modal.js @@ -2,10 +2,26 @@ import { useLocation } from 'react-router-dom'; import { useViewUrl } from 'data/services/lms/urls'; import { routeSteps } from 'constants'; +export const useRefreshUpstream = () => { + if (document.referrer !== '') { + const postMessage = (data) => window.parent.postMessage(data, process.env.BASE_URL); + return () => { + console.log("Send refresh upstream"); + postMessage({ type: 'ora-refresh' }); + }; + } + return () => { + console.log("refresh upstream"); + }; +}; + export const useCloseModal = () => { if (document.referrer !== '') { - const postMessage = (data) => window.parent.postMessage(data, document.referrer); - return () => postMessage({ type: 'plugin.modal-close' }); + const postMessage = (data) => window.parent.postMessage(data, '*'); + return () => { + postMessage({ type: 'ora-refresh' }); + postMessage({ type: 'plugin.modal-close' }); + }; } return () => { console.log("CLose Modal"); @@ -13,7 +29,7 @@ export const useCloseModal = () => { }; export const useOpenModal = () => { - const postMessage = (data) => window.parent.postMessage(data, document.referrer); + const postMessage = (data) => window.parent.postMessage(data, '*'); const viewUrl = useViewUrl(); return ({ view, title }) => { postMessage({ diff --git a/src/hooks/test.js b/src/hooks/test.js index d07a8906..379eebe6 100644 --- a/src/hooks/test.js +++ b/src/hooks/test.js @@ -69,7 +69,7 @@ export const useUpdateTestProgressKey = () => { testDataPath, ]); React.useEffect(() => { - if (!testDirty) { + if (testDataPath && !testDirty) { console.log({ testDirty, testProgressKey }); queryClient.invalidateQueries({ queryKey: [queryKeys.pageData] }); console.log("invalidated"); diff --git a/src/index.jsx b/src/index.jsx index cc119158..58ac232b 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -50,18 +50,3 @@ subscribe(APP_INIT_ERROR, (error) => { initialize({ messages, }); - -const resizeEvent = () => { - const height = document.body.scrollHeight; - if (document.referrer !== '') { - window.parent.postMessage( - { - type: 'ora-resize', - payload: { height }, - }, - document.referrer, - ); - } -}; -window.onload = () => resizeEvent(); -window.onresize = () => resizeEvent(); diff --git a/src/views/AssessmentView/PeerAssessmentView/index.jsx b/src/views/AssessmentView/PeerAssessmentView/index.jsx index 16f661fb..ea3ca4b6 100644 --- a/src/views/AssessmentView/PeerAssessmentView/index.jsx +++ b/src/views/AssessmentView/PeerAssessmentView/index.jsx @@ -1,9 +1,10 @@ import React from 'react'; - import { useIsORAConfigLoaded, usePrompts, + useResponse, + useSetResponse, useResponseData, } from 'hooks/app'; @@ -11,13 +12,14 @@ import Prompt from 'components/Prompt'; import TextResponse from 'components/TextResponse'; import FileUpload from 'components/FileUpload'; import BaseAssessmentView from '../BaseAssessmentView'; +import useAssessmentData from './useAssessmentData'; export const PeerAssessmentView = () => { - const prompts = usePrompts(); - const response = useResponseData(); - if (!useIsORAConfigLoaded()) { + const { prompts, response, isLoaded } = useAssessmentData(); + if (!isLoaded || !response) { return null; } + return ( {}}>
diff --git a/src/views/AssessmentView/SelfAssessmentView/index.jsx b/src/views/AssessmentView/SelfAssessmentView/index.jsx index f476b367..d509883d 100644 --- a/src/views/AssessmentView/SelfAssessmentView/index.jsx +++ b/src/views/AssessmentView/SelfAssessmentView/index.jsx @@ -3,7 +3,7 @@ import React from 'react'; import { useIsORAConfigLoaded, usePrompts, - useResponseData, + useResponse, } from 'hooks/app'; import FileUpload from 'components/FileUpload'; @@ -15,7 +15,7 @@ import BaseAssessmentView from '../BaseAssessmentView'; export const SelfAssessmentView = () => { const prompts = usePrompts(); - const response = useResponseData(); + const response = useResponse(); if (!useIsORAConfigLoaded()) { return null; } diff --git a/src/views/AssessmentView/StudentTrainingView/index.jsx b/src/views/AssessmentView/StudentTrainingView/index.jsx index 8992c083..6a2b230f 100644 --- a/src/views/AssessmentView/StudentTrainingView/index.jsx +++ b/src/views/AssessmentView/StudentTrainingView/index.jsx @@ -3,7 +3,7 @@ import React from 'react'; import { useIsORAConfigLoaded, usePrompts, - useResponseData, + useResponse, } from 'hooks/app'; import Prompt from 'components/Prompt'; @@ -15,7 +15,7 @@ import BaseAssessmentView from '../BaseAssessmentView'; export const StudentTrainingView = () => { const prompts = usePrompts(); - const response = useResponseData(); + const response = useResponse(); console.log("StudentTrainingView"); if (!useIsORAConfigLoaded()) { return null; diff --git a/src/views/AssessmentView/index.jsx b/src/views/AssessmentView/index.jsx new file mode 100644 index 00000000..ff40ee38 --- /dev/null +++ b/src/views/AssessmentView/index.jsx @@ -0,0 +1,33 @@ +import React from 'react'; + +import Prompt from 'components/Prompt'; +import TextResponse from 'components/TextResponse'; +import FileUpload from 'components/FileUpload'; + +import BaseAssessmentView from './BaseAssessmentView'; +import useAssessmentData from './useAssessmentData'; + +export const AssessmentView = () => { + const { prompts, response, isLoaded } = useAssessmentData(); + if (!isLoaded || !response) { + return null; + } + + return ( + {}}> +
+ {React.Children.toArray( + prompts.map((prompt, index) => ( +
+ + +
+ )), + )} + +
+
+ ); +}; + +export default AssessmentView; diff --git a/src/views/AssessmentView/useAssessmentData.js b/src/views/AssessmentView/useAssessmentData.js new file mode 100644 index 00000000..17cf1822 --- /dev/null +++ b/src/views/AssessmentView/useAssessmentData.js @@ -0,0 +1,43 @@ +import React from 'react'; +import { StrictDict, useKeyedState } from '@edx/react-unit-test-utils'; +import { + useIsORAConfigLoaded, + useIsPageDataLoaded, + usePageDataStatus, + usePrompts, + useResponse, + useSetResponse, + useResponseData, +} from 'hooks/app'; + +const stateKeys = StrictDict({ + initialized: 'initialized', +}); + +const useAssessmentData = () => { + const [initialized, setInitialized] = useKeyedState(stateKeys.initialized, false); + const prompts = usePrompts(); + const response = useResponse(); + const responseData = useResponseData(); + const setResponse = useSetResponse(); + const isLoaded = useIsORAConfigLoaded(); + const isPageDataLoaded = useIsPageDataLoaded(); + console.log({ pageDataStatus: usePageDataStatus() }); + React.useEffect(() => { + console.log("useAssessmentView useEffect"); + if (!initialized && isLoaded && isPageDataLoaded) { + setResponse(responseData); + setInitialized(true); + } + if (initialized && responseData && response !== responseData) { + setResponse(responseData); + } + }, [responseData, initialized]); + return { + isLoaded, + response, + prompts, + }; +}; + +export default useAssessmentData; diff --git a/src/views/SubmissionView/hooks/useSubmissionViewData.js b/src/views/SubmissionView/hooks/useSubmissionViewData.js index acf41621..b1248a1e 100644 --- a/src/views/SubmissionView/hooks/useSubmissionViewData.js +++ b/src/views/SubmissionView/hooks/useSubmissionViewData.js @@ -1,4 +1,6 @@ -import { useCallback } from 'react'; +import { useCallback, useEffect } from 'react'; + +import { StrictDict, useKeyedState } from '@edx/react-unit-test-utils'; import { useGlobalState, @@ -6,26 +8,42 @@ import { useSubmitResponse, useSetHasSubmitted, useHasSubmitted, + useRefreshPageData, + useSetResponse, + useResponse, } from 'hooks/app'; +import { + useRefreshUpstream, +} from 'hooks/modal'; import { stepStates, stepNames } from 'constants'; import useTextResponsesData from './useTextResponsesData'; import useUploadedFilesData from './useUploadedFilesData'; +const stateKeys = StrictDict({ + hasSavedDraft: 'hasSavedDraft', +}); + const useSubmissionViewData = () => { + const [hasSavedDraft, setHasSavedDraft] = useKeyedState(stateKeys.hasSavedDraft, false); const hasSubmitted = useHasSubmitted(); const setHasSubmitted = useSetHasSubmitted(); const submitResponseMutation = useSubmitResponse(); const rubricConfig = useRubricConfig(); const globalState = useGlobalState({ step: stepNames.submission }); + const refreshPageData = useRefreshPageData(); + const setResponse = useSetResponse(); + const response = useResponse(); + const refreshUpstream = useRefreshUpstream(); const stepState = hasSubmitted ? stepStates.submitted : globalState.stepState; const { textResponses, onUpdateTextResponse, - isDraftSaved, saveResponse, saveResponseStatus, + finishLater, + finishLaterStatus, } = useTextResponsesData(); const { uploadedFiles, @@ -38,23 +56,42 @@ const useSubmissionViewData = () => { textResponses, uploadedFiles, }).then(() => { + console.log("submitResponseMutation.then"); + setResponse({ textResponses, uploadedFiles }); setHasSubmitted(true); + refreshPageData(); + refreshUpstream(); }); }, [setHasSubmitted, submitResponseMutation, textResponses, uploadedFiles]); + useEffect(() => { + if (!hasSubmitted) { + const timer = setTimeout(() => { + saveResponse(); + if (!hasSavedDraft) { + setHasSavedDraft(true); + } + }, 5000); + return () => clearTimeout(timer); + } + }, [saveResponse, hasSubmitted]); + return { actionOptions: { + finishLater, + finishLaterStatus, saveResponse, saveResponseStatus, submit: submitResponseHandler, submitStatus: submitResponseMutation.status, hasSubmitted, }, + response: hasSubmitted + ? response + : { textResponses, uploadedFiles }, hasSubmitted, - textResponses, onUpdateTextResponse, - isDraftSaved, - uploadedFiles, + isDraftSaved: hasSavedDraft, onDeletedFile, onFileUploaded, showRubric: rubricConfig.showDuringResponse, diff --git a/src/views/SubmissionView/hooks/useTextResponsesData.js b/src/views/SubmissionView/hooks/useTextResponsesData.js index 462a3552..6b85046d 100644 --- a/src/views/SubmissionView/hooks/useTextResponsesData.js +++ b/src/views/SubmissionView/hooks/useTextResponsesData.js @@ -1,7 +1,7 @@ import { useCallback } from 'react'; import { StrictDict, useKeyedState } from '@edx/react-unit-test-utils'; -import { useSaveResponse, useTextResponses } from 'hooks/app'; +import { useFinishLater, useSaveDraftResponse, useTextResponses } from 'hooks/app'; import { MutationStatus } from 'constants'; export const stateKeys = StrictDict({ @@ -15,13 +15,19 @@ const useTextResponsesData = () => { const [isDirty, setIsDirty] = useKeyedState(stateKeys.isDirty, false); const [value, setValue] = useKeyedState(stateKeys.textResponses, textResponses); - const saveResponseMutation = useSaveResponse(); + const saveResponseMutation = useSaveDraftResponse(); + const finishLaterMutation = useFinishLater(); const saveResponse = useCallback(() => { setIsDirty(false); return saveResponseMutation.mutateAsync({ textResponses: value }); }, [setIsDirty, saveResponseMutation, value]); + const finishLater = useCallback(() => { + setIsDirty(false); + return finishLaterMutation.mutateAsync({ textResponses: value }); + }, [setIsDirty, finishLaterMutation, value]); + const onChange = useCallback((index) => (textResponse) => { setValue(oldResponses => { const out = [...oldResponses]; @@ -37,6 +43,8 @@ const useTextResponsesData = () => { isDraftSaved: saveResponseMutation.status === MutationStatus.success && !isDirty, saveResponse, saveResponseStatus: saveResponseMutation.status, + finishLater, + finishLaterStatus: finishLaterMutation.status, }; }; diff --git a/src/views/SubmissionView/hooks/useUploadedFilesData.js b/src/views/SubmissionView/hooks/useUploadedFilesData.js index cde4fce0..163362c2 100644 --- a/src/views/SubmissionView/hooks/useUploadedFilesData.js +++ b/src/views/SubmissionView/hooks/useUploadedFilesData.js @@ -22,6 +22,7 @@ const useUploadedFilesData = () => { ); const onFileUploaded = useCallback(async (data) => { + console.log({ onFileUploaded: { data } }); // const { fileData, queryClient } = data; const uploadResponse = await uploadFilesMutation.mutateAsync(data); if (uploadResponse) { diff --git a/src/views/SubmissionView/index.jsx b/src/views/SubmissionView/index.jsx index 54ecbcec..168cbfb0 100644 --- a/src/views/SubmissionView/index.jsx +++ b/src/views/SubmissionView/index.jsx @@ -24,10 +24,12 @@ export const SubmissionView = () => { const { actionOptions, showRubric, - textResponses, + response: { + textResponses, + uploadedFiles, + }, onUpdateTextResponse, isDraftSaved, - uploadedFiles, onDeletedFile, onFileUploaded, isReadOnly,