- Note: you can only have either Key Highlights+Hero button or a Hero Dropdown
+
+ Note: you can only have either Key Highlights+Hero button or a Hero Dropdown
{dropdown
diff --git a/src/layouts/EditContactUs.jsx b/src/layouts/EditContactUs.jsx
index fc0cee893..c34a68dfd 100644
--- a/src/layouts/EditContactUs.jsx
+++ b/src/layouts/EditContactUs.jsx
@@ -1,4 +1,3 @@
-// TODO: Error handling and validation (csp check)
// TODO: Clean up formatting, semi-colons, PropTypes etc
import React, { Component } from 'react';
import axios from 'axios';
@@ -27,6 +26,7 @@ import TemplateLocationsSection from '../templates/contact-us/LocationsSection'
import TemplateContactsSection from '../templates/contact-us/ContactsSection'
import TemplateFeedbackSection from '../templates/contact-us/FeedbackSection';
+import DeleteWarningModal from '../components/DeleteWarningModal';
/* eslint-disable react/jsx-props-no-spreading */
/* eslint-disable react/no-array-index-key */
@@ -62,9 +62,9 @@ const LocationSectionConstructor = (operatingHoursLength) => ({
const enumSection = (type, args) => {
switch (type) {
- case 'contact':
+ case 'contacts':
return ContactSectionConstructor();
- case 'location':
+ case 'locations':
return LocationSectionConstructor(args?.operatingHoursLength);
case 'contact_field':
return ContactFieldConstructor();
@@ -77,28 +77,38 @@ export default class EditContactUs extends Component {
constructor(props) {
super(props);
this.scrollRefs = {
- header: null,
- feedback: null,
- contact: null,
- location: null,
+ sectionsScrollRefs: {
+ locations: null,
+ contacts: null,
+ header: null,
+ feedback: null,
+ },
+ contacts: [],
+ locations: [],
};
this.state = {
frontMatter: {},
+ originalFrontMatter: {},
frontMatterSha: null,
footerContent: {},
+ originalFooterContent: {},
footerSha: null,
displaySections: {
sectionsDisplay: {
- location: false,
- contact: false,
+ locations: false,
+ contacts: false,
},
- contactCardsDisplay: [],
- locationCardsDisplay: [],
+ contacts: [],
+ locations: [],
},
errors: {
contacts: [],
locations: [],
- }
+ },
+ itemPendingForDelete: {
+ id: null,
+ type: '',
+ },
};
}
@@ -121,37 +131,51 @@ export default class EditContactUs extends Component {
const { contacts, locations } = sanitisedFrontMatter
- const contactsErrors = []
- const locationsErrors = []
- const contactCardsDisplay = []
- const locationCardsDisplay = []
+ const contactsErrors = [], locationsErrors = []
+ const contactsDisplay = [], locationsDisplay = []
+ const contactsScrollRefs = [], locationsScrollRefs = []
+
const sectionsDisplay = {
- contact: false,
- location:false
+ contacts: false,
+ locations: false
}
+ const sectionsScrollRefs = {
+ header: React.createRef(),
+ feedback: React.createRef(),
+ contacts: React.createRef(),
+ locations: React.createRef(),
+ }
+
contacts.forEach(_ => {
- contactsErrors.push(enumSection('contact'))
- contactCardsDisplay.push(false)
+ contactsErrors.push(enumSection('contacts'))
+ contactsDisplay.push(false)
+ contactsScrollRefs.push(React.createRef())
})
locations.forEach(location => {
- const args = {
- operatingHoursLength: location.operating_hours.length
- }
- locationsErrors.push(enumSection('location', args))
- locationCardsDisplay.push(false)
+ locationsErrors.push(enumSection('locations', { operatingHoursLength: location.operating_hours.length }))
+ locationsDisplay.push(false)
+ locationsScrollRefs.push(React.createRef())
})
+ this.scrollRefs = {
+ sectionsScrollRefs,
+ contacts: contactsScrollRefs,
+ locations: locationsScrollRefs,
+ }
+
this.setState({
+ originalFooterContent: _.cloneDeep(footerContent),
footerContent,
footerSha,
+ originalFrontMatter: _.cloneDeep(frontMatter),
frontMatter: sanitisedFrontMatter,
frontMatterSha: sha,
displaySections: {
sectionsDisplay,
- contactCardsDisplay,
- locationCardsDisplay,
+ contacts: contactsDisplay,
+ locations: locationsDisplay,
},
errors: {
contacts: contactsErrors,
@@ -164,8 +188,9 @@ export default class EditContactUs extends Component {
}
onDragEnd = (result) => {
+ const { scrollRefs, state } = this;
const { source, destination, type } = result;
- const { frontMatter, displaySections, errors } = this.state;
+ const { frontMatter, displaySections, errors } = state;
// If the user dropped the draggable to no known droppable
if (!destination) return;
@@ -176,72 +201,47 @@ export default class EditContactUs extends Component {
&& destination.index === source.index
) return;
- let newFrontMatter, newErrors, newDisplaySections;
+ const elem = frontMatter[type][source.index];
+ const elemError = errors[type][source.index];
+ const elemDisplay = displaySections[type][source.index];
+ const elemScrollRef = scrollRefs[type][source.index];
- switch (type) {
- case 'contact': {
- const elem = frontMatter.contacts[source.index];
- newFrontMatter = update(frontMatter, {
- contacts: {
- $splice: [
- [source.index, 1], // Remove elem from its original position
- [destination.index, 0, elem], // Splice elem into its new position
- ],
- },
- });
- const elemError = errors.contacts[source.index];
- newErrors = update(errors, {
- contacts: {
- $splice: [
- [source.index, 1], // Remove elem from its original position
- [destination.index, 0, elemError], // Splice elem into its new position
- ],
- },
- });
- const elemDisplay = displaySections.contactCardsDisplay[source.index];
- newDisplaySections = update(displaySections, {
- contactCardsDisplay: {
- $splice: [
- [source.index, 1],
- [destination.index, 0, elemDisplay],
- ],
- },
- });
- break;
- }
- case 'location': {
- const elem = frontMatter.locations[source.index];
- newFrontMatter = update(frontMatter, {
- locations: {
- $splice: [
- [source.index, 1], // Remove elem from its original position
- [destination.index, 0, elem], // Splice elem into its new position
- ],
- },
- });
- const elemError = errors.locations[source.index];
- newErrors = update(errors, {
- locations: {
- $splice: [
- [source.index, 1], // Remove elem from its original position
- [destination.index, 0, elemError], // Splice elem into its new position
- ],
- },
- });
- const elemDisplay = displaySections.locationCardsDisplay[source.index];
- newDisplaySections = update(displaySections, {
- locationCardsDisplay: {
- $splice: [
- [source.index, 1],
- [destination.index, 0, elemDisplay],
- ],
- },
- });
- break;
- }
- default: {
- }
- }
+ const newFrontMatter = update(frontMatter, {
+ [type]: {
+ $splice: [
+ [source.index, 1], // Remove elem from its original position
+ [destination.index, 0, elem], // Splice elem into its new position
+ ],
+ },
+ });
+ const newErrors = update(errors, {
+ [type]: {
+ $splice: [
+ [source.index, 1], // Remove elem from its original position
+ [destination.index, 0, elemError], // Splice elem into its new position
+ ],
+ },
+ });
+ const newDisplaySections = update(displaySections, {
+ [type]: {
+ $splice: [
+ [source.index, 1],
+ [destination.index, 0, elemDisplay],
+ ],
+ },
+ });
+ const newScrollRefs = update(scrollRefs, {
+ [type]: {
+ $splice: [
+ [source.index, 1],
+ [destination.index, 0, elemScrollRef],
+ ],
+ },
+ })
+
+ // scroll to new location of dragged element
+ this.scrollRefs[type][destination.index].current.scrollIntoView()
+ this.scrollRefs = newScrollRefs
this.setState({
frontMatter: newFrontMatter,
@@ -252,7 +252,7 @@ export default class EditContactUs extends Component {
onFieldChange = async (event) => {
try {
- const { state } = this;
+ const { scrollRefs, state } = this;
const { frontMatter, footerContent } = state
const { errors } = state;
const { id, value } = event.target;
@@ -265,6 +265,7 @@ export default class EditContactUs extends Component {
newFooterContent = update(footerContent, {
[elemType]: {$set: value},
});
+ scrollRefs.sectionsScrollRefs[elemType].current.scrollIntoView();
break;
}
case 'header': {
@@ -273,9 +274,10 @@ export default class EditContactUs extends Component {
newFrontMatter = update(frontMatter, {
[field]: {$set: value},
});
+ scrollRefs.sectionsScrollRefs[elemType].current.scrollIntoView();
break;
}
- case 'contact': {
+ case 'contacts': {
const contactIndex = parseInt(idArray[1], RADIX_PARSE_INT);
const contactType = idArray[2];
const contentIndex = parseInt(idArray[3], RADIX_PARSE_INT);
@@ -283,25 +285,25 @@ export default class EditContactUs extends Component {
switch (contactType) {
case 'title':
newFrontMatter = update(frontMatter, {
- contacts: {[contactIndex]: {[contactType]: {$set: value }}},
+ [elemType]: {[contactIndex]: {[contactType]: {$set: value }}},
});
newErrors = update(errors, {
- contacts: {[contactIndex]: {[contactType]: {$set: validateContact(contactType, value)}}}
+ [elemType]: {[contactIndex]: {[contactType]: {$set: validateContact(contactType, value)}}}
})
break;
default: // 'phone', 'email', 'other'
newFrontMatter = update(frontMatter, {
- contacts: {[contactIndex]: {content : {[contentIndex]: {[contactType]: {$set: value } }}}},
+ [elemType]: {[contactIndex]: {content : {[contentIndex]: {[contactType]: {$set: value } }}}},
});
newErrors = update(errors, {
- contacts: {[contactIndex]: {content : {[contentIndex]: {[contactType]: {$set: validateContact(contactType, value) } }}}},
+ [elemType]: {[contactIndex]: {content : {[contentIndex]: {[contactType]: {$set: validateContact(contactType, value) } }}}},
});
break;
}
+ scrollRefs[elemType][contactIndex].current.scrollIntoView();
break;
}
- case 'location': {
- const { locations } = state.frontMatter;
+ case 'locations': {
const locationIndex = parseInt(idArray[1], RADIX_PARSE_INT);
const locationType = idArray[2]; // e.g. "title" or "address"
const fieldIndex = parseInt(idArray[3], RADIX_PARSE_INT);
@@ -310,7 +312,7 @@ export default class EditContactUs extends Component {
switch (locationType) {
case 'operating_hours':
newFrontMatter = update(frontMatter, {
- locations: {[locationIndex]: {[locationType]: {[fieldIndex] : {[fieldType]: { $set: value }}}}},
+ [elemType]: {[locationIndex]: {[locationType]: {[fieldIndex] : {[fieldType]: { $set: value }}}}},
});
newErrors = update(errors, {
locations: {[locationIndex]: {[locationType]: {[fieldIndex] : {[fieldType]: { $set: validateLocation(fieldType, value) }}}}},
@@ -318,39 +320,40 @@ export default class EditContactUs extends Component {
break;
case 'add_operating_hours':
newFrontMatter = update(frontMatter, {
- locations: {[locationIndex]: {operating_hours : {$push: [enumSection('location_hours_field')]}}},
+ [elemType]: {[locationIndex]: {operating_hours : {$push: [enumSection('location_hours_field')]}}},
});
newErrors = update(errors, {
- locations: {[locationIndex]: {operating_hours : {$push: [enumSection('location_hours_field')]}}},
+ [elemType]: {[locationIndex]: {operating_hours : {$push: [enumSection('location_hours_field')]}}},
});
break;
case 'remove_operating_hours':
newFrontMatter = update(frontMatter, {
- locations: {[locationIndex]: {operating_hours : {$splice: [[fieldIndex,1]]}}}
+ [elemType]: {[locationIndex]: {operating_hours : {$splice: [[fieldIndex,1]]}}}
});
newErrors = update(errors, {
- locations: {[locationIndex]: {operating_hours : {$splice: [[fieldIndex,1]]}}}
+ [elemType]: {[locationIndex]: {operating_hours : {$splice: [[fieldIndex,1]]}}}
});
break;
case 'address':
newFrontMatter = update(frontMatter, {
- locations: {[locationIndex]: {[locationType]: {[fieldIndex] : { $set: value }}}},
+ [elemType]: {[locationIndex]: {[locationType]: {[fieldIndex] : { $set: value }}}},
});
// for address, we validate all address fields together, not the single field
const addressFields = newFrontMatter.locations[locationIndex][locationType]
newErrors = update(errors, {
- locations: {[locationIndex]: {[locationType]: { $set: validateLocation(locationType, addressFields) }}},
+ [elemType]: {[locationIndex]: {[locationType]: { $set: validateLocation(locationType, addressFields) }}},
});
break;
default:
newFrontMatter = update(frontMatter, {
- locations: {[locationIndex]: {[locationType]: { $set: value }}},
+ [elemType]: {[locationIndex]: {[locationType]: { $set: value }}},
});
newErrors = update(errors, {
- locations: {[locationIndex]: {[locationType]: { $set: validateLocation(locationType, value) }}},
+ [elemType]: {[locationIndex]: {[locationType]: { $set: validateLocation(locationType, value) }}},
});
break;
}
+ scrollRefs[elemType][locationIndex].current.scrollIntoView();
break;
}
}
@@ -359,7 +362,6 @@ export default class EditContactUs extends Component {
footerContent: _.isUndefined(newFooterContent) ? currState.footerContent : newFooterContent,
errors: _.isUndefined(newErrors) ? currState.errors : newErrors,
}));
- this.scrollRefs[elemType].scrollIntoView()
} catch (err) {
console.log(err);
@@ -369,89 +371,72 @@ export default class EditContactUs extends Component {
createHandler = async (event) => {
const { id } = event.target;
try {
+ const { scrollRefs, state } = this;
+ const { frontMatter, displaySections, errors } = state;
- const { frontMatter, displaySections, errors } = this.state;
-
- let newFrontMatter, newDisplaySections, newErrors;
- switch (id) {
- case 'contact': {
- newFrontMatter = update(frontMatter, {
- contacts: {$push: [enumSection('contact')]},
- });
- newErrors = update(errors, {
- contacts: {$push: [enumSection('contact')]},
- })
- newDisplaySections = update(displaySections, {
- contactCardsDisplay: {$push: [true]},
- });
- break;
- }
- case 'location': {
- newFrontMatter = update(frontMatter, {
- locations: {$push: [enumSection('location')]},
- });
- newErrors = update(errors, {
- locations: {$push: [enumSection('location')]},
- })
- newDisplaySections = update(displaySections, {
- locationCardsDisplay: {$push: [true]},
- });
- break;
- }
+ const newFrontMatter = update(frontMatter, {
+ [id]: {$push: [enumSection(id)]},
+ });
+ const newErrors = update(errors, {
+ [id]: {$push: [enumSection(id)]},
+ })
+ const newDisplaySections = update(displaySections, {
+ [id]: {$push: [true]},
+ });
+ const newScrollRefs = update(scrollRefs, {
+ [id]: {$push: [React.createRef()]},
+ });
+
+ if (scrollRefs[id].length) {
+ // Scroll to an approximation of where the new field will be based on the current last field, calibrated from the bottom of page
+ _.last(scrollRefs[id]).current.scrollIntoView()
+ } else {
+ scrollRefs.sectionsScrollRefs[id].current.scrollIntoView()
}
+
+ this.scrollRefs = newScrollRefs;
+
this.setState({
frontMatter: newFrontMatter,
errors: newErrors,
displaySections: newDisplaySections,
});
- this.scrollRefs[id].scrollIntoView()
+
} catch (err) {
console.log(err);
}
}
- deleteHandler = async (event) => {
- const { id } = event.target
+ deleteHandler = async (id) => {
try {
+ const { scrollRefs, state } = this;
+ const { frontMatter, displaySections, errors } = state;
+
const idArray = id.split('-');
const elemType = idArray[0];
const sectionIndex = parseInt(idArray[1], RADIX_PARSE_INT);
- const { frontMatter, displaySections, errors } = this.state;
- let newFrontMatter, newDisplaySections, newErrors;
+ const newFrontMatter = update(frontMatter, {
+ [elemType]: {$splice: [[sectionIndex, 1]]},
+ });
+ const newErrors = update(errors, {
+ [elemType]: {$splice: [[sectionIndex, 1]]},
+ });
+ const newDisplaySections = update(displaySections, {
+ [elemType]: {$splice: [[sectionIndex, 1]]},
+ });
+ const newScrollRefs = update(scrollRefs, {
+ [elemType]: {$splice: [[sectionIndex, 1]]},
+ });
+
+ this.scrollRefs = newScrollRefs;
- switch (elemType) {
- case 'contact': { // fix
- newFrontMatter = update(frontMatter, {
- contacts: {$splice: [[sectionIndex, 1]]},
- });
- newErrors = update(errors, {
- contacts: {$splice: [[sectionIndex, 1]]},
- });
- newDisplaySections = update(displaySections, {
- contactCardsDisplay: {$splice: [[sectionIndex, 1]]},
- });
- break;
- }
- case 'location': {
- newFrontMatter = update(frontMatter, {
- locations: {$splice: [[sectionIndex, 1]]},
- });
- newErrors = update(errors, {
- locations: {$splice: [[sectionIndex, 1]]},
- });
- newDisplaySections = update(displaySections, {
- locationCardsDisplay: {$splice: [[sectionIndex, 1]]},
- });
- break;
- }
- }
this.setState({
frontMatter: newFrontMatter,
errors: newErrors,
displaySections: newDisplaySections,
});
- this.scrollRefs[elemType].scrollIntoView()
+
} catch (err) {
console.log(err);
}
@@ -459,50 +444,50 @@ export default class EditContactUs extends Component {
displayHandler = async (event) => {
try {
+ const { state, scrollRefs } = this;
+ const { displaySections } = state;
+ const { contacts: contactsDisplay, locations: locationsDisplay } = displaySections;
+
const { id } = event.target;
const idArray = id.split('-');
const elemType = idArray[0];
- const { displaySections } = this.state;
- const { sectionsDisplay, contactCardsDisplay, locationCardsDisplay } = displaySections;
const sectionIndex = parseInt(idArray[1], RADIX_PARSE_INT) || idArray[1];
- let newSectionDisplay = { contact: false, location: false }
- let newContactCardsDisplay = _.fill(Array(contactCardsDisplay.length), false);
- let newLocationCardsDisplay = _.fill(Array(locationCardsDisplay.length), false);
+ let resetDisplaySections = {
+ sectionsDisplay: {
+ contacts: false,
+ locations: false,
+ },
+ contacts: _.fill(Array(contactsDisplay.length), false),
+ locations: _.fill(Array(locationsDisplay.length), false),
+ }
let newDisplaySections;
switch (elemType) {
case 'section': {
- const currDisplayValue = sectionsDisplay[sectionIndex];
- newSectionDisplay[sectionIndex] = !currDisplayValue;
+ const currDisplayValue = displaySections.sectionsDisplay[sectionIndex];
+ resetDisplaySections.sectionsDisplay[sectionIndex] = !currDisplayValue;
newDisplaySections = update(displaySections, {
- sectionsDisplay: {$set: newSectionDisplay},
- contactCardsDisplay: {$set: newContactCardsDisplay},
- locationCardsDisplay: {$set: newLocationCardsDisplay},
+ $set : resetDisplaySections,
});
- this.scrollRefs[sectionIndex].scrollIntoView()
+ scrollRefs.sectionsScrollRefs[sectionIndex].current.scrollIntoView();
break;
}
- case 'contact': {
- const currDisplayValue = contactCardsDisplay[sectionIndex];
- newContactCardsDisplay[sectionIndex] = !currDisplayValue;
+ default: {
+ const currDisplayValue = displaySections[elemType][sectionIndex];
+ resetDisplaySections[elemType][sectionIndex] = !currDisplayValue;
newDisplaySections = update(displaySections, {
- contactCardsDisplay: {$set: newContactCardsDisplay},
+ [elemType]: {$set: resetDisplaySections[elemType]},
});
+ scrollRefs[elemType][sectionIndex].current.scrollIntoView();
break;
}
- case 'location': {
- const currDisplayValue = locationCardsDisplay[sectionIndex]
- newLocationCardsDisplay[sectionIndex] = !currDisplayValue;
- newDisplaySections = update(displaySections, {
- locationCardsDisplay: {$set: newLocationCardsDisplay},
- });
- break;
- }
}
+
this.setState({
displaySections: newDisplaySections,
});
+
} catch (err) {
console.log(err);
}
@@ -524,8 +509,6 @@ export default class EditContactUs extends Component {
newContacts.push(_.cloneDeep(contact))
}
})
- filteredFrontMatter.contacts = newContacts;
-
let newLocations = [];
state.frontMatter.locations.forEach((location) => {
if ( !isEmpty(location) ) {
@@ -540,7 +523,13 @@ export default class EditContactUs extends Component {
newLocations.push(newLocation);
}
})
+
+ filteredFrontMatter.contacts = newContacts;
filteredFrontMatter.locations = newLocations;
+
+ // If array is empty, delete the object
+ if (!filteredFrontMatter.contacts.length) delete filteredFrontMatter.contacts
+ if (!filteredFrontMatter.locations.length) delete filteredFrontMatter.locations
const content = concatFrontMatterMdBody(filteredFrontMatter, '');
const base64EncodedContent = Base64.encode(content);
@@ -554,7 +543,7 @@ export default class EditContactUs extends Component {
withCredentials: true,
});
- // Update settings
+ // // Update settings
let updatedFooterContents = _.cloneDeep(state.footerContent)
const footerParams = {
@@ -573,30 +562,45 @@ export default class EditContactUs extends Component {
}
render() {
+ const { state, scrollRefs } = this
const {
footerContent,
+ originalFooterContent,
frontMatter,
+ originalFrontMatter,
displaySections,
frontMatterSha,
footerSha,
errors,
- } = this.state;
+ itemPendingForDelete,
+ } = state;
const { match } = this.props;
const { siteName } = match.params;
const { agency_name: agencyName, contacts, locations } = frontMatter
const { feedback } = footerContent
- const { sectionsDisplay, contactCardsDisplay, locationCardsDisplay } = displaySections
-
- const hasContactErrors = !isEmpty(errors.contacts)
- const hasLocationErrors = !isEmpty(errors.locations)
+ const { sectionsDisplay } = displaySections
+ const { sectionsScrollRefs } = scrollRefs
- const hasErrors = hasContactErrors || hasLocationErrors;
+ const hasErrors = !isEmpty(errors.contacts) || !isEmpty(errors.locations);
+ const hasChanges = JSON.stringify(originalFrontMatter) === JSON.stringify(frontMatter) && JSON.stringify(footerContent) === JSON.stringify(originalFooterContent);
return (
<>
+ {
+ itemPendingForDelete.id
+ && (
+ this.setState({ itemPendingForDelete: { id: null, type: '' } })}
+ onDelete={() => { this.deleteHandler(itemPendingForDelete.id); this.setState({ itemPendingForDelete: { id: null, type: '' } }); }}
+ type={itemPendingForDelete.type}
+ />
+ )
+ }
@@ -627,53 +631,54 @@ export default class EditContactUs extends Component {
cards={locations}
onFieldChange={this.onFieldChange}
createHandler={this.createHandler}
- deleteHandler={this.deleteHandler}
- shouldDisplay={sectionsDisplay.location}
- displayCards={locationCardsDisplay}
- sectionType={'location'}
+ deleteHandler={(event, type) => this.setState({ itemPendingForDelete: { id: event.target.id, type } })}
+ shouldDisplay={sectionsDisplay.locations}
+ displayCards={displaySections.locations}
displayHandler={this.displayHandler}
errors={errors.locations}
+ sectionId={'locations'}
/>
this.setState({ itemPendingForDelete: { id: event.target.id, type } })}
+ shouldDisplay={sectionsDisplay.contacts}
+ displayCards={displaySections.contacts}
displayHandler={this.displayHandler}
errors={errors.contacts}
+ sectionId={'contacts'}
/>