From 6bc33afde38574726b583591365fe67e0853ca9b Mon Sep 17 00:00:00 2001 From: jschwarz2030 <79289630+jschwarz2030@users.noreply.github.com> Date: Mon, 6 Nov 2023 11:42:39 -0600 Subject: [PATCH] Bulk edit instructions and confirmation modal (#2151) * add detailed instructions to bulk edit * add bulk confirmation page * transifex fixes * remove comment --- .../EditChallenge/BulkEditSteps.js | 16 ++ .../BulkSchemas/InstructionsSchema.js | 31 +++ .../EditChallenge/EditChallenges.js | 255 ++++++++++++++---- .../EditChallenge/Messages.js | 15 ++ src/components/ViewTask/ViewTask.js | 3 + src/lang/en-US.json | 3 + 6 files changed, 266 insertions(+), 57 deletions(-) create mode 100644 src/components/AdminPane/Manage/ManageChallenges/EditChallenge/BulkSchemas/InstructionsSchema.js diff --git a/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/BulkEditSteps.js b/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/BulkEditSteps.js index 4689f7dfb..25f199a08 100644 --- a/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/BulkEditSteps.js +++ b/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/BulkEditSteps.js @@ -14,6 +14,8 @@ import { jsSchema as basemapJsSchema, uiSchema as basemapUiSchema } from './BulkSchemas/BasemapSchema' import { jsSchema as dataSourceJsSchema, uiSchema as dataSourceUiSchema } from './BulkSchemas/DataSourceSchema' +import { jsSchema as instructionsJsSchema, + uiSchema as instructionsUiSchema } from './BulkSchemas/InstructionsSchema' import messages from './Messages' // Define individual workflow steps. Steps can be driven by either schemas or @@ -72,6 +74,15 @@ const prioritiesStep = { viewBox: "0 0 100 125", } +const instructionsStep = { + id: 'Instructions', + description: , + jsSchema: instructionsJsSchema, + uiSchema: instructionsUiSchema, + icon: "priority-icon", + viewBox: "0 0 100 125", +} + // String together workflow steps for creating a new challenge const bulkEditSteps = { 'Data Source': Object.assign({}, dataSourceStep, { @@ -84,6 +95,11 @@ const bulkEditSteps = { previous: 'AdvancedOptions', canFinish: true, }), + 'Instructions': Object.assign({}, instructionsStep, { + next: 'AdvancedOptions', + previous: 'AdvancedOptions', + canFinish: true, + }), 'Tags': Object.assign({}, tagsStep, { next: 'AdvancedOptions', previous: 'AdvancedOptions', diff --git a/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/BulkSchemas/InstructionsSchema.js b/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/BulkSchemas/InstructionsSchema.js new file mode 100644 index 000000000..625a054f7 --- /dev/null +++ b/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/BulkSchemas/InstructionsSchema.js @@ -0,0 +1,31 @@ +import messages from '../Messages' + +export const jsSchema = (intl) => { + const schemaFields = { + "$schema": "http://json-schema.org/draft-07/schema#", + type: "object", + properties: { + instruction: { + title: intl.formatMessage(messages.instructionLabel), + type: "string", + minLength: 150, + description: intl.formatMessage(messages.instructionsDescription), + }, + }, + } + + return schemaFields +} + +export const uiSchema = (intl) => { + const uiSchemaFields = { + "ui:order": [ "instruction" ], + instruction: { + "ui:field": "markdown", + "ui:help": intl.formatMessage(messages.instructionDescription), + "ui:previewNote": intl.formatMessage(messages.addMustachePreviewNote), + }, + } + + return uiSchemaFields +} diff --git a/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/EditChallenges.js b/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/EditChallenges.js index 5667f4c0a..1deb5b0b0 100644 --- a/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/EditChallenges.js +++ b/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/EditChallenges.js @@ -25,6 +25,11 @@ import WithChallengeManagement from "../../../HOCs/WithChallengeManagement/WithC import WithCurrentUser from "../../../../HOCs/WithCurrentUser/WithCurrentUser"; import WithTallied from "../../../HOCs/WithTallied/WithTallied"; import WithTaskPropertyStyleRules from "../../../HOCs/WithTaskPropertyStyleRules/WithTaskPropertyStyleRules"; +import External from "../../../../External/External"; +import Modal from "../../../../Modal/Modal"; +import { Light as SyntaxHighlighter } from 'react-syntax-highlighter' +import jsonLang from 'react-syntax-highlighter/dist/esm/languages/hljs/json' +import highlightColors from 'react-syntax-highlighter/dist/esm/styles/hljs/agate' import { ChallengeCategoryKeywords } from "../../../../../services/Challenge/ChallengeKeywords/ChallengeKeywords"; @@ -36,6 +41,10 @@ import manageMessages from "../../Messages"; import messages from "./Messages"; import "./EditChallenge.scss"; +SyntaxHighlighter.registerLanguage('json', jsonLang) + +highlightColors.hljs.background="rgba(0, 0, 0, 0.15)" + export class EditChallenges extends Component { challengeState = null; @@ -46,6 +55,7 @@ export class EditChallenges extends Component { isSaving: false, expandedFieldGroups: {}, challengeNumberSaving: 0, + confirmModal: false, }; validationPromise = null; @@ -83,6 +93,7 @@ export class EditChallenges extends Component { preferredTags: formData.preferredTags, exportableProperties: formData.exportableProperties, customBasemap: formData.customBasemap, + instruction: formData.instruction, defaultBasemap: formData.defaultBasemap, defaultBasemapId: formData.defaultBasemapId, defaultPriority: formData.defaultPriority, @@ -148,6 +159,16 @@ export class EditChallenges extends Component { return challengeData; }; + toggleConfirmModal = (bool) => { + if (bool) { + this.prepareFormDataForSaving().then(async (formData) => { + this.setState({ confirmModal: formData }) + }) + } else { + this.setState({ confirmModal: false }) + } + } + /** * Performs the reverse of prepareChallengeDataForForm, taking the form data * and massaging it back into the format of challenge data expected by the @@ -302,65 +323,81 @@ export class EditChallenges extends Component { }; return ( - -
-
-
+ +
+
+ + this.validate(formData, errors, activeStep) } - )} - className="form" - validate={(formData, errors) => - this.validate(formData, errors, activeStep) - } - transformErrors={this.transformErrors(this.props.intl)} - widgets={{ - SelectWidget: CustomSelectWidget, - TextWidget: CustomTextWidget, - }} - ArrayFieldTemplate={CustomArrayFieldTemplate} - FieldTemplate={CustomFieldTemplate} - fields={customFields} - tagType={"challenges"} - noHtml5Validate - showErrorList={false} - formData={challengeData} - formContext={_merge(this.state.formContext, { - bounding: _get(challengeData, "bounding"), - buttonAction: BoundsSelectorModal, - })} - onChange={this.changeHandler} - onSubmit={(formData) => - this.handleSubmit(formData, nextStep) - } - onError={() => null} - extraErrors={this.state.extraErrors} - > - + transformErrors={this.transformErrors(this.props.intl)} + widgets={{ + SelectWidget: CustomSelectWidget, + TextWidget: CustomTextWidget, + }} + ArrayFieldTemplate={CustomArrayFieldTemplate} + FieldTemplate={CustomFieldTemplate} + fields={customFields} + tagType={"challenges"} + noHtml5Validate + showErrorList={false} + formData={challengeData} + formContext={_merge(this.state.formContext, { + bounding: _get(challengeData, "bounding"), + buttonAction: BoundsSelectorModal, + })} + onChange={this.changeHandler} + onSubmit={(formData) => { + this.toggleConfirmModal(formData) + }} + onError={() => null} + extraErrors={this.state.extraErrors} + > + +
-
- + + { + this.state.confirmModal + ? { + this.toggleConfirmModal(false) + this.handleSubmit(formData, nextStep)} + } + cancel={() => { + this.toggleConfirmModal(false) + }} + /> + : null + } + ); }} /> @@ -444,6 +481,110 @@ const BreadcrumbWrapper = (props) => { ); }; +const confirmationMap = () => { + return [ + { + id: 'additionalKeywords', + displayName: + }, + { + id: 'taskTags', + displayName: + }, + { + id: 'exportableProperties', + displayName: + }, + { + id: 'customBasemap', + displayName: + }, + { + id: 'instruction', + displayName: , + props: { + wrapLines: true, + lineProps: { style: { wordBreak: 'break-all', whiteSpace: 'pre-wrap'} } + } + }, + { + id: 'defaultBasemapId', + displayName: , + }, + { + id: 'defaultPriority', + displayName: , + }, + { + id: 'dataOriginDate', + displayName: , + }, + { + id: 'highPriorityRule', + default: "{}", + json: true, + displayName: , + }, + { + id: 'mediumPriorityRule', + default: "{}", + json: true, + displayName: , + }, + { + id: 'lowPriorityRule', + default: "{}", + json: true, + displayName: , + } + ] +} + +class ConfirmationModal extends Component { + render() { + const { formData } = this.props + return ( + + +

+
+ {confirmationMap().map((data) => { + if (formData[data.id] && formData[data.id] !== data.default) { + const val = data.json ? JSON.parse(formData[data.id]) : formData[data.id] + return ( +
+
{data.displayName || data.id}
+ + {JSON.stringify(val, null, 4)} + +
+ ) + } + })} + + +
+
+ ) + } +} + export default WithCurrentUser( WithCurrentProject( (WithTaskPropertyStyleRules( diff --git a/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/Messages.js b/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/Messages.js index 80fda6abb..9ee63417d 100644 --- a/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/Messages.js +++ b/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/Messages.js @@ -29,6 +29,11 @@ export default defineMessages({ defaultMessage: "Cancel Edit", }, + submitChallenges: { + id: "Admin.EditChallenge.bulkSubmit.header", + defaultMessage: "Submit Bulk Edit", + }, + cloneChallenge: { id: "Admin.EditChallenge.clone.header", defaultMessage: "Clone", @@ -732,6 +737,16 @@ will not be able to make sense of it. defaultMessage: "Request Review By Default", }, + reviewAndSubmitLabel: { + id: "Admin.EditChallenge.form.reviewAndSubmit.label", + defaultMessage: "Review and Submit", + }, + + bulkEditWarningLabel: { + id: "Admin.EditChallenge.form.bulkSubmit.label", + defaultMessage: "The following changes will be applied to all selected challenges. This process is not reversible, so please review before submitting.", + }, + reviewSettingDescription: { id: "Admin.EditChallenge.form.reviewSetting.description", defaultMessage: diff --git a/src/components/ViewTask/ViewTask.js b/src/components/ViewTask/ViewTask.js index 72651d532..d35eca656 100644 --- a/src/components/ViewTask/ViewTask.js +++ b/src/components/ViewTask/ViewTask.js @@ -16,6 +16,9 @@ export default class ViewTask extends Component { return } + console.log(this.props.task.geometries) + console.log(JSON.stringify(this.props.task.geometries, null, 4)) + return (
diff --git a/src/lang/en-US.json b/src/lang/en-US.json index 45609065c..a0b320273 100644 --- a/src/lang/en-US.json +++ b/src/lang/en-US.json @@ -107,6 +107,7 @@ "Admin.ChallengeTaskMap.controls.inspectTask.label": "Inspect Task", "Admin.EditChallenge.bulkCancel.header": "Cancel Edit", "Admin.EditChallenge.bulkEdit.header": "Bulk Edit", + "Admin.EditChallenge.bulkSubmit.header": "Submit Bulk Edit", "Admin.EditChallenge.clone.header": "Clone", "Admin.EditChallenge.controls.cancel.label": "Abandon Changes", "Admin.EditChallenge.controls.cancelNewChallenge.label": "Abandon Challenge", @@ -118,6 +119,7 @@ "Admin.EditChallenge.form.additionalKeywords.label": "Additional Categorization Keywords", "Admin.EditChallenge.form.blurb.description": "A very brief description of your challenge suitable for small spaces, such as a map marker popup. This field supports [Markdown](https://learn.maproulette.org/documentation/markdown/).", "Admin.EditChallenge.form.blurb.label": "Blurb", + "Admin.EditChallenge.form.bulkSubmit.label": "The following changes will be applied to all selected challenges. This process is not reversible, so please review before submitting.", "Admin.EditChallenge.form.category.description": "Selecting an appropriate category for your challenge can help users discover challenges that match their interests. Choose the Other category if nothing seems appropriate.", "Admin.EditChallenge.form.category.label": "How should your Challenge be Categorized?", "Admin.EditChallenge.form.checkinComment.description": "Comment to be associated with changes made by users in editor", @@ -197,6 +199,7 @@ "Admin.EditChallenge.form.required.label": "is a required property", "Admin.EditChallenge.form.requiresLocal.description": "Tasks require local or on-the-ground knowledge to complete. Note: challenge will not appear in the Find Challenges list.", "Admin.EditChallenge.form.requiresLocal.label": "Requires Local Knowledge", + "Admin.EditChallenge.form.reviewAndSubmit.label": "Review and Submit", "Admin.EditChallenge.form.reviewSetting.description": "This will pre-check the users request review box when submitting a task.", "Admin.EditChallenge.form.reviewSetting.label": "Request Review By Default", "Admin.EditChallenge.form.source.label": "Location of your Task Data",