diff --git a/src/backend/app/central/central_crud.py b/src/backend/app/central/central_crud.py index 77586f0088..1251ee251a 100644 --- a/src/backend/app/central/central_crud.py +++ b/src/backend/app/central/central_crud.py @@ -357,7 +357,7 @@ def download_submissions( return fixed.splitlines() -async def test_form_validity(xform_content: str, form_type: str): +async def test_form_validity(xform_content: bytes, form_type: str): """Validate an XForm. Args: @@ -365,37 +365,34 @@ async def test_form_validity(xform_content: str, form_type: str): form_type (str): type of form (xls or xlsx). """ try: - xlsform_path = f"/tmp/validate_form.{form_type}" - outfile = "/tmp/outfile.xml" - - with open(xlsform_path, "wb") as f: - f.write(xform_content) - - xls2xform_convert(xlsform_path=xlsform_path, xform_path=outfile, validate=False) - - namespaces = { - "h": "http://www.w3.org/1999/xhtml", - "odk": "http://www.opendatakit.org/xforms", - "xforms": "http://www.w3.org/2002/xforms", - } - - with open(outfile, "r") as xml: - data = xml.read() - - root = ElementTree.fromstring(data) - instances = root.findall(".//xforms:instance[@src]", namespaces) - - geojson_list = [] - for inst in instances: - try: - if "src" in inst.attrib: - if (inst.attrib["src"].split("."))[1] == "geojson": - parts = (inst.attrib["src"].split("."))[0].split("/") - geojson_name = parts[-1] - geojson_list.append(geojson_name) - except Exception: - continue + if form_type != "xml": + # Write xform_content to a temporary file + with open(f"/tmp/xform_temp.{form_type}", "wb") as f: + f.write(xform_content) + else: + with open(f"/tmp/xlsform.{form_type}", "wb") as f: + f.write(xform_content) + # Convert XLSForm to XForm + xls2xform_convert( + xlsform_path="/tmp/xlsform.xls", + xform_path="/tmp/xform_temp.xml", + validate=False, + ) + + # Parse XForm + namespaces = {"xforms": "http://www.w3.org/2002/xforms"} + tree = ElementTree.parse("/tmp/xform_temp.xml") + root = tree.getroot() + + # Extract geojson filenames + geojson_list = [ + os.path.splitext(inst.attrib["src"].split("/")[-1])[0] + for inst in root.findall(".//xforms:instance[@src]", namespaces) + if inst.attrib.get("src", "").endswith(".geojson") + ] + return {"required media": geojson_list, "message": "Your form is valid"} + except Exception as e: return JSONResponse( content={"message": "Your form is invalid", "possible_reason": str(e)}, diff --git a/src/backend/app/main.py b/src/backend/app/main.py index d206619f43..3f08d6044c 100644 --- a/src/backend/app/main.py +++ b/src/backend/app/main.py @@ -45,6 +45,7 @@ # Add sentry tracing only in prod if not settings.DEBUG: + log.info("Adding Sentry tracing") sentry_sdk.init( dsn=settings.SENTRY_DSN, traces_sample_rate=0.1, diff --git a/src/backend/app/projects/project_routes.py b/src/backend/app/projects/project_routes.py index 2979020022..2635c262b7 100644 --- a/src/backend/app/projects/project_routes.py +++ b/src/backend/app/projects/project_routes.py @@ -588,19 +588,21 @@ async def edit_project_boundary( } -@router.post("/validate_form") +@router.post("/validate-form") async def validate_form(form: UploadFile): """Tests the validity of the xls form uploaded. Parameters: - form: The xls form to validate """ - file_name = os.path.splitext(form.filename) - file_ext = file_name[1] + file = Path(form.filename) + file_ext = file.suffix - allowed_extensions = [".xls", ".xlsx"] + allowed_extensions = [".xls", ".xlsx", "xml"] if file_ext not in allowed_extensions: - raise HTTPException(status_code=400, detail="Provide a valid .xls file") + raise HTTPException( + status_code=400, detail="Provide a valid .xls,.xlsx,.xml file" + ) contents = await form.read() return await central_crud.test_form_validity(contents, file_ext[1:]) diff --git a/src/backend/app/submissions/submission_crud.py b/src/backend/app/submissions/submission_crud.py index a384be1229..d51566193e 100644 --- a/src/backend/app/submissions/submission_crud.py +++ b/src/backend/app/submissions/submission_crud.py @@ -411,7 +411,12 @@ def get_all_submissions_json(db: Session, project_id): get_task_id_list_sync = async_to_sync(tasks_crud.get_task_id_list) task_list = get_task_id_list_sync(db, project_id) - submissions = project.getAllSubmissions(project_info.odkid, task_list) + xform_list = [ + f"{project_info.project_name_prefix}_{task}_{project_info.xform_title}" + for task in task_list + ] + + submissions = project.getAllSubmissions(project_info.odkid, xform_list) return submissions @@ -802,7 +807,8 @@ async def get_submission_by_task( odk_credentials = await project_deps.get_odk_credentials(db, project.id) xform = get_odk_form(odk_credentials) - data = xform.listSubmissions(project.odkid, str(task_id), filters) + xform_name = f"{project.project_name_prefix}_{task_id}_{project.xform_title}" + data = xform.listSubmissions(project.odkid, xform_name, filters) submissions = data.get("value", []) count = data.get("@odata.count", 0) diff --git a/src/frontend/src/components/ProjectDetailsV2/TaskSectionPopup.tsx b/src/frontend/src/components/ProjectDetailsV2/TaskSectionPopup.tsx index a5856e49fd..3d953f64c1 100644 --- a/src/frontend/src/components/ProjectDetailsV2/TaskSectionPopup.tsx +++ b/src/frontend/src/components/ProjectDetailsV2/TaskSectionPopup.tsx @@ -5,6 +5,7 @@ import { ProjectActions } from '@/store/slices/ProjectSlice'; import environment from '@/environment'; import { ProjectFilesById } from '@/api/Files'; import QrcodeComponent from '@/components/QrcodeComponent'; +import { useNavigate } from 'react-router-dom'; type TaskSectionPopupPropType = { taskId: number | null; @@ -14,10 +15,16 @@ type TaskSectionPopupPropType = { const TaskSectionPopup = ({ taskId, body, feature }: TaskSectionPopupPropType) => { const dispatch = CoreModules.useAppDispatch(); - const [task_status, set_task_status] = useState('READY'); - const taskModalStatus = CoreModules.useAppSelector((state) => state.project.taskModalStatus); + const navigate = useNavigate(); const params = CoreModules.useParams(); + const encodedProjectId = params.id; const currentProjectId = environment.decode(params.id); + + console.log(encodedProjectId); + + const [task_status, set_task_status] = useState('READY'); + + const taskModalStatus = CoreModules.useAppSelector((state) => state.project.taskModalStatus); const projectData = CoreModules.useAppSelector((state) => state.project.projectTaskBoundries); const projectIndex = projectData.findIndex((project) => project.id == currentProjectId); @@ -74,7 +81,9 @@ const TaskSectionPopup = ({ taskId, body, feature }: TaskSectionPopupPropType) = {}} + onClick={() => { + navigate(`/project-submissions/${encodedProjectId}?tab=table&task_id=${taskId}`); + }} /> { +const DataExtract = ({ flag, customDataExtractUpload, setCustomDataExtractUpload }) => { const dispatch = useDispatch(); const navigate = useNavigate(); - + console.log(customDataExtractUpload, 'customDataExtractUpload'); const [extractWays, setExtractWays] = useState(''); - const [featureType, setFeatureType] = useState(''); const projectDetails: any = useAppSelector((state) => state.createproject.projectDetails); const projectAoiGeojson = useAppSelector((state) => state.createproject.drawnGeojson); const dataExtractGeojson = useAppSelector((state) => state.createproject.dataExtractGeojson); const isFgbFetching = useAppSelector((state) => state.createproject.isFgbFetching); const submission = () => { - if (featureType !== formValues?.dataExtractFeatureType && formValues.dataExtractWays === 'osm_data_extract') { - dispatch( - CommonActions.SetSnackBar({ - open: true, - message: `Please generate data extract for ${FeatureTypeName[featureType]}`, - variant: 'warning', - duration: 2000, - }), - ); - return; - } - dispatch(CreateProjectActions.SetIndividualProjectDetailsData(formValues)); dispatch(CommonActions.SetCurrentStepFormStep({ flag: flag, step: 5 })); @@ -107,12 +82,9 @@ const DataExtract = ({ flag, customLineUpload, setCustomLineUpload, customPolygo dispatch( CreateProjectActions.SetIndividualProjectDetailsData({ ...formValues, - data_extract_type: featureType, data_extract_url: fgbUrl, dataExtractWays: extractWays, - dataExtractFeatureType: featureType, - customLineUpload: null, - customPolygonUpload: null, + customDataExtractUpload: null, }), ); @@ -143,10 +115,7 @@ const DataExtract = ({ flag, customLineUpload, setCustomLineUpload, customPolygo if (formValues?.dataExtractWays) { setExtractWays(formValues?.dataExtractWays); } - if (formValues?.dataExtractFeatureType) { - setFeatureType(formValues?.dataExtractFeatureType); - } - }, [formValues?.dataExtractWays, formValues?.dataExtractFeatureType]); + }, [formValues?.dataExtractWays]); const toggleStep = (step, url) => { if (url === '/select-category') { @@ -154,7 +123,6 @@ const DataExtract = ({ flag, customLineUpload, setCustomLineUpload, customPolygo CreateProjectActions.SetIndividualProjectDetailsData({ ...formValues, dataExtractWays: extractWays, - dataExtractFeatureType: featureType, }), ); } @@ -260,72 +228,32 @@ const DataExtract = ({ flag, customLineUpload, setCustomLineUpload, customPolygo btnText="Generate Data Extract" btnType="primary" onClick={() => { - resetFile(setCustomPolygonUpload); - resetFile(setCustomLineUpload); + resetFile(setCustomDataExtractUpload); generateDataExtract(); }} className="fmtm-mt-6" isLoading={isFgbFetching} loadingText="Data extracting..." - disabled={dataExtractGeojson && !customPolygonUpload && !customLineUpload ? true : false} + disabled={dataExtractGeojson && customDataExtractUpload ? true : false} /> )} {extractWays === 'custom_data_extract' && ( <> - {/* TODO add option for point upload */} - {/* Set dataExtractFeatureType = 'centroid' */} - {/* { - changeFileHandler(e, setCustomPolygonUpload); - handleCustomChange('customPolygonUpload', e.target.files[0]); - handleCustomChange('dataExtractFeatureType', 'polygon'); - setFeatureType(''); - }} - onResetFile={() => { - resetFile(setCustomPolygonUpload); - handleCustomChange('customPolygonUpload', null); - }} - customFile={customPolygonUpload} - btnText="Upload Polygons" - accept=".geojson,.json,.fgb" - fileDescription="*The supported file formats are .geojson, .json, .fgb" - errorMsg={errors.customPolygonUpload} - /> */} - { - changeFileHandler(e, setCustomPolygonUpload); - handleCustomChange('customPolygonUpload', e.target.files[0]); - handleCustomChange('dataExtractFeatureType', 'polygon'); - handleCustomChange('data_extract_type', 'line'); - setFeatureType('polygon'); - }} - onResetFile={() => { - resetFile(setCustomPolygonUpload); - handleCustomChange('customPolygonUpload', null); - }} - customFile={customPolygonUpload} - btnText="Upload Polygons" - accept=".geojson,.json,.fgb" - fileDescription="*The supported file formats are .geojson, .json, .fgb" - errorMsg={errors.customPolygonUpload} - /> { - changeFileHandler(e, setCustomLineUpload); - handleCustomChange('customLineUpload', e.target.files[0]); - handleCustomChange('dataExtractFeatureType', 'line'); - handleCustomChange('data_extract_type', 'line'); - setFeatureType('line'); + changeFileHandler(e, setCustomDataExtractUpload); + handleCustomChange('customDataExtractUpload', e.target.files[0]); }} onResetFile={() => { - resetFile(setCustomLineUpload); - handleCustomChange('customLineUpload', null); + resetFile(setCustomDataExtractUpload); + handleCustomChange('customDataExtractUpload', null); + dispatch(CreateProjectActions.setDataExtractGeojson(null)); }} - customFile={customLineUpload} - btnText="Upload Lines" + customFile={customDataExtractUpload} + btnText="Upload Data Extract" accept=".geojson,.json,.fgb" fileDescription="*The supported file formats are .geojson, .json, .fgb" - errorMsg={errors.customLineUpload} + errorMsg={errors.customDataExtractUpload} /> )} diff --git a/src/frontend/src/components/createnewproject/SelectForm.tsx b/src/frontend/src/components/createnewproject/SelectForm.tsx index a8f8b457d5..76b9dfbe4b 100644 --- a/src/frontend/src/components/createnewproject/SelectForm.tsx +++ b/src/frontend/src/components/createnewproject/SelectForm.tsx @@ -81,7 +81,7 @@ const SelectForm = ({ flag, geojsonFile, customFormFile, setCustomFormFile }) => }; useEffect(() => { if (customFormFile && !customFileValidity) { - dispatch(ValidateCustomForm(`${import.meta.env.VITE_API_URL}/projects/validate_form`, customFormFile)); + dispatch(ValidateCustomForm(`${import.meta.env.VITE_API_URL}/projects/validate-form`, customFormFile)); } }, [customFormFile]); return ( @@ -153,8 +153,8 @@ const SelectForm = ({ flag, geojsonFile, customFormFile, setCustomFormFile }) => onResetFile={resetFile} customFile={customFormFile} btnText="Select a Form" - accept=".xls,.xlsx" - fileDescription="*The supported file formats are .xlsx, .xls" + accept=".xls,.xlsx,.xml" + fileDescription="*The supported file formats are .xlsx, .xls, .xml" errorMsg={errors.customFormUpload} /> ) : null} diff --git a/src/frontend/src/components/createnewproject/SplitTasks.tsx b/src/frontend/src/components/createnewproject/SplitTasks.tsx index f11755969a..f0389065ae 100644 --- a/src/frontend/src/components/createnewproject/SplitTasks.tsx +++ b/src/frontend/src/components/createnewproject/SplitTasks.tsx @@ -33,7 +33,7 @@ const alogrithmList = [ ]; let generateProjectLogIntervalCb: any = null; -const SplitTasks = ({ flag, geojsonFile, setGeojsonFile, customLineUpload, customPolygonUpload, customFormFile }) => { +const SplitTasks = ({ flag, geojsonFile, setGeojsonFile, customDataExtractUpload, customFormFile }) => { const dispatch = useDispatch(); const navigate = useNavigate(); @@ -41,30 +41,24 @@ const SplitTasks = ({ flag, geojsonFile, setGeojsonFile, customLineUpload, custo const [taskGenerationStatus, setTaskGenerationStatus] = useState(false); const divRef = useRef(null); - const splitTasksSelection = CoreModules.useAppSelector((state) => state.createproject.splitTasksSelection); - const drawnGeojson = CoreModules.useAppSelector((state) => state.createproject.drawnGeojson); - const projectDetails = CoreModules.useAppSelector((state) => state.createproject.projectDetails); + const splitTasksSelection = useAppSelector((state) => state.createproject.splitTasksSelection); + const drawnGeojson = useAppSelector((state) => state.createproject.drawnGeojson); + const projectDetails = useAppSelector((state) => state.createproject.projectDetails); const dataExtractGeojson = useAppSelector((state) => state.createproject.dataExtractGeojson); - const generateQrSuccess: any = CoreModules.useAppSelector((state) => state.createproject.generateQrSuccess); - const projectDetailsResponse = CoreModules.useAppSelector((state) => state.createproject.projectDetailsResponse); - const generateProjectLog: any = CoreModules.useAppSelector((state) => state.createproject.generateProjectLog); - const dividedTaskGeojson = CoreModules.useAppSelector((state) => state.createproject.dividedTaskGeojson); - const projectDetailsLoading = CoreModules.useAppSelector((state) => state.createproject.projectDetailsLoading); - const generateProjectLogLoading = CoreModules.useAppSelector( - (state) => state.createproject.generateProjectLogLoading, - ); - const dividedTaskLoading = CoreModules.useAppSelector((state) => state.createproject.dividedTaskLoading); - const taskSplittingGeojsonLoading = CoreModules.useAppSelector( - (state) => state.createproject.taskSplittingGeojsonLoading, - ); - const isTasksGenerated = CoreModules.useAppSelector((state) => state.createproject.isTasksGenerated); - const isFgbFetching = CoreModules.useAppSelector((state) => state.createproject.isFgbFetching); - const toggleSplittedGeojsonEdit = CoreModules.useAppSelector( - (state) => state.createproject.toggleSplittedGeojsonEdit, - ); + const generateQrSuccess = useAppSelector((state) => state.createproject.generateQrSuccess); + const projectDetailsResponse = useAppSelector((state) => state.createproject.projectDetailsResponse); + const generateProjectLog = useAppSelector((state) => state.createproject.generateProjectLog); + const dividedTaskGeojson = useAppSelector((state) => state.createproject.dividedTaskGeojson); + const projectDetailsLoading = useAppSelector((state) => state.createproject.projectDetailsLoading); + const generateProjectLogLoading = useAppSelector((state) => state.createproject.generateProjectLogLoading); + const dividedTaskLoading = useAppSelector((state) => state.createproject.dividedTaskLoading); + const taskSplittingGeojsonLoading = useAppSelector((state) => state.createproject.taskSplittingGeojsonLoading); + const isTasksGenerated = useAppSelector((state) => state.createproject.isTasksGenerated); + const isFgbFetching = useAppSelector((state) => state.createproject.isFgbFetching); + const toggleSplittedGeojsonEdit = useAppSelector((state) => state.createproject.toggleSplittedGeojsonEdit); - const toggleStep = (step, url) => { + const toggleStep = (step: number, url: string) => { dispatch(CommonActions.SetCurrentStepFormStep({ flag: flag, step: step })); navigate(url); }; @@ -92,7 +86,7 @@ const SplitTasks = ({ flag, geojsonFile, setGeojsonFile, customLineUpload, custo dispatch(CreateProjectActions.SetIndividualProjectDetailsData(formValues)); const hashtags = projectDetails.hashtags; const arrayHashtag = hashtags - .split('#') + ?.split('#') .map((item) => item.trim()) .filter(Boolean); @@ -114,7 +108,6 @@ const SplitTasks = ({ flag, geojsonFile, setGeojsonFile, customLineUpload, custo form_ways: projectDetails.formWays, // "uploaded_form": projectDetails.uploaded_form, hashtags: arrayHashtag, - data_extract_type: projectDetails.data_extract_type, data_extract_url: projectDetails.data_extract_url, }; // Append extra param depending on task split type @@ -136,7 +129,7 @@ const SplitTasks = ({ flag, geojsonFile, setGeojsonFile, customLineUpload, custo projectData, taskAreaGeojsonFile, customFormFile, - customPolygonUpload || customLineUpload, + customDataExtractUpload, projectDetails.dataExtractWays === 'osm_data_extract', ), ); @@ -178,10 +171,6 @@ const SplitTasks = ({ flag, geojsonFile, setGeojsonFile, customLineUpload, custo }), ); } else if (splitTasksSelection === task_split_type['task_splitting_algorithm']) { - // const a = document.createElement('a'); - // a.href = URL.createObjectURL(drawnGeojsonFile); - // a.download = 'test.json'; - // a.click(); dispatch( TaskSplittingPreviewService( `${import.meta.env.VITE_API_URL}/projects/task-split`, @@ -312,7 +301,7 @@ const SplitTasks = ({ flag, geojsonFile, setGeojsonFile, customLineUpload, custo
- {/*
)} diff --git a/src/frontend/src/components/createnewproject/UploadArea.tsx b/src/frontend/src/components/createnewproject/UploadArea.tsx index 668f6aa53f..763ea84975 100644 --- a/src/frontend/src/components/createnewproject/UploadArea.tsx +++ b/src/frontend/src/components/createnewproject/UploadArea.tsx @@ -31,7 +31,7 @@ const uploadAreaOptions = [ }, ]; -const UploadArea = ({ flag, geojsonFile, setGeojsonFile, setCustomLineUpload, setCustomPolygonUpload }) => { +const UploadArea = ({ flag, geojsonFile, setGeojsonFile, setCustomDataExtractUpload }) => { const dispatch = useDispatch(); const navigate = useNavigate(); // const [uploadAreaFile, setUploadAreaFile] = useState(null); @@ -46,7 +46,8 @@ const UploadArea = ({ flag, geojsonFile, setGeojsonFile, setCustomLineUpload, se const submission = () => { if (totalAreaSelection) { const totalArea = parseFloat(totalAreaSelection?.split(' ')[0]); - if (totalArea > 1000) { + const areaUnit = totalAreaSelection?.split(' ')[1]; + if (totalArea > 1000 && areaUnit === 'km²') { dispatch( CommonActions.SetSnackBar({ open: true, @@ -162,7 +163,8 @@ const UploadArea = ({ flag, geojsonFile, setGeojsonFile, setCustomLineUpload, se useEffect(() => { if (totalAreaSelection) { const totalArea = parseFloat(totalAreaSelection?.split(' ')[0]); - if (totalArea > 100) { + const areaUnit = totalAreaSelection?.split(' ')[1]; + if (totalArea > 100 && areaUnit === 'km²') { dispatch( CommonActions.SetSnackBar({ open: true, @@ -172,7 +174,7 @@ const UploadArea = ({ flag, geojsonFile, setGeojsonFile, setCustomLineUpload, se }), ); } - if (totalArea > 1000) { + if (totalArea > 1000 && areaUnit === 'km²') { dispatch( CommonActions.SetSnackBar({ open: true, @@ -323,8 +325,7 @@ const UploadArea = ({ flag, geojsonFile, setGeojsonFile, setCustomLineUpload, se dispatch(CreateProjectActions.SetDrawnGeojson(JSON.parse(geojson))); dispatch(CreateProjectActions.SetTotalAreaSelection(area)); dispatch(CreateProjectActions.ClearProjectStepState(formValues)); - setCustomLineUpload(null); - setCustomPolygonUpload(null); + setCustomDataExtractUpload(null); }} getAOIArea={(area) => { dispatch(CreateProjectActions.SetTotalAreaSelection(area)); diff --git a/src/frontend/src/components/createnewproject/validation/DataExtractValidation.tsx b/src/frontend/src/components/createnewproject/validation/DataExtractValidation.tsx index 02b3631f37..f1afa2435e 100644 --- a/src/frontend/src/components/createnewproject/validation/DataExtractValidation.tsx +++ b/src/frontend/src/components/createnewproject/validation/DataExtractValidation.tsx @@ -3,16 +3,14 @@ interface ProjectValues { dataExtractWays: string; data_extractFile: object; data_extract_options: string; - customPolygonUpload: string; - customLineUpload: string; + customDataExtractUpload: string; } interface ValidationErrors { form_ways?: string; dataExtractWays?: string; data_extractFile?: string; data_extract_options?: string; - customPolygonUpload?: string; - customLineUpload?: string; + customDataExtractUpload?: string; } function DataExtractValidation(values: ProjectValues) { @@ -22,13 +20,8 @@ function DataExtractValidation(values: ProjectValues) { errors.dataExtractWays = 'Data Extract Selection is Required.'; } - if ( - values.dataExtractWays && - values.dataExtractWays === 'custom_data_extract' && - !values.customPolygonUpload && - !values.customLineUpload - ) { - errors.customPolygonUpload = 'A GeoJSON file is required.'; + if (values.dataExtractWays && values.dataExtractWays === 'custom_data_extract' && !values.customDataExtractUpload) { + errors.customDataExtractUpload = 'A GeoJSON file is required.'; } return errors; diff --git a/src/frontend/src/components/organisation/OrganisationAddForm.tsx b/src/frontend/src/components/organisation/OrganisationAddForm.tsx deleted file mode 100644 index 868a4f748c..0000000000 --- a/src/frontend/src/components/organisation/OrganisationAddForm.tsx +++ /dev/null @@ -1,205 +0,0 @@ -import React from 'react'; -import CoreModules from '@/shared/CoreModules.js'; -import useForm from '@/hooks/useForm.js'; -import OrganisationAddValidation from '@/components/organisation/Validation/OrganisationAddValidation.js'; -import { MenuItem, Select } from '@mui/material'; -import { OrganisationService } from '@/api/OrganisationService'; - -const formData = {}; -const organisationTypeList = ['FREE', 'DISCOUNTED', 'FULL_FEE']; -const organisationDataList = organisationTypeList.map((item, index) => ({ label: item, value: index + 1 })); -const OrganisationAddForm = () => { - const dispatch = CoreModules.useAppDispatch(); - const defaultTheme: any = CoreModules.useAppSelector((state) => state.theme.hotTheme); - - const submission = () => { - // eslint-disable-next-line no-use-before-define - // submitForm(); - dispatch(OrganisationService(`${import.meta.env.VITE_API_URL}/organisation/`, values)); - }; - const { handleSubmit, handleCustomChange, values, errors }: any = useForm( - formData, - submission, - OrganisationAddValidation, - ); - const inputFormStyles = () => { - return { - style: { - color: defaultTheme.palette.error.main, - fontFamily: defaultTheme.typography.fontFamily, - fontSize: defaultTheme.typography.fontSize, - }, // or className: 'your-class' - }; - }; - return ( -
- - - Add Organization - - {/* Organization Name Form Input For Create Project */} - - - Name - - * - - - { - handleCustomChange('name', e.target.value); - }} - helperText={errors.name} - FormHelperTextProps={inputFormStyles()} - /> - {/* {errors.name} * */} - - - - Website - - * - - - { - handleCustomChange('url', e.target.value); - }} - helperText={errors.url} - FormHelperTextProps={inputFormStyles()} - /> - {/* {errors.name} * */} - - {/* Description Form Input For Create Project */} - - - Description - - * - - - { - handleCustomChange('description', e.target.value); - }} - multiline - rows={4} - helperText={errors.description} - FormHelperTextProps={inputFormStyles()} - /> - - {/* END */} - - - Logo - - * - - - - - - - - Type - - {/* */} - - * - - - {/* Organization */} - - - - {errors.type && ( - - {errors.type} - - )} - - - {/* END */} - - - { - // onCreateProjectSubmission(); - }} - > - Submit - - -
- ); -}; - -OrganisationAddForm.propTypes = {}; - -export default OrganisationAddForm; diff --git a/src/frontend/src/components/organisation/Validation/OrganisationAddValidation.tsx b/src/frontend/src/components/organisation/Validation/OrganisationAddValidation.tsx deleted file mode 100644 index bbd4935658..0000000000 --- a/src/frontend/src/components/organisation/Validation/OrganisationAddValidation.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { isValidUrl } from '@/utilfunctions/urlChecker'; - -interface OrganisationValues { - logo: string; - name: string; - description: string; - url: string; - type: number; - odk_central_url: string; - odk_central_user: string; - odk_central_password: string; -} -interface ValidationErrors { - logo?: string; - name?: string; - description?: string; - url?: string; - type?: string; - odk_central_url?: string; - odk_central_user?: string; - odk_central_password?: string; -} - -function OrganisationAddValidation(values: OrganisationValues) { - const errors: ValidationErrors = {}; - - if (!values?.name) { - errors.name = 'Name is Required.'; - } - - if (!values?.description) { - errors.description = 'Description is Required.'; - } - - if (!values?.url) { - errors.url = 'Organization Url is Required.'; - } else if (!isValidUrl(values.url)) { - errors.url = 'Invalid URL.'; - } - - if (values?.odk_central_url && !isValidUrl(values.odk_central_url)) { - errors.odk_central_url = 'Invalid URL.'; - } - - // if (!values?.logo) { - // errors.logo = 'Logo is Required.'; - // } - - return errors; -} - -export default OrganisationAddValidation; diff --git a/src/frontend/src/routes.jsx b/src/frontend/src/routes.jsx index 4666843eeb..338b0e8ec8 100755 --- a/src/frontend/src/routes.jsx +++ b/src/frontend/src/routes.jsx @@ -6,7 +6,6 @@ import MainView from '@/views/MainView'; import ProtectedRoute from '@/utilities/ProtectedRoute'; import NotFoundPage from '@/views/NotFound404'; import Organisation from '@/views/Organisation'; -import CreateOrganisation from '@/views/CreateOrganisation'; import CreateEditOrganization from '@/views/CreateEditOrganization'; import ApproveOrganization from '@/views/ApproveOrganization'; import Authorized from '@/views/Authorized'; @@ -41,14 +40,6 @@ const routes = createBrowserRouter([ ), }, - { - path: '/createOrganisation', - element: ( - - - - ), - }, { path: '/create-organization', element: ( diff --git a/src/frontend/src/store/types/ICreateProject.ts b/src/frontend/src/store/types/ICreateProject.ts index 95e345dede..e54c874393 100644 --- a/src/frontend/src/store/types/ICreateProject.ts +++ b/src/frontend/src/store/types/ICreateProject.ts @@ -1,7 +1,9 @@ +import { task_split_type } from '@/types/enums'; + export type CreateProjectStateTypes = { editProjectDetails: EditProjectDetailsTypes; editProjectResponse?: EditProjectResponseTypes | null; - projectDetails: ProjectDetailsTypes; + projectDetails: Partial; projectDetailsResponse: EditProjectResponseTypes | null; projectDetailsLoading: boolean; editProjectDetailsLoading: boolean; @@ -17,7 +19,7 @@ export type CreateProjectStateTypes = { generateProjectLog: GenerateProjectLogTypes | null; createProjectStep: number; dividedTaskLoading: boolean; - dividedTaskGeojson: string | null; + dividedTaskGeojson: null | GeoJSONFeatureTypes; formUpdateLoading: boolean; taskSplittingGeojsonLoading: boolean; taskSplittingGeojson: TaskSplittingGeojsonTypes | null; @@ -28,12 +30,12 @@ export type CreateProjectStateTypes = { validateCustomFormResponse: ValidateCustomFormResponse | null; uploadAreaSelection: string; totalAreaSelection: string | null; - splitTasksSelection: string | null; + splitTasksSelection: task_split_type | null; dataExtractGeojson: GeoJSONFeatureTypes | null; createProjectValidations: {}; isUnsavedChanges: boolean; canSwitchCreateProjectSteps: boolean; - isTasksGenerated: {}; + isTasksGenerated: Record; isFgbFetching: boolean; toggleSplittedGeojsonEdit: boolean; customFileValidity: boolean; @@ -53,6 +55,7 @@ export type GeoJSONFeatureTypes = { properties: Record; id: string; bbox: null | number[]; + features?: []; }; export type ProjectTaskTypes = { @@ -93,7 +96,6 @@ export type EditProjectDetailsTypes = { export type ProjectDetailsTypes = { dimension: number; - data_extract_type?: string; data_extract_url?: string; task_split_dimension?: number; task_num_buildings?: number; @@ -111,6 +113,10 @@ export type ProjectDetailsTypes = { data_extract_options?: string; form_ways?: string; organisation_id?: number | null; + formWays?: string; + formCategorySelection?: string; + average_buildings_per_task?: number; + dataExtractWays?: string; }; export type ProjectAreaTypes = { @@ -153,4 +159,5 @@ export type DrawnGeojsonTypes = { type: string; properties: null; geometry: GeometryTypes; + features?: []; }; diff --git a/src/frontend/src/store/types/IProject.ts b/src/frontend/src/store/types/IProject.ts index 53e3f345b0..4010176497 100644 --- a/src/frontend/src/store/types/IProject.ts +++ b/src/frontend/src/store/types/IProject.ts @@ -9,7 +9,7 @@ export type ProjectStateTypes = { projectLoading: boolean; projectTaskBoundries: projectTaskBoundriesType[]; newProjectTrigger: boolean; - projectInfo: projectInfoType | Record; + projectInfo: Partial; projectSubmissionLoading: boolean; projectSubmission: []; projectDataExtractLoading: boolean; diff --git a/src/frontend/src/views/CreateNewProject.tsx b/src/frontend/src/views/CreateNewProject.tsx index 4307c8de4f..253164eb9a 100644 --- a/src/frontend/src/views/CreateNewProject.tsx +++ b/src/frontend/src/views/CreateNewProject.tsx @@ -21,8 +21,7 @@ const CreateNewProject = () => { const canSwitchCreateProjectSteps = useAppSelector((state) => state.createproject.canSwitchCreateProjectSteps); const projectDetails: any = useAppSelector((state) => state.createproject.projectDetails); const [geojsonFile, setGeojsonFile] = useState(null); - const [customLineUpload, setCustomLineUpload] = useState(null); - const [customPolygonUpload, setCustomPolygonUpload] = useState(null); + const [customDataExtractUpload, setCustomDataExtractUpload] = useState(null); const [customFormFile, setCustomFormFile] = useState(null); const [dataExtractFile] = useState(null); @@ -66,8 +65,7 @@ const CreateNewProject = () => { flag="create_project" geojsonFile={geojsonFile} setGeojsonFile={setGeojsonFile} - setCustomLineUpload={setCustomLineUpload} - setCustomPolygonUpload={setCustomPolygonUpload} + setCustomDataExtractUpload={setCustomDataExtractUpload} /> ); case '/select-category': @@ -83,10 +81,8 @@ const CreateNewProject = () => { return ( ); case '/split-tasks': @@ -95,8 +91,7 @@ const CreateNewProject = () => { flag="create_project" geojsonFile={geojsonFile} setGeojsonFile={setGeojsonFile} - customLineUpload={customLineUpload} - customPolygonUpload={customPolygonUpload} + customDataExtractUpload={customDataExtractUpload} customFormFile={customFormFile} /> ); diff --git a/src/frontend/src/views/CreateOrganisation.tsx b/src/frontend/src/views/CreateOrganisation.tsx deleted file mode 100644 index 28563deb83..0000000000 --- a/src/frontend/src/views/CreateOrganisation.tsx +++ /dev/null @@ -1,208 +0,0 @@ -import React, { useEffect } from 'react'; -import CoreModules from '@/shared/CoreModules'; -import environment from '@/environment'; -import useForm from '@/hooks/useForm'; -import InputTextField from '@/components/common/InputTextField'; -import TextArea from '@/components/common/TextArea'; -import OrganisationAddValidation from '@/components/organisation/Validation/OrganisationAddValidation'; -import { PostOrganisationDataService } from '@/api/OrganisationService'; -import { useNavigate, useSearchParams } from 'react-router-dom'; -import { OrganisationAction } from '@/store/slices/organisationSlice'; -import { useAppSelector } from '@/types/reduxTypes'; - -const CreateOrganisationForm = () => { - const dispatch = CoreModules.useAppDispatch(); - const navigate = useNavigate(); - const [searchParams, setSearchParams] = useSearchParams(); - const defaultTheme: any = useAppSelector((state) => state.theme.hotTheme); - const postOrganisationData = useAppSelector((state) => state.organisation.postOrganisationData); - - const organisationFormData = useAppSelector((state) => state.organisation.organisationFormData); - - const submission = () => { - dispatch(PostOrganisationDataService(`${import.meta.env.VITE_API_URL}/organisation/`, values)); - }; - const { handleSubmit, handleCustomChange, values, errors }: any = useForm( - organisationFormData, - submission, - OrganisationAddValidation, - ); - const inputFormStyles = () => { - return { - style: { - color: defaultTheme.palette.error.main, - fontFamily: defaultTheme.typography.fontFamily, - fontSize: defaultTheme.typography.fontSize, - }, - }; - }; - - useEffect(() => { - if (postOrganisationData) { - dispatch(OrganisationAction.postOrganisationData(null)); - dispatch(OrganisationAction.SetOrganisationFormData({})); - if (searchParams.get('popup') === 'true') { - window.close(); - } else { - navigate('/organisation'); - } - } - }, [postOrganisationData]); - - return ( - -
-

CREATE NEW ORGANIZATION

-
- -
- - - { - handleCustomChange('name', e.target.value); - }} - fieldType="text" - required - errorMsg={errors.name} - /> - - - { - handleCustomChange('url', e.target.value); - }} - fieldType="text" - required - errorMsg={errors.url} - /> - - -