From 9ac545a6d435aa0e2d4a33b24d939ac7382c5de0 Mon Sep 17 00:00:00 2001 From: Matthew Northcott Date: Tue, 21 Feb 2023 16:58:20 +1300 Subject: [PATCH] [#40] Bulk PDF export - frontend changes --- frontend/src/assets/data/staticText.json | 4 + frontend/src/components/Stepper.jsx | 81 -------------- .../components/providers/FilterProvider.jsx | 37 +++++++ .../components/providers/StepperProvider.jsx | 100 ++++++++++++++++++ .../steps/address/AddressSearch.jsx | 21 ++-- .../components/steps/address/AddressStep.jsx | 32 ++++-- .../steps/complete/CompleteStep.jsx | 24 +++++ .../steps/habitat/HabitatSelector.jsx | 28 ++--- .../components/steps/habitat/HabitatStep.jsx | 19 ++-- .../steps/location/LocationStep.jsx | 19 ++-- .../src/components/steps/location/Map.jsx | 22 ++-- .../components/steps/results/ResultsStep.jsx | 22 ++-- .../steps/soilvariant/SoilSelector.jsx | 17 +-- .../components/steps/soilvariant/SoilStep.jsx | 20 ++-- .../specifics/ProjectSpecificsSelector.jsx | 9 -- .../steps/specifics/ProjectSpecificsStep.jsx | 24 ----- .../steps/summary/SummaryContent.jsx | 19 ++-- .../components/steps/summary/SummaryStep.jsx | 19 ++-- .../components/steps/zone/ZoneSelector.jsx | 20 ++-- .../src/components/steps/zone/ZoneStep.jsx | 32 ++++-- frontend/src/index.js | 9 +- frontend/src/pages/ApplyPage.jsx | 54 ++++------ frontend/src/pages/MainPage.jsx | 55 +++------- frontend/src/repository/Repository.js | 7 +- 24 files changed, 393 insertions(+), 301 deletions(-) delete mode 100644 frontend/src/components/Stepper.jsx create mode 100644 frontend/src/components/providers/FilterProvider.jsx create mode 100644 frontend/src/components/providers/StepperProvider.jsx create mode 100644 frontend/src/components/steps/complete/CompleteStep.jsx delete mode 100644 frontend/src/components/steps/specifics/ProjectSpecificsSelector.jsx delete mode 100644 frontend/src/components/steps/specifics/ProjectSpecificsStep.jsx diff --git a/frontend/src/assets/data/staticText.json b/frontend/src/assets/data/staticText.json index 56a12e9..e4fcf7b 100644 --- a/frontend/src/assets/data/staticText.json +++ b/frontend/src/assets/data/staticText.json @@ -35,6 +35,10 @@ "results": { "title": "Plant List Results", "forestDiagramDescription": "Forest Position Information Diagram" + }, + "complete": { + "title": "Application Complete", + "description": "You have completed your application and submitted your results. You may now return to the homepage or fill out another application for a different habitat or zone." } } } diff --git a/frontend/src/components/Stepper.jsx b/frontend/src/components/Stepper.jsx deleted file mode 100644 index 2bc1041..0000000 --- a/frontend/src/components/Stepper.jsx +++ /dev/null @@ -1,81 +0,0 @@ -import { useState } from 'react'; -import Box from '@mui/material/Box'; -import Stepper from '@mui/material/Stepper'; -import Step from '@mui/material/Step'; -import StepLabel from '@mui/material/StepLabel'; -import Button from '@mui/material/Button'; -import Tooltip from '@mui/material/Tooltip'; - -export default function StepperWizard({ steps }) { - const [filters, setFilters] = useState({}); - const [activeStep, setActiveStep] = useState(0); - const [nextDisabled, setNextDisabled] = useState(true); - const [redirectBack, setRedirectBack] = useState(false); - - const resetStepState = () => { - setNextDisabled(true); - setRedirectBack(false); - } - - const handleNext = () => { - if (redirectBack) { - setActiveStep((prevActiveStep) => prevActiveStep - 1); - } else { - setActiveStep((prevActiveStep) => prevActiveStep + 1); - } - resetStepState(); - }; - - const handleBack = () => { - setActiveStep((prevActiveStep) => prevActiveStep - 1); - resetStepState(); - }; - - const handleReset = () => { - setActiveStep(0); - resetStepState(); - setFilters({}); - }; - - const updateFilterState = (newFilters) => { - setFilters(f => ({...f, ...newFilters})); - }; - - let CurrentStep = activeStep >= steps.length ? steps[steps.length - 1].component : steps[activeStep].component; - - return ( - - - {steps.map((step) => { - return ( - - - {step.label} - - - ); - })} - - setFilters({})} - setNextDisabled={setNextDisabled} - setRedirectBack={setRedirectBack} - /> - - - - {activeStep === steps.length - 1 ? - : } - - - ); -} diff --git a/frontend/src/components/providers/FilterProvider.jsx b/frontend/src/components/providers/FilterProvider.jsx new file mode 100644 index 0000000..755b0d6 --- /dev/null +++ b/frontend/src/components/providers/FilterProvider.jsx @@ -0,0 +1,37 @@ +import { createContext, useState, useContext } from 'react'; +import Repository from '../../repository/Repository'; + +const FilterContext = createContext(null); + +const FilterProvider = ({children}) => { + const [filters, setFilters] = useState({}); + + const resetFilters = () => setFilters({}); + + const updateFilters = newFilters => setFilters(oldFilters => ({...oldFilters, ...newFilters})); + + const submit = async () => { + return await Repository.post("/questionnaire/", { + location: `SRID=4326;POINT (${filters.coordinates.lng} ${filters.coordinates.lat})`, + soil_variant: filters.soilVariant, + zone: filters.zone.id, + }); + }; + + const value = { + filters, + setFilters, + resetFilters, + updateFilters, + submit, + }; + + return {children}; +}; + +const useFilter = () => useContext(FilterContext); + +export { + FilterProvider, + useFilter, +}; diff --git a/frontend/src/components/providers/StepperProvider.jsx b/frontend/src/components/providers/StepperProvider.jsx new file mode 100644 index 0000000..8cb4e35 --- /dev/null +++ b/frontend/src/components/providers/StepperProvider.jsx @@ -0,0 +1,100 @@ +import { createContext, useState, useContext } from 'react'; +import Box from '@mui/material/Box'; +import Stepper from '@mui/material/Stepper'; +import Step from '@mui/material/Step'; +import StepLabel from '@mui/material/StepLabel'; +import Tooltip from '@mui/material/Tooltip'; +import Button from '@mui/material/Button'; +import { useFilter } from './FilterProvider'; + +const StepContext = createContext(null); + +const StepperWizard = ({children}) => { + const [step, setStep] = useState(0); + + const isStep = n => (0 <= n && n < children.length); + const setStepNext = () => setStep(n => isStep(n + 1) ? n + 1 : n); + const setStepBack = () => setStep(n => isStep(n - 1) ? n - 1 : n); + + const value = { + step, + setStep, + setStepNext, + setStepBack, + isStep, + }; + + return ( + + + + {children.map(child => ( + + + {child.props.label} + + ) + )} + + {children[step]} + + + ); +}; + +const useStepper = () => useContext(StepContext); + +const StepperFooter = ({nextDisabled, backDisabled, onBack = null, onNext = null, onSubmit = () => {}}) => { + const { step, isStep, setStepNext, setStepBack, setStep } = useStepper(); + const { resetFilters } = useFilter(); + const isSubmit = !isStep(step + 2); + + const _onBack = () => { + if (isStep(step + 1)) { + setStepBack(); + } else { + resetFilters(); + setStep(0); + } + }; + + const _onNext = () => { + setStepNext(); + }; + + const _onSubmit = () => { + onSubmit(); + if (onNext) { + onNext(); + } else { + _onNext(); + } + }; + + return ( + + {isStep(step - 1) && + + } + + {isStep(step + 1) && + + } + ); +}; + +export { + StepperWizard, + StepperFooter, + useStepper, +}; diff --git a/frontend/src/components/steps/address/AddressSearch.jsx b/frontend/src/components/steps/address/AddressSearch.jsx index 8ebc18b..688b5dd 100644 --- a/frontend/src/components/steps/address/AddressSearch.jsx +++ b/frontend/src/components/steps/address/AddressSearch.jsx @@ -25,9 +25,10 @@ const AddressSearchSuggestions = ({results, onClick}) => ( : null); -const AddressSearch = ({filters, updateFilterState, resetFilterState, setNextDisabled, setRedirectBack, classNames}) => { +const AddressSearch = ({onSelect, classNames}) => { const [value, setValue] = useState(""); const [enable, setEnable] = useState(true); + const [selected, setSelected] = useState(null); const [results, setResults] = useState([]); const [isLoading, setIsLoading] = useState(false); @@ -35,7 +36,6 @@ const AddressSearch = ({filters, updateFilterState, resetFilterState, setNextDis setResults([]); if (enable && value && value.length > 5) { - setNextDisabled(true); const timer = setTimeout(() => { setIsLoading(true); LocationRepsostory.getPropertyDetails({search: value}).then(resp => { @@ -46,7 +46,11 @@ const AddressSearch = ({filters, updateFilterState, resetFilterState, setNextDis return () => clearTimeout(timer); } - }, [value, enable, setNextDisabled]); + }, [value, enable]); + + useEffect(() => { + onSelect(selected); + }, [selected, onSelect]); return (
@@ -65,6 +69,9 @@ const AddressSearch = ({filters, updateFilterState, resetFilterState, setNextDis onChange={(e) => { setEnable(true); setValue(e.target.value); + if (selected && e.target.value !== selected.address) { + setSelected(null); + } }} value={value} /> @@ -72,14 +79,8 @@ const AddressSearch = ({filters, updateFilterState, resetFilterState, setNextDis results={results} onClick={(r) => { setValue(r.address); - updateFilterState({ - coordinates: { - lng: r.coordinates[0], - lat: r.coordinates[1], - }, - }); - setNextDisabled(false); setEnable(false); + setSelected(r); }} />
); diff --git a/frontend/src/components/steps/address/AddressStep.jsx b/frontend/src/components/steps/address/AddressStep.jsx index de705df..47abb6b 100644 --- a/frontend/src/components/steps/address/AddressStep.jsx +++ b/frontend/src/components/steps/address/AddressStep.jsx @@ -1,11 +1,16 @@ +import { useState } from 'react'; import Step from '../Step'; import StepInformation from '../StepInformation'; import staticText from '../../../assets/data/staticText.json' import addressBackgroundImage from '../../../assets/img/stepBackgrounds/step1.jpg'; import AddressSearch from './AddressSearch'; +import { StepperFooter } from '../../providers/StepperProvider'; +import { useFilter } from '../../providers/FilterProvider'; -const AddressStep = (props) => { +const AddressStep = () => { + const [nextDisabled, setNextDisabled] = useState(true); + const { updateFilters } = useFilter(); const addressPanel = (
@@ -14,16 +19,31 @@ const AddressStep = (props) => { description={

{staticText.steps.address.description}

} />
- + { + if (address) { + setNextDisabled(false); + updateFilters({ + coordinates: { + lat: address.coordinates[1], + lng: address.coordinates[0], + }, + }); + } else { + setNextDisabled(true); + } + }}/>
); return ( - ); + <> + + + ); }; export default AddressStep; diff --git a/frontend/src/components/steps/complete/CompleteStep.jsx b/frontend/src/components/steps/complete/CompleteStep.jsx new file mode 100644 index 0000000..59ac5eb --- /dev/null +++ b/frontend/src/components/steps/complete/CompleteStep.jsx @@ -0,0 +1,24 @@ +import Step from "../Step"; +import StepInformation from "../StepInformation"; +import completeBackgroundImage from "../../../assets/img/stepBackgrounds/step6.jpg"; +import staticText from "../../../assets/data/staticText.json"; +import { StepperFooter } from "../../providers/StepperProvider"; + +export default function CompleteStep() { + const completeInfoPanel = ( + {staticText.steps.complete.description}

} + /> + ); + + return ( + <> + + + + ); +}; diff --git a/frontend/src/components/steps/habitat/HabitatSelector.jsx b/frontend/src/components/steps/habitat/HabitatSelector.jsx index 33df9b8..1096b27 100644 --- a/frontend/src/components/steps/habitat/HabitatSelector.jsx +++ b/frontend/src/components/steps/habitat/HabitatSelector.jsx @@ -8,16 +8,18 @@ import FormLabel from "@mui/material/FormLabel"; import FormControl from "@mui/material/FormControl"; import staticText from "../../../assets/data/staticText.json"; import { HabitatSVG } from "../HabitatSVG"; +import { useFilter } from '../../providers/FilterProvider'; -export default function HabitatSelector(props) { +export default function HabitatSelector({setNextDisabled}) { + const { filters, updateFilters } = useFilter(); const [habitats, setHabitats] = useState([]); const [habitatsMap, setHabitatsMap] = useState({}); const [value, setValue] = React.useState( - props.filters.habitat && props.filters.habitat.id + filters.habitat && filters.habitat.id ); const [selectedHabitat, setSelectedHabitat] = React.useState({}); const [imageValue, setImageValue] = React.useState( - props.filters.habitatImage + filters.habitatImage ); const getHabitats = () => { @@ -39,22 +41,22 @@ export default function HabitatSelector(props) { habitats.length === 0 && getHabitats(); // Sets the selected habitat if its already set in filters - if (props.filters.habitat && props.filters.habitat.id) { - setSelectedHabitat(habitatsMap[props.filters.habitat.id]); + if (filters.habitat && filters.habitat.id) { + setSelectedHabitat(habitatsMap[filters.habitat.id]); } // If both the habitat and the image is selected, then enable the next step - if (props.filters.habitat && props.filters.habitatImage) { - props.setNextDisabled(false); + if (filters.habitat && filters.habitatImage) { + setNextDisabled(false); } - }, [habitats.length, props, habitatsMap]); + }, [habitats.length, setNextDisabled, habitatsMap, filters]); const setHabitatImage = (imageId) => { // Sets the selected image radio, updates filter state for image and enable the next button setImageValue(imageId); - props.updateFilterState({ habitatImage: imageId }); - props.updateFilterState({ zone: null }); - props.setNextDisabled(false); + updateFilters({ habitatImage: imageId }); + updateFilters({ zone: null }); + setNextDisabled(false); }; const handleRadioChange = (event) => { @@ -69,11 +71,11 @@ export default function HabitatSelector(props) { if (habitatObject.images.length === 1) { setHabitatImage(habitatObject.images[0].id); } else { - props.setNextDisabled(true); + setNextDisabled(true); } // Update the filters for the selected habitat - props.updateFilterState({ + updateFilters({ habitat: { id: habitatObject.id, name: habitatObject.name }, }); }; diff --git a/frontend/src/components/steps/habitat/HabitatStep.jsx b/frontend/src/components/steps/habitat/HabitatStep.jsx index 165fe36..a12e0ac 100644 --- a/frontend/src/components/steps/habitat/HabitatStep.jsx +++ b/frontend/src/components/steps/habitat/HabitatStep.jsx @@ -1,10 +1,14 @@ +import { useState } from 'react'; import Step from "../Step"; import HabitatSelector from "./HabitatSelector"; import StepInformation from "../StepInformation"; import staticText from "../../../assets/data/staticText.json"; import habitatBackgroundImage from "../../../assets/img/stepBackgrounds/step3.jpg"; +import { StepperFooter } from '../../providers/StepperProvider'; export default function HabitatStep(props) { + const [nextDisabled, setNextDisabled] = useState(true); + const habitatInfoPanel = ( ); - const habitatSelectionPanel = ; + const habitatSelectionPanel = ; return ( - + <> + + + ); } diff --git a/frontend/src/components/steps/location/LocationStep.jsx b/frontend/src/components/steps/location/LocationStep.jsx index 4e4c0b4..b5dcd13 100644 --- a/frontend/src/components/steps/location/LocationStep.jsx +++ b/frontend/src/components/steps/location/LocationStep.jsx @@ -1,10 +1,14 @@ +import { useState } from "react"; import Step from "../Step"; import LocationSelectorMap from "./Map"; import StepInformation from "../StepInformation"; import staticText from "../../../assets/data/staticText.json"; import locationBackgroundImage from "../../../assets/img/stepBackgrounds/step1.jpg"; +import { StepperFooter } from '../../providers/StepperProvider'; export default function LocationStep(props) { + const [nextDisabled, setNextDisabled] = useState(true); + const locationInfoPanel = ( ); - const locationSelectionPanel = ; + const locationSelectionPanel = ; return ( - + <> + + + ); } diff --git a/frontend/src/components/steps/location/Map.jsx b/frontend/src/components/steps/location/Map.jsx index a2a5690..1f66697 100644 --- a/frontend/src/components/steps/location/Map.jsx +++ b/frontend/src/components/steps/location/Map.jsx @@ -1,28 +1,31 @@ import { useState, useEffect } from "react"; import { MapContainer, TileLayer, Marker, useMapEvents } from "react-leaflet"; import LocationRepository from "../../../repository/LocationRepository"; +import { useFilter } from "../../providers/FilterProvider"; const NZ_BOUNDS = [ [-47.204642, 165.344238], [-34.307144, 179.824219], ]; -function LocationMarker(props) { +function LocationMarker({setNextDisabled}) { const [position, setPosition] = useState(null); + const { filters, updateFilters } = useFilter(); + const map = useMapEvents({ click(e) { const newPosition = e.latlng; setPosition(newPosition); - props.updateFilterState({ coordinates: newPosition }); - props.setNextDisabled(false); + updateFilters({ coordinates: newPosition }); + setNextDisabled(false); }, }); map.whenReady(() => { - const savedCoordinates = props.filters["coordinates"]; + const savedCoordinates = filters.coordinates; if (!position && savedCoordinates) { setPosition(savedCoordinates); - props.setNextDisabled(false); + setNextDisabled(false); map.flyTo(savedCoordinates, 9); } }); @@ -36,16 +39,17 @@ const fitNZBounds = (map) => { function LocationDetailsDisplay(props) { const [locationDetails, setLocationDetails] = useState({}); + const { filters } = useFilter(); useEffect(() => { - if (props.filters["coordinates"]) { - LocationRepository.getLocationData(props.filters).then((result) => { + if (filters.coordinates) { + LocationRepository.getLocationData(filters).then((result) => { setLocationDetails(result); }); } - }, [props.filters, props.filters.coordinates]); + }, [filters]); - const savedCoordinates = props.filters["coordinates"]; + const savedCoordinates = filters.coordinates; if (savedCoordinates) { const soilString = `${locationDetails.soil_name} (${locationDetails.soil_code})`; diff --git a/frontend/src/components/steps/results/ResultsStep.jsx b/frontend/src/components/steps/results/ResultsStep.jsx index 07a3ce8..578b416 100644 --- a/frontend/src/components/steps/results/ResultsStep.jsx +++ b/frontend/src/components/steps/results/ResultsStep.jsx @@ -8,6 +8,8 @@ import PlantRepository from "../../../repository/PlantRepository"; import { Typography, Box } from "@mui/material"; import resultsBackgroundImage from "../../../assets/img/stepBackgrounds/step6.jpg"; import staticText from "../../../assets/data/staticText.json"; +import { useFilter } from "../../providers/FilterProvider"; +import { StepperFooter } from "../../providers/StepperProvider"; const RESULTS_DESCRIPTION = ( @@ -29,10 +31,11 @@ const RESULTS_DESCRIPTION = ( export default function ResultsStep(props) { const [plants, setPlants] = useState([]); + const { filters } = useFilter(); useEffect(() => { const updatePlants = () => { - PlantRepository.getFilteredPlants(props.filters) + PlantRepository.getFilteredPlants(filters) .then((response) => { if (response.status === 200) { setPlants(response.data); @@ -43,7 +46,7 @@ export default function ResultsStep(props) { }); }; updatePlants(); - }, [props.filters]); + }, [filters]); function createData( name, @@ -91,13 +94,13 @@ export default function ResultsStep(props) { }; const downloadCSV = () => { - PlantRepository.getPlantsCSV(props.filters).then((response) => { + PlantRepository.getPlantsCSV(filters).then((response) => { download(response, "text/csv", "plants.csv"); }); }; const downloadPDF = () => { - PlantRepository.getPlantsPDF(props.filters).then((response) => { + PlantRepository.getPlantsPDF(filters).then((response) => { download(response, "application/pdf", "planting_guide.pdf"); }); }; @@ -133,9 +136,12 @@ export default function ResultsStep(props) { ); return ( - + <> + + + ); } diff --git a/frontend/src/components/steps/soilvariant/SoilSelector.jsx b/frontend/src/components/steps/soilvariant/SoilSelector.jsx index a0a0fac..980a6f3 100644 --- a/frontend/src/components/steps/soilvariant/SoilSelector.jsx +++ b/frontend/src/components/steps/soilvariant/SoilSelector.jsx @@ -7,6 +7,7 @@ import FormControl from "@mui/material/FormControl"; import FormHelperText from "@mui/material/FormHelperText"; import Tooltip from "@mui/material/Tooltip"; import staticText from "../../../assets/data/staticText.json"; +import { useFilter } from "../../providers/FilterProvider"; const WET_SOIL_DESCRIPTION = (

@@ -53,24 +54,26 @@ const MESIC_SOIL_DESCRIPTION = (

); -export default function SoilSelector(props) { - const [value, setValue] = React.useState(props.filters.soilVariant); +export default function SoilSelector({setNextDisabled}) { + const { filters, updateFilters } = useFilter(); + + const [value, setValue] = React.useState(filters.soilVariant); const [helperText, setHelperText] = React.useState( staticText.steps.soil.optionsHelperText ); React.useEffect(() => { - if (props.filters.soilVariant) { - props.setNextDisabled(false); + if (filters.soilVariant) { + setNextDisabled(false); } - }); + }, [filters, setNextDisabled]); const handleRadioChange = (event) => { const soilVariantSelection = event.target.value; setValue(soilVariantSelection); setHelperText(" "); - props.updateFilterState({ soilVariant: soilVariantSelection }); - props.setNextDisabled(false); + updateFilters({ soilVariant: soilVariantSelection }); + setNextDisabled(false); }; return ( diff --git a/frontend/src/components/steps/soilvariant/SoilStep.jsx b/frontend/src/components/steps/soilvariant/SoilStep.jsx index bd087c5..78959e2 100644 --- a/frontend/src/components/steps/soilvariant/SoilStep.jsx +++ b/frontend/src/components/steps/soilvariant/SoilStep.jsx @@ -1,10 +1,15 @@ +import { useState } from 'react'; import Step from "../Step"; import SoilSelector from "./SoilSelector"; import StepInformation from "../StepInformation"; import staticText from "../../../assets/data/staticText.json"; import soilBackgroundImage from "../../../assets/img/stepBackgrounds/step2.jpg"; +import { StepperFooter } from '../../providers/StepperProvider'; + export default function SoilVariantStep(props) { + const [nextDisabled, setNextDisabled] = useState(true); + const SOIL_DESCRIPTION = (

From your site location, we use{" "} @@ -36,15 +41,18 @@ export default function SoilVariantStep(props) { const soilVarientSelectionPanel = (

- +
); return ( - + <> + + + ); } diff --git a/frontend/src/components/steps/specifics/ProjectSpecificsSelector.jsx b/frontend/src/components/steps/specifics/ProjectSpecificsSelector.jsx deleted file mode 100644 index 0b16c56..0000000 --- a/frontend/src/components/steps/specifics/ProjectSpecificsSelector.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import { useEffect } from "react"; - -export default function ProjectSpecificsSelector(props) { - useEffect(() => { - props.setNextDisabled(false); - }); - - return

Project Specifics Selector

; -} diff --git a/frontend/src/components/steps/specifics/ProjectSpecificsStep.jsx b/frontend/src/components/steps/specifics/ProjectSpecificsStep.jsx deleted file mode 100644 index 22e0da9..0000000 --- a/frontend/src/components/steps/specifics/ProjectSpecificsStep.jsx +++ /dev/null @@ -1,24 +0,0 @@ -import Step from "../Step"; -import ProjectSpecificsSelector from "./ProjectSpecificsSelector"; -import StepInformation from "../StepInformation"; -import staticText from "../../../assets/data/staticText.json"; - -export default function ProjectSpecificsStep(props) { - const projectSpecificsInfoPanel = ( - {staticText.steps.projectSpecifics.description}

} - /> - ); - - const projectSpecificsSelectionPanel = ( - - ); - - return ( - - ); -} diff --git a/frontend/src/components/steps/summary/SummaryContent.jsx b/frontend/src/components/steps/summary/SummaryContent.jsx index a50d1a8..0ebf0f2 100644 --- a/frontend/src/components/steps/summary/SummaryContent.jsx +++ b/frontend/src/components/steps/summary/SummaryContent.jsx @@ -8,13 +8,15 @@ import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import SummaryTable from "./SummaryTable"; import LocationRepository from "../../../repository/LocationRepository"; +import { useFilter } from "../../providers/FilterProvider"; -export default function SummaryContent(props) { +export default function SummaryContent() { const [expanded, setExpanded] = React.useState(null); const [locationDetails, setLocationDetails] = React.useState({}); + const { filters } = useFilter(); const getLocationDetails = () => { - LocationRepository.getLocationData(props.filters).then((result) => { + LocationRepository.getLocationData(filters).then((result) => { setLocationDetails(result); }); }; @@ -24,7 +26,6 @@ export default function SummaryContent(props) { }; React.useEffect(() => { - props.setNextDisabled(false); !Object.keys(locationDetails).length && getLocationDetails(); }); @@ -35,7 +36,7 @@ export default function SummaryContent(props) { const locationData = [ createData( "Geographical Coordinates (latitude, longitude)", - `(${props.filters.coordinates.lat}, ${props.filters.coordinates.lng})` + `(${filters.coordinates.lat}, ${filters.coordinates.lng})` ), createData("Ecological Region", locationDetails.ecological_region || ""), createData( @@ -50,22 +51,22 @@ export default function SummaryContent(props) { "Soil Order", `${locationDetails.soil_name} (${locationDetails.soil_code})` || "" ), - createData("Soil Variant", props.filters.soilVariant), + createData("Soil Variant", filters.soilVariant), ]; const siteData = [ - createData("Habitat", props.filters.habitat.name ?? ""), + createData("Habitat", filters.habitat.name ?? ""), createData( "Zone Name", - (props.filters.zone && props.filters.zone.name) ?? "" + (filters.zone && filters.zone.name) ?? "" ), createData( "Zone Variant", - (props.filters.zone && props.filters.zone.variant) ?? "" + (filters.zone && filters.zone.variant) ?? "" ), createData( "Zone Refined Variant", - (props.filters.zone && props.filters.zone.refined_variant) ?? "" + (filters.zone && filters.zone.refined_variant) ?? "" ), ]; diff --git a/frontend/src/components/steps/summary/SummaryStep.jsx b/frontend/src/components/steps/summary/SummaryStep.jsx index ef7aa02..835bbc8 100644 --- a/frontend/src/components/steps/summary/SummaryStep.jsx +++ b/frontend/src/components/steps/summary/SummaryStep.jsx @@ -3,8 +3,10 @@ import SummaryContent from "./SummaryContent"; import StepInformation from "../StepInformation"; import staticText from "../../../assets/data/staticText.json"; import summaryBackgroundImage from "../../../assets/img/stepBackgrounds/step5.jpg"; +import { StepperFooter } from '../../providers/StepperProvider'; -export default function SummaryStep(props) { + +export default function SummaryStep({ onSubmit }) { const summaryInfoPanel = ( ); - const summaryContent = ; + const summaryContent = ; return ( - + <> + + + ); } diff --git a/frontend/src/components/steps/zone/ZoneSelector.jsx b/frontend/src/components/steps/zone/ZoneSelector.jsx index b2463f0..171a762 100644 --- a/frontend/src/components/steps/zone/ZoneSelector.jsx +++ b/frontend/src/components/steps/zone/ZoneSelector.jsx @@ -1,11 +1,13 @@ import { useEffect, useState } from "react"; import SiteRepository from "../../../repository/SiteRepository"; +import { useFilter } from "../../providers/FilterProvider"; import { HabitatSVG } from "../HabitatSVG"; -export default function ZoneSelector(props) { +export default function ZoneSelector({setNextDisabled}) { const [habitatImageObject, setHabitatImageObject] = useState({}); const [segmentMapping, setSegmentMapping] = useState({}); const [selectedZoneSegment, setZoneSegment] = useState(null); + const { filters, updateFilters } = useFilter(); const setZoneOrRedirect = (zone) => { const redirectHabitat = zone && zone.redirect_habitat; @@ -15,15 +17,13 @@ export default function ZoneSelector(props) { ? { habitat: { id: redirectHabitat.id, name: redirectHabitat.name } } : { zone: zone }; - props.updateFilterState(newFilterState); - props.setRedirectBack(Boolean(redirectHabitat)); - - props.setNextDisabled(false); + updateFilters(newFilterState); + setNextDisabled(false); }; useEffect(() => { const getHabitatImage = () => { - SiteRepository.getHabitatImage(props.filters.habitatImage).then( + SiteRepository.getHabitatImage(filters.habitatImage).then( (response) => { if (response.status === 200) { const imageData = response.data; @@ -34,8 +34,8 @@ export default function ZoneSelector(props) { }; const setInitialZone = () => { - if (props.filters.zone) { - const zone = props.filters.zone; + if (filters.zone) { + const zone = filters.zone; const zoneSegment = document.querySelectorAll( `.zone-selector-svg svg path[inkscapelabel="${zone.related_svg_segment}"]` )[0]; @@ -45,7 +45,7 @@ export default function ZoneSelector(props) { setZoneSegment(zoneSegment); zoneSegment.style.fill = "#eeeeee"; zoneSegment.style["fill-opacity"] = 0.5; - props.setNextDisabled(false); + setNextDisabled(false); } } }; @@ -69,7 +69,7 @@ export default function ZoneSelector(props) { // Retrieves the habitat image from the api if it's not loaded already Object.keys(segmentMapping).length === 0 && getZones(); - }, [habitatImageObject, segmentMapping, props, props.filters.zone]); + }, [habitatImageObject, segmentMapping, setNextDisabled, filters]); const selectZone = (element) => { if ( diff --git a/frontend/src/components/steps/zone/ZoneStep.jsx b/frontend/src/components/steps/zone/ZoneStep.jsx index e4ebf3b..c870293 100644 --- a/frontend/src/components/steps/zone/ZoneStep.jsx +++ b/frontend/src/components/steps/zone/ZoneStep.jsx @@ -1,10 +1,20 @@ +import { useState } from 'react'; +import Button from '@mui/material/Button'; import Step from "../Step"; import ZoneSelector from "./ZoneSelector"; import StepInformation from "../StepInformation"; import staticText from "../../../assets/data/staticText.json"; import zoneBackgroundImage from "../../../assets/img/stepBackgrounds/step4.jpg"; +import { StepperFooter, useStepper } from '../../providers/StepperProvider'; +import { useFilter } from '../../providers/FilterProvider'; + +export default function ZoneStep({repeatable}) { + const [nextDisabled, setNextDisabled] = useState(true); + const { setStepBack, setStepNext } = useStepper(); + const { filters } = useFilter(); + + const redirect = !nextDisabled && !filters.zone; -export default function ZoneStep(props) { const zoneInfoPanel = ( - + ); return ( - + <> + + + ); -} +}; diff --git a/frontend/src/index.js b/frontend/src/index.js index f7fe04d..ffff73c 100644 --- a/frontend/src/index.js +++ b/frontend/src/index.js @@ -4,6 +4,7 @@ import { createTheme, ThemeProvider } from "@mui/material/styles"; import reportWebVitals from "./reportWebVitals"; import { createBrowserRouter, RouterProvider } from "react-router-dom"; import MainPage from "./pages/MainPage"; +import { FilterProvider } from "./components/providers/FilterProvider"; // Styles import "./assets/styles/main.scss"; @@ -33,9 +34,11 @@ const darkTheme = createTheme({ ReactDOM.render(
- - - + + + + +
, document.getElementById("root") diff --git a/frontend/src/pages/ApplyPage.jsx b/frontend/src/pages/ApplyPage.jsx index a4f1879..7c956b6 100644 --- a/frontend/src/pages/ApplyPage.jsx +++ b/frontend/src/pages/ApplyPage.jsx @@ -1,45 +1,29 @@ import { Container } from "reactstrap"; -import Stepper from "../components/Stepper"; import Header from "../components/Header"; import AddressStep from "../components/steps/address/AddressStep"; import SoilStep from "../components/steps/soilvariant/SoilStep"; import HabitatStep from "../components/steps/habitat/HabitatStep"; import ZoneStep from "../components/steps/zone/ZoneStep"; import SummaryStep from "../components/steps/summary/SummaryStep"; +import { StepperWizard } from "../components/providers/StepperProvider"; +import CompleteStep from "../components/steps/complete/CompleteStep"; +import { useFilter } from "../components/providers/FilterProvider"; -const ApplyPage = () => ( - -
- - -); +const ApplyPage = () => { + const { submit } = useFilter(); + + return ( + +
+ + + + + + + + + ); +}; export default ApplyPage; diff --git a/frontend/src/pages/MainPage.jsx b/frontend/src/pages/MainPage.jsx index 7828472..f56f66e 100644 --- a/frontend/src/pages/MainPage.jsx +++ b/frontend/src/pages/MainPage.jsx @@ -1,5 +1,4 @@ import { Container } from "reactstrap"; -import Stepper from "../components/Stepper"; import Header from "../components/Header"; import LocationStep from "../components/steps/location/LocationStep"; import SoilStep from "../components/steps/soilvariant/SoilStep"; @@ -7,45 +6,21 @@ import HabitatStep from "../components/steps/habitat/HabitatStep"; import ZoneStep from "../components/steps/zone/ZoneStep"; import SummaryStep from "../components/steps/summary/SummaryStep"; import ResultsStep from "../components/steps/results/ResultsStep"; +import { StepperWizard } from "../components/providers/StepperProvider"; -const MainPage = () => ( - -
- - -); +const MainPage = () => { + return ( + +
+ + + + + + + + + ); +}; export default MainPage; diff --git a/frontend/src/repository/Repository.js b/frontend/src/repository/Repository.js index 0bc49af..6626035 100644 --- a/frontend/src/repository/Repository.js +++ b/frontend/src/repository/Repository.js @@ -2,10 +2,9 @@ import axios from "axios"; // Create the axios object const repo = axios.create({ - baseURL: - window.location.hostname === "localhost" - ? "http://localhost:9000/api" - : "/api", + baseURL: "/api", + xsrfHeaderName: "X-CSRFToken", + xsrfCookieName: "csrftoken", }); repo.defaults.headers.post["access-control-allow-origin"] = "*";