From 14fa53c1b3e9d416bc59c7f359d927e9fcf8e672 Mon Sep 17 00:00:00 2001 From: Martin Krulis Date: Thu, 26 Apr 2018 18:47:25 +0200 Subject: [PATCH] Adding warning and confirm query to submit ref. solution dialog to prevent empty submission descriptions. --- .travis.yml | 2 +- .../SubmitSolution/SubmitSolution.js | 358 ++++++++++-------- src/components/forms/Confirm/Confirm.js | 6 +- src/locales/cs.json | 4 +- src/locales/en.json | 5 +- 5 files changed, 216 insertions(+), 159 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2197832ec..9ef0fde1c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,3 @@ language: node_js node_js: - - "node" + - "lts/*" diff --git a/src/components/Submissions/SubmitSolution/SubmitSolution.js b/src/components/Submissions/SubmitSolution/SubmitSolution.js index 052040dfc..5f520d1b8 100644 --- a/src/components/Submissions/SubmitSolution/SubmitSolution.js +++ b/src/components/Submissions/SubmitSolution/SubmitSolution.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { injectIntl, intlShape, defineMessages } from 'react-intl'; @@ -23,6 +23,8 @@ import { } from '../../icons'; import UploadContainer from '../../../containers/UploadContainer'; import UsersNameContainer from '../../../containers/UsersNameContainer'; +import Confirm from '../../forms/Confirm'; + import { createGetUploadedFiles } from '../../../redux/selectors/upload'; import { hasEntryPoint } from '../../../redux/selectors/submission'; @@ -49,6 +51,16 @@ const commonMessages = defineMessages({ defaultMessage: 'Select the point of entry (bootstrap file of your application):' }, + emptyNoteWarning: { + id: 'app.submitSolution.emptyNoteWarning', + defaultMessage: + 'The description is empty. Reference solutions are strongly encouraged to be labeled with relevant descriptions.' + }, + emptyNoteSubmitConfirm: { + id: 'app.submitSolution.emptyNoteSubmitConfirm', + defaultMessage: + 'The description is empty. Reference solutions are strongly encouraged to be labeled with relevant descriptions. Do you rellay wish to proceed with submit?' + }, resetForm: { id: 'generic.reset', defaultMessage: 'Reset' @@ -99,168 +111,206 @@ const referenceSolutionMessages = defineMessages({ } }); -const SubmitSolution = ({ - userId, - isOpen, - onClose, - onFilesChange, - reset, - uploadId, - canSubmit, - isSending, - isValidating, - hasFailed, - note = '', - attachedFiles, - presubmitEnvironments, - presubmitVariables, - selectedEnvironment, - changeRuntimeEnvironment, - selectedEntryPoint, - changeEntryPoint, - saveNote, - submitSolution, - isReferenceSolution, - messages, - intl: { formatMessage } -}) => - - - - {formatMessage(messages.title)} - - - -

- -

+class SubmitSolution extends Component { + _createSubmitButton = (btnProps = {}) => { + const { canSubmit, hasFailed, intl: { formatMessage } } = this.props; + return ( + + ); + }; - - - - - - - - {formatMessage(commonMessages.runtimeEnvironment)} - - {isValidating - ?

- - {formatMessage(commonMessages.validating)} -

- : !presubmitEnvironments - ?

- {formatMessage(commonMessages.uploadFilesFirst)} -

- : presubmitEnvironments.length > 0 - ? changeRuntimeEnvironment(e.target.value)} - componentClass="select" - defaultValue={selectedEnvironment} - > - {presubmitEnvironments.map(rte => - - )} - - :

- {formatMessage(commonMessages.noEnvironments)} -

} -
+ createSubmitButton = () => { + const { + isReferenceSolution, + note, + submitSolution, + intl: { formatMessage } + } = this.props; + return isReferenceSolution && note.trim().length === 0 + ? + {this._createSubmitButton()} + + : this._createSubmitButton({ + onClick: submitSolution + }); + }; - {Boolean( - !isValidating && - presubmitVariables && - presubmitVariables.length > 0 && - attachedFiles && - attachedFiles.length > 1 && - hasEntryPoint(presubmitVariables, selectedEnvironment) - ) && - - - {formatMessage(commonMessages.entryPoint)} - - changeEntryPoint(e.target.value)} - componentClass="select" - defaultValue={selectedEntryPoint} - > - {attachedFiles.map(item => item.name).sort().map(file => - - )} - - } + render() { + const { + userId, + isOpen, + onClose, + onFilesChange, + reset, + uploadId, + canSubmit, + isSending, + isValidating, + hasFailed, + note = '', + attachedFiles, + presubmitEnvironments, + presubmitVariables, + selectedEnvironment, + changeRuntimeEnvironment, + selectedEntryPoint, + changeEntryPoint, + saveNote, + isReferenceSolution, + messages, + intl: { formatMessage } + } = this.props; + + return ( + + + + {formatMessage(messages.title)} + + + +

+ +

-
+ + + + + + + + {formatMessage(commonMessages.runtimeEnvironment)} + + {isValidating + ?

+ + {formatMessage(commonMessages.validating)} +

+ : !presubmitEnvironments + ?

+ {formatMessage(commonMessages.uploadFilesFirst)} +

+ : presubmitEnvironments.length > 0 + ? + changeRuntimeEnvironment(e.target.value)} + componentClass="select" + defaultValue={selectedEnvironment} + > + {presubmitEnvironments.map(rte => + + )} + + :

+ {formatMessage(commonMessages.noEnvironments)} +

} +
- - - {formatMessage(messages.noteLabel)} - - saveNote(e.target.value)} - value={note} - type="text" - /> - - -
- {hasFailed && -

- {formatMessage(commonMessages.submissionRejected)} -

} -
- -
- {isSending && - } + {Boolean( + !isValidating && + presubmitVariables && + presubmitVariables.length > 0 && + attachedFiles && + attachedFiles.length > 1 && + hasEntryPoint(presubmitVariables, selectedEnvironment) + ) && + + + {formatMessage(commonMessages.entryPoint)} + + changeEntryPoint(e.target.value)} + componentClass="select" + defaultValue={selectedEntryPoint} + > + {attachedFiles.map(item => item.name).sort().map(file => + + )} + + } - {!isSending && - } +
- + + + {formatMessage(messages.noteLabel)} + + saveNote(e.target.value)} + value={note} + type="text" + /> + + {isReferenceSolution && + note.trim().length === 0 && +

+ {formatMessage(commonMessages.emptyNoteWarning)} +

} + + + {hasFailed && +

+ {formatMessage(commonMessages.submissionRejected)} +

} + + +
+ {isSending && + } - -
-
-
+ {!isSending && this.createSubmitButton()} - {!canSubmit && - - - {formatMessage(commonMessages.instructions)} - - } -
- ; + + +
+
+
+ + {!canSubmit && + + + {formatMessage(commonMessages.instructions)} + + } +
+
+ ); + } +} SubmitSolution.propTypes = { userId: PropTypes.string.isRequired, onClose: PropTypes.func.isRequired, diff --git a/src/components/forms/Confirm/Confirm.js b/src/components/forms/Confirm/Confirm.js index ef5c89a66..43bf8fa08 100644 --- a/src/components/forms/Confirm/Confirm.js +++ b/src/components/forms/Confirm/Confirm.js @@ -41,12 +41,13 @@ class Confirm extends Component { - ) + ), + placement = 'bottom' } = this.props; const { target, showPopup } = this.state; return ( - +
@@ -92,6 +93,7 @@ Confirm.propTypes = { question: stringOrFormattedMessage.isRequired, yes: stringOrFormattedMessage, no: stringOrFormattedMessage, + placement: PropTypes.string, children: PropTypes.element.isRequired, className: PropTypes.string }; diff --git a/src/locales/cs.json b/src/locales/cs.json index 992dad04e..175acf6d2 100644 --- a/src/locales/cs.json +++ b/src/locales/cs.json @@ -1016,6 +1016,8 @@ "app.submitRefSolution.title": "Vytvořit referenční řešení", "app.submitSolution.addFile": "Přidat soubor", "app.submitSolution.dragAndDrop": "Přetahujte soubory do tohoto pole.", + "app.submitSolution.emptyNoteSubmitConfirm": "Popis referenčního řešení je prázdný. Je nanejvýš vhodné, aby referenční řešení byla opatřena relevantním popiskem. Opravdu si přejete pokračovat v odevzdání?", + "app.submitSolution.emptyNoteWarning": "Popis referenčního řešení je prázdný. Je nanejvýš vhodné, aby referenční řešení byla opatřena relevantním popiskem.", "app.submitSolution.entryPoint": "Vyberte vstupní bod (zaváděcí soubor vaší aplikace):", "app.submitSolution.noEnvironments": "Nahrané soubory neodpovídají žádnému povolenému běhovému prostředí.", "app.submitSolution.noteLabel": "Poznámka pro vás a vašeho vyučujícího:", @@ -1095,4 +1097,4 @@ "recodex-judge-shuffle-all": "Sudí neuspořádaných tokenů a řádků", "recodex-judge-shuffle-newline": "Sudí neuspořádaných tokenů (ignorující konce řádků)", "recodex-judge-shuffle-rows": "Sudí neuspořádaných řádků" -} +} \ No newline at end of file diff --git a/src/locales/en.json b/src/locales/en.json index 281efac2f..6023da428 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1016,6 +1016,8 @@ "app.submitRefSolution.title": "Create Reference Solution", "app.submitSolution.addFile": "Add File(s)", "app.submitSolution.dragAndDrop": "Drag and drop files here.", + "app.submitSolution.emptyNoteSubmitConfirm": "The description is empty. Reference solutions are strongly encouraged to be labeled with relevant descriptions. Do you rellay wish to proceed with submit?", + "app.submitSolution.emptyNoteWarning": "The description is empty. Reference solutions are strongly encouraged to be labeled with relevant descriptions.", "app.submitSolution.entryPoint": "Select the point of entry (bootstrap file of your application):", "app.submitSolution.noEnvironments": "Uploaded files do not meet criteria of any allowed runtime environment.", "app.submitSolution.noteLabel": "Note for you and your supervisor(s):", @@ -1073,6 +1075,7 @@ "generic.edit": "Edit", "generic.loading": "Loading ...", "generic.name": "Name", + "generic.noRecordsInTable": "There are no records in the table.", "generic.reset": "Reset", "generic.runtimeShort": "Runtime/Language", "generic.runtimesShort": "Runtimes/Languages", @@ -1095,4 +1098,4 @@ "recodex-judge-shuffle-all": "Unordered-tokens-and-rows judge", "recodex-judge-shuffle-newline": "Unordered-tokens judge (ignoring ends of lines)", "recodex-judge-shuffle-rows": "Unordered-rows judge" -} +} \ No newline at end of file