Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bulk edit instructions and confirmation modal #2151

Merged
merged 4 commits into from
Nov 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -72,6 +74,15 @@ const prioritiesStep = {
viewBox: "0 0 100 125",
}

const instructionsStep = {
id: 'Instructions',
description: <FormattedMessage {...messages.instructionsStepDescription} />,
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, {
Expand All @@ -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',
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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;

Expand All @@ -46,6 +55,7 @@ export class EditChallenges extends Component {
isSaving: false,
expandedFieldGroups: {},
challengeNumberSaving: 0,
confirmModal: false,
};

validationPromise = null;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -302,65 +323,81 @@ export class EditChallenges extends Component {
};

return (
<BreadcrumbWrapper
{...this.props}
cancel={this.cancel}
isCloningChallenge={false}
isNewChallenge={false}
challengeState={this.challengeState}
>
<div className="mr-flex">
<div className="mr-p-4 md:mr-p-8 mr-w-full">
<Form
schema={activeStep.jsSchema(
this.props.intl,
this.props.user,
challengeData,
this.state.extraErrors,
{
longForm: true,
}
)}
uiSchema={activeStep.uiSchema(
this.props.intl,
this.props.user,
challengeData,
this.state.extraErrors,
{
longForm: true,
<>
<BreadcrumbWrapper
{...this.props}
cancel={this.cancel}
isCloningChallenge={false}
isNewChallenge={false}
challengeState={this.challengeState}
>
<div className="mr-flex">
<div className="mr-p-4 md:mr-p-8 mr-w-full">
<Form
schema={activeStep.jsSchema(
this.props.intl,
this.props.user,
challengeData,
this.state.extraErrors,
{
longForm: true,
}
)}
uiSchema={activeStep.uiSchema(
this.props.intl,
this.props.user,
challengeData,
this.state.extraErrors,
{
longForm: true,
}
)}
className="form"
validate={(formData, errors) =>
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}
>
</Form>
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}
>
</Form>
</div>
</div>
</div>
</BreadcrumbWrapper>
</BreadcrumbWrapper>
{
this.state.confirmModal
? <ConfirmationModal
formData={this.state.confirmModal}
submit={() => {
this.toggleConfirmModal(false)
this.handleSubmit(formData, nextStep)}
}
cancel={() => {
this.toggleConfirmModal(false)
}}
/>
: null
}
</>
);
}}
/>
Expand Down Expand Up @@ -444,6 +481,110 @@ const BreadcrumbWrapper = (props) => {
);
};

const confirmationMap = () => {
return [
{
id: 'additionalKeywords',
displayName: <FormattedMessage {...messages.additionalKeywordsLabel} />
},
{
id: 'taskTags',
displayName: <FormattedMessage {...messages.preferredTagsLabel} />
},
{
id: 'exportableProperties',
displayName: <FormattedMessage {...messages.exportablePropertiesLabel} />
},
{
id: 'customBasemap',
displayName: <FormattedMessage {...messages.customBasemapLabel} />
},
{
id: 'instruction',
displayName: <FormattedMessage {...messages.instructionLabel} />,
props: {
wrapLines: true,
lineProps: { style: { wordBreak: 'break-all', whiteSpace: 'pre-wrap'} }
}
},
{
id: 'defaultBasemapId',
displayName: <FormattedMessage {...messages.defaultBasemapLabel} />,
},
{
id: 'defaultPriority',
displayName: <FormattedMessage {...messages.defaultPriorityLabel} />,
},
{
id: 'dataOriginDate',
displayName: <FormattedMessage {...messages.dataOriginDateLabel} />,
},
{
id: 'highPriorityRule',
default: "{}",
json: true,
displayName: <FormattedMessage {...messages.highPriorityRulesLabel} />,
},
{
id: 'mediumPriorityRule',
default: "{}",
json: true,
displayName: <FormattedMessage {...messages.mediumPriorityRulesLabel} />,
},
{
id: 'lowPriorityRule',
default: "{}",
json: true,
displayName: <FormattedMessage {...messages.lowPriorityRulesLabel} />,
}
]
}

class ConfirmationModal extends Component {
render() {
const { formData } = this.props
return (
<External>
<Modal
fullScreen
isActive={true}
onClose={this.props.onClose}
>
<h1 style={{ marginBottom: 10 }}><FormattedMessage {...messages.reviewAndSubmitLabel} /></h1>
<div className="mr-text-red mr-text-lg" style={{ marginBottom: 14 }}><FormattedMessage {...messages.bulkEditWarningLabel} /></div>
{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 (
<div key={data.id} style={{ marginBottom: 10 }}>
<div>{data.displayName || data.id}</div>
<SyntaxHighlighter language="json" style={{ ...highlightColors }} {...data.props}>
{JSON.stringify(val, null, 4)}
</SyntaxHighlighter>
</div>
)
}
})}
<button
type="button"
className="mr-button mr-button--white mr-button--small mr-mr-8 mr-text-red mr-border-red"
onClick={() => this.props.submit()}
>
<FormattedMessage {...messages.submitChallenges} />
</button>
<button
type="button"
className="mr-button mr-button--white mr-button--small"
onClick={() => this.props.cancel()}
>
<FormattedMessage {...messages.cancelChallenges} />
</button>
</Modal>
</External>
)
}
}

export default WithCurrentUser(
WithCurrentProject(
(WithTaskPropertyStyleRules(
Expand Down
Loading
Loading