+
@@ -96,9 +90,7 @@ const ProjectMap = ({}) => {
zIndex={5}
/>
)}
- {buildingBoundaries?.type && (
-
- )}
+ {buildingBoundaries?.type && }
{/* )} */}
diff --git a/src/frontend/fmtm_openlayer_map/src/components/QrcodeComponent.jsx b/src/frontend/src/components/QrcodeComponent.jsx
similarity index 53%
rename from src/frontend/fmtm_openlayer_map/src/components/QrcodeComponent.jsx
rename to src/frontend/src/components/QrcodeComponent.jsx
index 02217c068a..57ebe37fcd 100755
--- a/src/frontend/fmtm_openlayer_map/src/components/QrcodeComponent.jsx
+++ b/src/frontend/src/components/QrcodeComponent.jsx
@@ -1,12 +1,11 @@
-import React, { useState } from "react";
-import BasicCard from "fmtm/BasicCard";
+import React, { useState } from 'react';
+import BasicCard from '../utilities/BasicCard';
// import Activities from "./Activities";
-import environment from "fmtm/environment";
-import { ProjectFilesById } from "../api/Files";
-import { ShareSocial } from "react-share-social";
-import CoreModules from "fmtm/CoreModules";
-import AssetModules from "fmtm/AssetModules";
-import { HomeActions } from "fmtm/HomeSlice";
+import environment from '../environment';
+import { ProjectFilesById } from '../api/Files';
+import CoreModules from '../shared/CoreModules';
+import AssetModules from '../shared/AssetModules';
+import { HomeActions } from '../store/slices/HomeSlice';
const TasksComponent = ({ type, task, defaultTheme }) => {
const dispatch = CoreModules.useAppDispatch();
@@ -14,90 +13,65 @@ const TasksComponent = ({ type, task, defaultTheme }) => {
const params = CoreModules.useParams();
const { loading, qrcode } = ProjectFilesById(
`${environment.baseApiUrl}/projects/${environment.decode(params.id)}`,
- task
+ task,
);
const socialStyles = {
copyContainer: {
- border: `1px solid ${defaultTheme.palette.info["main"]}`,
- background: defaultTheme.palette.info["info"],
- color: defaultTheme.palette.info["main"],
+ border: `1px solid ${defaultTheme.palette.info['main']}`,
+ background: defaultTheme.palette.info['info'],
+ color: defaultTheme.palette.info['main'],
},
title: {
- color: defaultTheme.palette.info["main"],
- fontStyle: "italic",
+ color: defaultTheme.palette.info['main'],
+ fontStyle: 'italic',
},
};
return (
-
+
-
-
- {`Qrcode`}
-
+
+
+ {`Qrcode`}
-
- {qrcode == "" ? (
+
+ {qrcode == '' ? (
) : (
-
+
)}
-
-
+
+
{
const linkSource = `data:image/png;base64,${qrcode}`;
- const downloadLink = document.createElement("a");
+ const downloadLink = document.createElement('a');
downloadLink.href = linkSource;
downloadLink.download = `Task_${task}`;
downloadLink.click();
}}
- disabled={qrcode == "" ? true : false}
+ disabled={qrcode == '' ? true : false}
color="info"
aria-label="download qrcode"
>
-
+
{
color="error"
onClick={() => {
document.location.href =
- "intent://getodk.org/#Intent;scheme=app;package=org.odk.collect.android;end";
+ 'intent://getodk.org/#Intent;scheme=app;package=org.odk.collect.android;end';
}}
>
Go To ODK
-
+
{
dispatch(
HomeActions.SetSnackBar({
open: true,
message: `not implemented`,
- variant: "warning",
+ variant: 'warning',
duration: 3000,
- })
+ }),
);
// setOpen(true);
}}
- disabled={qrcode == "" ? true : false}
+ disabled={qrcode == '' ? true : false}
color="info"
aria-label="share qrcode"
>
-
+
{/* {
+ const { mapRef, map } = useOLMap({
+ // center: fromLonLat([85.3, 27.7]),
+ center: [0, 0],
+ zoom: 4,
+ maxZoom: 25,
+ });
+
+ return (
+
+
+
+ {outlineBoundary?.type && (
+
+ )}
+ {featureGeojson?.type && }
+ {/* )} */}
+
+
+ );
+};
+
+SubmissionMap.propTypes = {};
+
+export default SubmissionMap;
diff --git a/src/frontend/fmtm_openlayer_map/src/layers/TasksLayer.jsx b/src/frontend/src/components/TasksLayer.jsx
similarity index 65%
rename from src/frontend/fmtm_openlayer_map/src/layers/TasksLayer.jsx
rename to src/frontend/src/components/TasksLayer.jsx
index b10514bc9e..34bea911a8 100755
--- a/src/frontend/fmtm_openlayer_map/src/layers/TasksLayer.jsx
+++ b/src/frontend/src/components/TasksLayer.jsx
@@ -1,12 +1,12 @@
-import React, { useEffect } from "react";
-import { Vector as VectorLayer } from "ol/layer.js";
-import GeoJSON from "ol/format/GeoJSON";
-import { Vector as VectorSource } from "ol/source.js";
-import { geojsonObjectModel } from "../models/geojsonObjectModel";
-import MapStyles from "../hooks/MapStyles";
-import environment from "fmtm/environment";
-import CoreModules from "fmtm/CoreModules";
-import { get } from "ol/proj";
+import React, { useEffect } from 'react';
+import { Vector as VectorLayer } from 'ol/layer.js';
+import GeoJSON from 'ol/format/GeoJSON';
+import { Vector as VectorSource } from 'ol/source.js';
+import { geojsonObjectModel } from '../models/geojsonObjectModel';
+import MapStyles from '../hooks/MapStyles';
+import environment from '../environment';
+import CoreModules from '../shared/CoreModules';
+import { get } from 'ol/proj';
let geojsonObject;
const TasksLayer = (map, view, feature) => {
const params = CoreModules.useParams();
@@ -15,37 +15,31 @@ const TasksLayer = (map, view, feature) => {
useEffect(() => {
if (state.projectTaskBoundries.length != 0 && map != undefined) {
- if (
- state.projectTaskBoundries.findIndex(
- (project) => project.id == environment.decode(params.id)
- ) != -1
- ) {
+ if (state.projectTaskBoundries.findIndex((project) => project.id == environment.decode(params.id)) != -1) {
geojsonObject = null;
- const index = state.projectTaskBoundries.findIndex(
- (project) => project.id == environment.decode(params.id)
- );
+ const index = state.projectTaskBoundries.findIndex((project) => project.id == environment.decode(params.id));
const styleFunction = function (feature) {
- let id = feature.getId().toString().replace("_", ",");
- geojsonStyles[id.split(",")[1]];
- return geojsonStyles[id.split(",")[1]];
+ let id = feature.getId().toString().replace('_', ',');
+ geojsonStyles[id.split(',')[1]];
+ return geojsonStyles[id.split(',')[1]];
};
geojsonObject = { ...geojsonObjectModel };
- geojsonObject["features"] = [];
+ geojsonObject['features'] = [];
state.projectTaskBoundries[index].taskBoundries.forEach((task) => {
- geojsonObject["features"].push({
+ geojsonObject['features'].push({
id: `${task.id}_${task.task_status_str}`,
type: task.outline_geojson.type,
geometry: task.outline_geojson.geometry,
properties: { centroid: task.bbox },
});
});
- console.log(geojsonObject, "geojsonObject");
- console.log(state.projectTaskBoundries, "projectTaskBoundries");
+ console.log(geojsonObject, 'geojsonObject');
+ console.log(state.projectTaskBoundries, 'projectTaskBoundries');
const vectorSource = new VectorSource({
features: new GeoJSON().readFeatures(geojsonObject, {
- featureProjection: get("EPSG:3857"),
+ featureProjection: get('EPSG:3857'),
}),
});
@@ -79,8 +73,8 @@ const TasksLayer = (map, view, feature) => {
padding: [50, 50, 50, 200], // Optional padding around the extent
});
map.addLayer(vectorLayer);
- map.on("loadend", function () {
- map.getTargetElement().classList.remove("spinner");
+ map.on('loadend', function () {
+ map.getTargetElement().classList.remove('spinner');
});
}
}
diff --git a/src/frontend/fmtm_openlayer_map/src/components/TasksMap/TasksMap.jsx b/src/frontend/src/components/TasksMap/TasksMap.jsx
similarity index 71%
rename from src/frontend/fmtm_openlayer_map/src/components/TasksMap/TasksMap.jsx
rename to src/frontend/src/components/TasksMap/TasksMap.jsx
index 0f7e0b622b..78322e2f3b 100644
--- a/src/frontend/fmtm_openlayer_map/src/components/TasksMap/TasksMap.jsx
+++ b/src/frontend/src/components/TasksMap/TasksMap.jsx
@@ -1,16 +1,14 @@
-import React from "react";
-import useOLMap from "../../hooks/useOlMap";
-import { MapContainer as MapComponent } from "../MapComponent/OpenLayersComponent";
-import LayerSwitcherControl from "../MapComponent/OpenLayersComponent/LayerSwitcher/index.js";
-import { VectorLayer } from "../MapComponent/OpenLayersComponent/Layers";
+import React from 'react';
+import useOLMap from '../../hooks/useOlMap';
+import { MapContainer as MapComponent } from '../MapComponent/OpenLayersComponent';
+import LayerSwitcherControl from '../MapComponent/OpenLayersComponent/LayerSwitcher/index.js';
+import { VectorLayer } from '../MapComponent/OpenLayersComponent/Layers';
function elastic(t) {
- return (
- Math.pow(2, -10 * t) * Math.sin(((t - 0.075) * (2 * Math.PI)) / 0.3) + 1
- );
+ return Math.pow(2, -10 * t) * Math.sin(((t - 0.075) * (2 * Math.PI)) / 0.3) + 1;
}
const basicGeojsonTemplate = {
- type: "FeatureCollection",
+ type: 'FeatureCollection',
features: [],
};
const TasksMap = ({ projectTaskBoundries, projectBuildingGeojson }) => {
@@ -23,17 +21,17 @@ const TasksMap = ({ projectTaskBoundries, projectBuildingGeojson }) => {
zoom: 4,
maxZoom: 25,
});
- console.log(projectTaskBoundries, "projectTaskBoundries");
+ console.log(projectTaskBoundries, 'projectTaskBoundries');
return (
-
+
diff --git a/src/frontend/src/components/common/Button.tsx b/src/frontend/src/components/common/Button.tsx
new file mode 100644
index 0000000000..cbe8cbc2f1
--- /dev/null
+++ b/src/frontend/src/components/common/Button.tsx
@@ -0,0 +1,47 @@
+import React from 'react';
+
+interface IButton {
+ btnText: string;
+ btnType: 'primary' | 'secondary' | 'other';
+ type: 'submit' | 'button';
+ onClick?: (event: React.MouseEvent) => void;
+ className?: string;
+ count?: number;
+ dataTip?: string;
+ icon?: React.ReactNode;
+}
+
+const btnStyle = (btnType, className) => {
+ switch (btnType) {
+ case 'primary':
+ return `${className} hover:fmtm-bg-red-700 fmtm-flex fmtm-px-4 fmtm-py-1 fmtm-bg-primaryRed fmtm-text-white fmtm-rounded-[8px] fmtm-w-full`;
+ case 'secondary':
+ return `hover:fmtm-bg-gray-100 fmtm-flex fmtm-bg-white fmtm-px-4 fmtm-py-1 fmtm-border border-[#E0E0E0] fmtm-rounded-[8px] ${className}`;
+
+ case 'other':
+ return `fmtm-py-1 fmtm-px-5 fmtm-bg-red-500 fmtm-text-white fmtm-rounded-lg hover:fmtm-bg-red-600`;
+
+ default:
+ return 'fmtm-primary';
+ }
+};
+const Button = ({ btnText, btnType, type, onClick, className, count, dataTip, icon }: IButton) => (
+
+
+ {btnText}
+ {count && (
+
+ {count}
+
+ )}
+ {icon && icon}
+
+
+);
+
+export default Button;
diff --git a/src/frontend/src/components/common/InputTextField.tsx b/src/frontend/src/components/common/InputTextField.tsx
new file mode 100644
index 0000000000..c029d6edb9
--- /dev/null
+++ b/src/frontend/src/components/common/InputTextField.tsx
@@ -0,0 +1,74 @@
+/* eslint-disable react/no-unstable-nested-components */
+import React from 'react';
+// import PropTypes from 'prop-types';
+export const blockInvalidChar = (e) => ['e', 'E', '+', '-'].includes(e.key) && e.preventDefault();
+
+interface IInputTextFieldProps {
+ id?: string;
+ label: string;
+ onChange: (event: React.ChangeEvent) => void;
+ onKeyDown?: (event: React.KeyboardEvent) => void;
+ errorMsg?: string;
+ value: string;
+ placeholder?: string;
+ fieldType: string;
+ name?: string;
+ flag?: string;
+ classNames?: string;
+ maxRange?: string;
+ minRange?: string;
+ maxLength?: number;
+ disabled?: boolean;
+ required?: boolean;
+}
+
+function InputTextField({
+ id,
+ label,
+ onChange,
+ onKeyDown,
+ errorMsg,
+ value,
+ placeholder,
+ fieldType,
+ name,
+ flag,
+ classNames,
+ maxRange,
+ minRange,
+ maxLength,
+ disabled,
+ required,
+}: IInputTextFieldProps) {
+ return (
+
+
+
{label}
+ {required &&
*
}
+
+
+
+
+ {errorMsg &&
{errorMsg}
}
+
+ );
+}
+
+export default InputTextField;
diff --git a/src/frontend/src/components/common/RadioButton.tsx b/src/frontend/src/components/common/RadioButton.tsx
new file mode 100644
index 0000000000..7bf3f6a207
--- /dev/null
+++ b/src/frontend/src/components/common/RadioButton.tsx
@@ -0,0 +1,43 @@
+import React from 'react';
+
+interface IRadioButton {
+ name: string;
+ value: string;
+ label: string | number;
+ icon?: React.ReactNode;
+}
+
+interface RadioButtonProps {
+ topic?: string;
+ options: IRadioButton[];
+ direction: 'row' | 'column';
+ onChangeData: (value: string) => void;
+}
+
+const RadioButton: React.FC = ({ topic, options, direction, onChangeData }) => (
+
+
+
+ {options.map((option) => {
+ return (
+
+
onChangeData(e.target.value)}
+ />
+
{option.label}
+ {option.icon && option.icon}
+
+ );
+ })}
+
+
+);
+
+export default RadioButton;
diff --git a/src/frontend/src/components/common/Select.tsx b/src/frontend/src/components/common/Select.tsx
new file mode 100644
index 0000000000..b76f343b30
--- /dev/null
+++ b/src/frontend/src/components/common/Select.tsx
@@ -0,0 +1,110 @@
+import * as React from 'react';
+import * as SelectPrimitive from '@radix-ui/react-select';
+import { Check, ChevronDown } from 'lucide-react';
+
+import { cn } from '../../utilfunctions/shadcn';
+
+const Select = SelectPrimitive.Root;
+
+const SelectGroup = SelectPrimitive.Group;
+
+const SelectValue = SelectPrimitive.Value;
+
+const SelectTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+ {children}
+
+
+
+
+));
+SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
+
+const SelectContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, position = 'popper', ...props }, ref) => (
+
+
+
+ {children}
+
+
+
+));
+SelectContent.displayName = SelectPrimitive.Content.displayName;
+
+const SelectLabel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+SelectLabel.displayName = SelectPrimitive.Label.displayName;
+
+const SelectItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+
+
+
+
+ {children}
+
+));
+SelectItem.displayName = SelectPrimitive.Item.displayName;
+
+const SelectSeparator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
+
+export { Select, SelectGroup, SelectValue, SelectTrigger, SelectContent, SelectLabel, SelectItem, SelectSeparator };
diff --git a/src/frontend/src/components/common/StepSwitcher.tsx b/src/frontend/src/components/common/StepSwitcher.tsx
new file mode 100644
index 0000000000..0d1fd1626a
--- /dev/null
+++ b/src/frontend/src/components/common/StepSwitcher.tsx
@@ -0,0 +1,65 @@
+import React from 'react';
+import AssetModules from '../../shared/AssetModules.js';
+import { CommonActions } from '../../store/slices/CommonSlice';
+import CoreModules from '../../shared/CoreModules.js';
+import { useNavigate } from 'react-router-dom';
+
+const StepSwitcher = ({ data, flag }) => {
+ interface IIndividualStep {
+ url: string;
+ step: number;
+ label: string;
+ name: string;
+ }
+
+ const dispatch = CoreModules.useAppDispatch();
+ const navigate = useNavigate();
+ const currentStep = CoreModules.useAppSelector((state) => state.common.currentStepFormStep[flag]);
+
+ const toggleStep = (step: IIndividualStep) => {
+ dispatch(CommonActions.SetCurrentStepFormStep({ flag: flag, step: step.step }));
+ navigate(step.url);
+ };
+ return (
+
+ {data?.map((step: IIndividualStep, i = 1) => {
+ const index = i + 1;
+ return (
+
+
+
+
+
{step.label}
+
{step.name}
+
+
+
= index ? 'fmtm-bg-primaryRed' : 'fmtm-bg-transparent'
+ }`}
+ onClick={() => toggleStep(step)}
+ >
+
= index ? 'fmtm-text-white' : 'fmtm-text-primaryRed'
+ } lg:fmtm-text-lg xl:fmtm-text-xl`}
+ />
+
+ {data?.length > index && (
+
= index ? 'fmtm-border-solid' : 'fmtm-border-dashed'
+ }`}
+ >
+ )}
+
+
+
+
+ );
+ })}
+
+ );
+};
+
+export default StepSwitcher;
diff --git a/src/frontend/main/src/components/common/Switch.tsx b/src/frontend/src/components/common/Switch.tsx
similarity index 100%
rename from src/frontend/main/src/components/common/Switch.tsx
rename to src/frontend/src/components/common/Switch.tsx
diff --git a/src/frontend/src/components/common/TextArea.tsx b/src/frontend/src/components/common/TextArea.tsx
new file mode 100644
index 0000000000..b65fa21170
--- /dev/null
+++ b/src/frontend/src/components/common/TextArea.tsx
@@ -0,0 +1,61 @@
+/* eslint-disable react/no-unstable-nested-components */
+import React from 'react';
+
+interface IInputTextFieldProps {
+ id?: string;
+ label: string;
+ rows: number;
+ onChange: (event: React.ChangeEvent) => void;
+ errorMsg?: string;
+ value: string;
+ placeholder?: string;
+ name?: string;
+ classNames?: string;
+ maxLength?: number;
+ disabled?: boolean;
+ required?: boolean;
+}
+
+function TextArea({
+ id,
+ label,
+ rows,
+ onChange,
+ errorMsg,
+ value,
+ placeholder,
+ name,
+ classNames,
+ maxLength,
+ disabled,
+ required,
+}: IInputTextFieldProps) {
+ return (
+
+
+
{label}
+ {required &&
*
}
+
+
+
+
+ {errorMsg &&
{errorMsg}
}
+
+ );
+}
+
+export default TextArea;
diff --git a/src/frontend/src/components/createnewproject/CreateProjectHeader.tsx b/src/frontend/src/components/createnewproject/CreateProjectHeader.tsx
new file mode 100644
index 0000000000..8349fe3c23
--- /dev/null
+++ b/src/frontend/src/components/createnewproject/CreateProjectHeader.tsx
@@ -0,0 +1,27 @@
+import React from 'react';
+import AssetModules from '../../shared/AssetModules.js';
+import { useNavigate } from 'react-router-dom';
+
+const CreateProjectHeader = () => {
+ const navigate = useNavigate();
+ return (
+
+
+
+
CREATE NEW PROJECT
+
+ Setup your field mapping project following the five comprehensive steps.
+
+
+
+
+
+ );
+};
+
+export default CreateProjectHeader;
diff --git a/src/frontend/src/components/createnewproject/DataExtract.tsx b/src/frontend/src/components/createnewproject/DataExtract.tsx
new file mode 100644
index 0000000000..fe5df7b3a3
--- /dev/null
+++ b/src/frontend/src/components/createnewproject/DataExtract.tsx
@@ -0,0 +1,120 @@
+import React, { useRef, useState } from 'react';
+import Button from '../../components/common/Button';
+import { useDispatch } from 'react-redux';
+import { CommonActions } from '../../store/slices/CommonSlice';
+import RadioButton from '../../components/common/RadioButton';
+import AssetModules from '../../shared/AssetModules.js';
+import { useNavigate } from 'react-router-dom';
+
+const dataExtractOptions = [
+ { name: 'data_extract', value: 'osm_data_extract', label: 'Use OSM data extract' },
+ { name: 'data_extract', value: 'custom_data_extract', label: 'Upload custom data extract' },
+];
+
+const osmFeatureTypeOptions = [
+ { name: 'osm_feature_type', value: 'point_centroid', label: 'Point/Centroid' },
+ { name: 'osm_feature_type', value: 'line', label: 'Line' },
+ { name: 'osm_feature_type', value: 'polygon', label: 'Polygon' },
+];
+
+const DataExtract = ({ flag }) => {
+ const dispatch = useDispatch();
+ const navigate = useNavigate();
+ const fileInputRef = useRef(null);
+
+ const [extractOption, setExtractOption] = useState({});
+
+ const toggleStep = (step, url) => {
+ dispatch(CommonActions.SetCurrentStepFormStep({ flag: flag, step: step }));
+ navigate(url);
+ };
+
+ const [selectedFileName, setSelectedFileName] = useState('');
+
+ const changeFileHandler = (event) => {
+ const { files } = event.target;
+ setSelectedFileName(files[0].name);
+ };
+ return (
+
+
+
Data Extract
+
+ Fill in your project basic information such as name, description, hashtag, etc.
+ To complete the first step, you will need the account credentials of ODK central server. {' '}
+ Here are the instructions for setting up a Central ODK Server on Digital Ocean.
+
+
+
+
+
+
+
setExtractOption(value)}
+ />
+ {extractOption === 'osm_data_extract' && (
+
+ console.log(value)}
+ />
+
+ )}
+ {extractOption === 'custom_data_extract' && (
+
+
+
fileInputRef?.current?.click()}
+ className="fmtm-bg-primaryRed fmtm-px-4 fmtm-py-1 fmtm-text-white fmtm-rounded-md"
+ >
+
+ Select a file
+
+
+
+
+
setSelectedFileName('')} />
+
+
+ {selectedFileName && (
+
+ )}
+
+ *The supported file formats are zipped shapefile, geojson or kml files.
+
+
+ )}
+
+
+ toggleStep(2, '/upload-area')}
+ className="fmtm-font-bold"
+ />
+ toggleStep(4, '/define-tasks')}
+ className="fmtm-font-bold"
+ />
+
+
+
+
+
+
+ );
+};
+
+export default DataExtract;
diff --git a/src/frontend/main/src/components/createproject/DrawSvg.tsx b/src/frontend/src/components/createnewproject/DrawSvg.tsx
similarity index 100%
rename from src/frontend/main/src/components/createproject/DrawSvg.tsx
rename to src/frontend/src/components/createnewproject/DrawSvg.tsx
diff --git a/src/frontend/src/components/createnewproject/ProjectDetailsForm.tsx b/src/frontend/src/components/createnewproject/ProjectDetailsForm.tsx
new file mode 100644
index 0000000000..d73095ff40
--- /dev/null
+++ b/src/frontend/src/components/createnewproject/ProjectDetailsForm.tsx
@@ -0,0 +1,218 @@
+import TextArea from '../../components/common/TextArea';
+import InputTextField from '../../components/common/InputTextField';
+import React, { useEffect } from 'react';
+import { CreateProjectActions } from '../../store/slices/CreateProjectSlice';
+import { useDispatch } from 'react-redux';
+import { useNavigate } from 'react-router-dom';
+import { useAppSelector } from '../../types/reduxTypes';
+import useForm from '../../hooks/useForm';
+import CreateProjectValidation from '../../components/createproject/validation/CreateProjectValidation';
+import Button from '../../components/common/Button';
+import { CommonActions } from '../../store/slices/CommonSlice';
+import AssetModules from '../../shared/AssetModules.js';
+import { createPopup } from '../../utilfunctions/createPopup';
+import {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from '../../components/common/Select';
+import { OrganisationService } from '../../api/CreateProjectService';
+import environment from '../../environment';
+
+const ProjectDetailsForm = ({ flag }) => {
+ const dispatch = useDispatch();
+ const navigate = useNavigate();
+
+ const projectDetails: any = useAppSelector((state) => state.createproject.projectDetails);
+ const organizationListData: any = useAppSelector((state) => state.createproject.organizationList);
+
+ const organizationList = organizationListData.map((item) => ({ label: item.name, value: item.id }));
+
+ const submission = () => {
+ dispatch(CreateProjectActions.SetIndividualProjectDetailsData(values));
+ dispatch(CommonActions.SetCurrentStepFormStep({ flag: flag, step: 2 }));
+ navigate('/upload-area');
+ };
+
+ const { handleSubmit, handleCustomChange, values, errors }: any = useForm(
+ projectDetails,
+ submission,
+ CreateProjectValidation,
+ );
+
+ const onFocus = () => {
+ dispatch(OrganisationService(`${environment.baseApiUrl}/organization/`));
+ };
+ useEffect(() => {
+ window.addEventListener('focus', onFocus);
+ onFocus();
+ // Calls onFocus when the window first loads
+ return () => {
+ window.removeEventListener('focus', onFocus);
+ };
+ }, []);
+
+ const hashtagPrefix = '#FMTM ';
+
+ // Checks if hashtag value starts with hotosm-fmtm'
+ const handleHashtagOnChange = (e) => {
+ let enteredText = e.target.value;
+ if (!enteredText.startsWith(hashtagPrefix)) {
+ handleCustomChange('hashtags', hashtagPrefix);
+ return;
+ }
+ handleCustomChange('hashtags', enteredText);
+ };
+
+ // Doesn't let the user to press 'Backspace' or 'Delete' if input value is 'hotosm-fmtm '
+ const handleHashtagKeyPress = (e) => {
+ if (
+ ((e.key === 'Backspace' || e.key === 'Delete') && values.hashtags === hashtagPrefix) ||
+ (e.ctrlKey && e.key === 'Backspace')
+ ) {
+ e.preventDefault();
+ }
+ };
+
+ return (
+
+
+
Project Details
+
+ Fill in your project basic information such as name, description, hashtag, etc.
+ To complete the first step, you will need the account credentials of ODK central server. {' '}
+ Here are the instructions for setting up a Central ODK Server on Digital Ocean.
+
+
+
+
+
+ );
+};
+
+export default ProjectDetailsForm;
diff --git a/src/frontend/src/components/createnewproject/SelectForm.tsx b/src/frontend/src/components/createnewproject/SelectForm.tsx
new file mode 100644
index 0000000000..0707d9bd7b
--- /dev/null
+++ b/src/frontend/src/components/createnewproject/SelectForm.tsx
@@ -0,0 +1,113 @@
+import React, { useState } from 'react';
+import { useDispatch } from 'react-redux';
+import { CommonActions } from '../../store/slices/CommonSlice';
+import Button from '../../components/common/Button';
+import {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from '../../components/common/Select';
+import { useNavigate } from 'react-router-dom';
+
+const selectFormWaysList = ['Use Existing Form', 'Upload a Custom Form'];
+
+const SelectForm = ({ flag }) => {
+ const dispatch = useDispatch();
+ const navigate = useNavigate();
+ const selectFormWays = selectFormWaysList.map((item) => ({ label: item, value: item }));
+
+ const toggleStep = (step, url) => {
+ dispatch(CommonActions.SetCurrentStepFormStep({ flag: flag, step: step }));
+ navigate(url);
+ };
+ return (
+
+
+
Select Form
+
+ Fill in your project basic information such as name, description, hashtag, etc.
+ To complete the first step, you will need the account credentials of ODK central server. {' '}
+ Here are the instructions for setting up a Central ODK Server on Digital Ocean.
+
+
+
+
+
+
+
+
Select form category
+
+
+ handleCustomChange('organisation_id', value)}
+ >
+
+
+
+
+
+ {selectFormWays?.map((formWay) => (
+
+ {formWay.label}
+
+ ))}
+
+
+
+
+
+
+
+
Select form
+
+
+ handleCustomChange('organisation_id', value)}
+ >
+
+
+
+
+
+ {selectFormWays?.map((formWay) => (
+
+ {formWay.label}
+
+ ))}
+
+
+
+
+
+
+
+
+ toggleStep(4, '/define-tasks')}
+ className="fmtm-font-bold"
+ />
+ console.log('submit')}
+ className="fmtm-font-bold"
+ />
+
+
+
+
+
+
+ );
+};
+
+export default SelectForm;
diff --git a/src/frontend/src/components/createnewproject/SplitTasks.tsx b/src/frontend/src/components/createnewproject/SplitTasks.tsx
new file mode 100644
index 0000000000..00ba2726d1
--- /dev/null
+++ b/src/frontend/src/components/createnewproject/SplitTasks.tsx
@@ -0,0 +1,119 @@
+import React, { useState } from 'react';
+import Button from '../../components/common/Button';
+import RadioButton from '../../components/common/RadioButton';
+import AssetModules from '../../shared/AssetModules.js';
+import { useDispatch } from 'react-redux';
+import { CommonActions } from '../../store/slices/CommonSlice';
+import { useNavigate } from 'react-router-dom';
+
+const dataExtractOptions = [
+ { name: 'define_tasks', value: 'divide_on_square', label: 'Divide on square' },
+ { name: 'define_tasks', value: 'choose_area_as_task', label: 'Choose area as task' },
+ { name: 'define_tasks', value: 'task_splitting_algorithm', label: 'Task Splitting Algorithm' },
+];
+
+const SplitTasks = ({ flag }) => {
+ const dispatch = useDispatch();
+ const navigate = useNavigate();
+
+ const [defineOption, setDefineOption] = useState('');
+
+ const toggleStep = (step, url) => {
+ dispatch(CommonActions.SetCurrentStepFormStep({ flag: flag, step: step }));
+ navigate(url);
+ };
+
+ return (
+
+
+
Split Tasks
+
+ Fill in your project basic information such as name, description, hashtag, etc.
+ To complete the first step, you will need the account credentials of ODK central server. {' '}
+ Here are the instructions for setting up a Central ODK Server on Digital Ocean.
+
+
+
+
+
+
+
setDefineOption(value)}
+ />
+ {defineOption === 'divide_on_square' && (
+
+
Dimension of square in metres:
+
console.log(e.target.value)}
+ className="fmtm-outline-none fmtm-border-[1px] fmtm-border-gray-600 fmtm-h-7 fmtm-w-16 fmtm-px-2 "
+ />
+
+ )}
+ {defineOption === 'task_splitting_algorithm' && (
+
+
Average number of buildings per task:
+
console.log(e.target.value)}
+ className="fmtm-outline-none fmtm-border-[1px] fmtm-border-gray-600 fmtm-h-7 fmtm-w-16 fmtm-px-2 "
+ />
+
+ )}
+ {(defineOption === 'divide_on_square' || defineOption === 'task_splitting_algorithm') && (
+
+
+
console.log('gen task')}
+ className=""
+ icon={ }
+ />
+ console.log('stop gen')}
+ className=""
+ />
+
+
+ )}
+ {defineOption && (
+
+ Total number of task: 10
+
+ )}
+
+
+ toggleStep(3, '/data-extract')}
+ className="fmtm-font-bold"
+ />
+ toggleStep(5, '/select-form')}
+ className="fmtm-font-bold"
+ />
+
+
+
+
+
+
+ );
+};
+
+export default SplitTasks;
diff --git a/src/frontend/src/components/createnewproject/UploadArea.tsx b/src/frontend/src/components/createnewproject/UploadArea.tsx
new file mode 100644
index 0000000000..1b32e8fe5b
--- /dev/null
+++ b/src/frontend/src/components/createnewproject/UploadArea.tsx
@@ -0,0 +1,133 @@
+import React, { useRef, useState } from 'react';
+import { CommonActions } from '../../store/slices/CommonSlice';
+import Button from '../../components/common/Button';
+import { useDispatch } from 'react-redux';
+import RadioButton from '../../components/common/RadioButton';
+import AssetModules from '../../shared/AssetModules.js';
+import DrawSvg from './DrawSvg';
+import { useNavigate } from 'react-router-dom';
+
+const uploadAreaOptions = [
+ {
+ name: 'upload_area',
+ value: 'draw',
+ label: 'Draw',
+ icon: ,
+ },
+ {
+ name: 'upload_area',
+ value: 'upload_file',
+ label: 'Upload File',
+ icon: ,
+ },
+];
+
+const UploadArea = ({ flag }) => {
+ const dispatch = useDispatch();
+ const navigate = useNavigate();
+ const fileInputRef = useRef(null);
+
+ const [uploadOption, setUloadOption] = useState('');
+
+ const toggleStep = (step, url) => {
+ dispatch(CommonActions.SetCurrentStepFormStep({ flag: flag, step: step }));
+ navigate(url);
+ };
+
+ const [selectedFileName, setSelectedFileName] = useState('');
+
+ const changeFileHandler = (event) => {
+ const { files } = event.target;
+ setSelectedFileName(files[0].name);
+ };
+ return (
+
+
+
Upload Area
+
+ Fill in your project basic information such as name, description, hashtag, etc.
+ To complete the first step, you will need the account credentials of ODK central server. {' '}
+ Here are the instructions for setting up a Central ODK Server on Digital Ocean.
+
+
+
+
+
+
+
setUloadOption(value)}
+ />
+ {uploadOption === 'draw' && (
+
+
Draw a polygon on the map to plot the area
+
console.log('reset')}
+ className=""
+ />
+
+ Total Area: 234 sq.km
+
+
+ )}
+ {uploadOption === 'upload_file' && (
+
+
+
fileInputRef?.current?.click()}
+ className="fmtm-bg-primaryRed fmtm-px-4 fmtm-py-1 fmtm-text-white fmtm-rounded-md"
+ >
+
+ Select a file
+
+
+
+
+
setSelectedFileName('')} />
+
+
+ {selectedFileName && (
+
+ )}
+
+ *The supported file formats are zipped shapefile, geojson or kml files.
+
+
+ Total Area: 234 sq.km
+
+
+ )}
+
+
+ toggleStep(1, '/create-project')}
+ className="fmtm-font-bold"
+ />
+ toggleStep(3, '/data-extract')}
+ className="fmtm-font-bold"
+ />
+
+
+
+
+
+
+ );
+};
+
+export default UploadArea;
diff --git a/src/frontend/main/src/components/createproject/BasemapSelection.tsx b/src/frontend/src/components/createproject/BasemapSelection.tsx
similarity index 100%
rename from src/frontend/main/src/components/createproject/BasemapSelection.tsx
rename to src/frontend/src/components/createproject/BasemapSelection.tsx
diff --git a/src/frontend/main/src/components/createproject/DataExtract.tsx b/src/frontend/src/components/createproject/DataExtract.tsx
similarity index 96%
rename from src/frontend/main/src/components/createproject/DataExtract.tsx
rename to src/frontend/src/components/createproject/DataExtract.tsx
index aec6af82ce..640e5f6cf5 100755
--- a/src/frontend/main/src/components/createproject/DataExtract.tsx
+++ b/src/frontend/src/components/createproject/DataExtract.tsx
@@ -1,6 +1,6 @@
import React, { useEffect } from 'react';
import enviroment from '../../environment';
-import CoreModules from '../../shared/CoreModules';
+import CoreModules from '../../shared/CoreModules.js';
import FormGroup from '@mui/material/FormGroup';
import { FormCategoryService } from '../../api/CreateProjectService';
import { useNavigate, Link } from 'react-router-dom';
@@ -8,7 +8,8 @@ import { CreateProjectActions } from '../../store/slices/CreateProjectSlice';
import { Grid, InputLabel, MenuItem, Select } from '@mui/material';
import useForm from '../../hooks/useForm';
//@ts-ignore
-import DefineAreaMap from 'map/DefineAreaMap';
+// import DefineAreaMap from './views/DefineAreaMap';
+import DefineAreaMap from '../../views//DefineAreaMap';
import DataExtractValidation from './validation/DataExtractValidation';
// import { SelectPicker } from 'rsuite';
@@ -76,6 +77,14 @@ const DataExtract: React.FC = ({
submission,
DataExtractValidation,
);
+ useEffect(() => {
+ if (values.data_extract_options === 'Data Extract Ways') {
+ setDataExtractFile(null);
+ setDataExtractFileValue(null);
+ setLineExtractFile(null);
+ setLineExtractFileValue(null);
+ }
+ }, [values.data_extract_options]);
return (
{
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default DrawSvg;
diff --git a/src/frontend/main/src/components/createproject/FormSelection.tsx b/src/frontend/src/components/createproject/FormSelection.tsx
similarity index 100%
rename from src/frontend/main/src/components/createproject/FormSelection.tsx
rename to src/frontend/src/components/createproject/FormSelection.tsx
diff --git a/src/frontend/main/src/components/createproject/LoadingBar.tsx b/src/frontend/src/components/createproject/LoadingBar.tsx
similarity index 79%
rename from src/frontend/main/src/components/createproject/LoadingBar.tsx
rename to src/frontend/src/components/createproject/LoadingBar.tsx
index 1d839fe1d6..d04c925a8d 100644
--- a/src/frontend/main/src/components/createproject/LoadingBar.tsx
+++ b/src/frontend/src/components/createproject/LoadingBar.tsx
@@ -44,6 +44,17 @@ const LoadingBar = ({ activeStep, totalSteps, title }) => {
borderRadius: '20%',
}}
>
+ {/*
+
+
+ {completedPercentage}%
+
+
+
*/}
import('map/DefineAreaMap'));
+const DefineAreaMap = React.lazy(() => import('../../views/DefineAreaMap'));
const UploadArea: React.FC = ({ geojsonFile, setGeojsonFile, setInputValue, inputValue }: any) => {
const navigate = useNavigate();
@@ -22,6 +22,9 @@ const UploadArea: React.FC = ({ geojsonFile, setGeojsonFile, setInputValue,
// passing payloads for creating project from form whenever user clicks submit on upload area passing previous project details form aswell
const onCreateProjectSubmission = () => {
+ console.log(drawnGeojson, 'drawnGeojson');
+ console.log(geojsonFile, 'geojsonFile');
+
if (drawnGeojson) {
dispatch(CreateProjectActions.SetCreateProjectFormStep('select-form'));
navigate('/data-extract');
@@ -119,7 +122,9 @@ const UploadArea: React.FC = ({ geojsonFile, setGeojsonFile, setInputValue,
{
+ // dispatch(CreateProjectActions.SetDividedTaskGeojson(JSON.parse(geojson)));
dispatch(CreateProjectActions.SetDrawnGeojson(JSON.parse(geojson)));
+ // setGeojsonFile(JSON.parse(geojson));
}}
/>
diff --git a/src/frontend/main/src/components/createproject/validation/CreateProjectValidation.tsx b/src/frontend/src/components/createproject/validation/CreateProjectValidation.tsx
similarity index 100%
rename from src/frontend/main/src/components/createproject/validation/CreateProjectValidation.tsx
rename to src/frontend/src/components/createproject/validation/CreateProjectValidation.tsx
diff --git a/src/frontend/main/src/components/createproject/validation/DataExtractValidation.tsx b/src/frontend/src/components/createproject/validation/DataExtractValidation.tsx
similarity index 100%
rename from src/frontend/main/src/components/createproject/validation/DataExtractValidation.tsx
rename to src/frontend/src/components/createproject/validation/DataExtractValidation.tsx
diff --git a/src/frontend/src/components/createproject/validation/DefineTaskValidation.tsx b/src/frontend/src/components/createproject/validation/DefineTaskValidation.tsx
new file mode 100644
index 0000000000..83027d4c25
--- /dev/null
+++ b/src/frontend/src/components/createproject/validation/DefineTaskValidation.tsx
@@ -0,0 +1,27 @@
+interface ProjectValues {
+ splitting_algorithm: string;
+ dimension: number;
+}
+interface ValidationErrors {
+ splitting_algorithm?: string;
+ dimension?: string;
+}
+
+function DefineTaskValidation(values: ProjectValues) {
+ const errors: ValidationErrors = {};
+
+ if (!values?.splitting_algorithm) {
+ errors.splitting_algorithm = 'Splitting Algorithm is Required.';
+ }
+ if (values?.splitting_algorithm === 'Divide on Square' && !values?.dimension) {
+ errors.dimension = 'Dimension is Required.';
+ }
+ if (values?.splitting_algorithm === 'Divide on Square' && values?.dimension && values.dimension < 9) {
+ errors.dimension = 'Dimension should be greater than 10 or equal to 10.';
+ }
+
+ console.log(errors);
+ return errors;
+}
+
+export default DefineTaskValidation;
diff --git a/src/frontend/main/src/components/createproject/validation/SelectFormValidation.tsx b/src/frontend/src/components/createproject/validation/SelectFormValidation.tsx
similarity index 99%
rename from src/frontend/main/src/components/createproject/validation/SelectFormValidation.tsx
rename to src/frontend/src/components/createproject/validation/SelectFormValidation.tsx
index 29af16990f..af4b0b2b97 100644
--- a/src/frontend/main/src/components/createproject/validation/SelectFormValidation.tsx
+++ b/src/frontend/src/components/createproject/validation/SelectFormValidation.tsx
@@ -1,4 +1,3 @@
-
interface ProjectValues {
xform_title: string;
form_ways: string;
@@ -18,7 +17,6 @@ function SelectFormValidation(values: ProjectValues) {
errors.form_ways = 'Form Selection is Required.';
}
-
console.log(errors);
return errors;
}
diff --git a/src/frontend/main/src/components/editproject/EditProjectDetails.tsx b/src/frontend/src/components/editproject/EditProjectDetails.tsx
similarity index 100%
rename from src/frontend/main/src/components/editproject/EditProjectDetails.tsx
rename to src/frontend/src/components/editproject/EditProjectDetails.tsx
diff --git a/src/frontend/main/src/components/editproject/UpdateForm.tsx b/src/frontend/src/components/editproject/UpdateForm.tsx
similarity index 100%
rename from src/frontend/main/src/components/editproject/UpdateForm.tsx
rename to src/frontend/src/components/editproject/UpdateForm.tsx
diff --git a/src/frontend/main/src/components/editproject/UpdateProjectArea.tsx b/src/frontend/src/components/editproject/UpdateProjectArea.tsx
similarity index 93%
rename from src/frontend/main/src/components/editproject/UpdateProjectArea.tsx
rename to src/frontend/src/components/editproject/UpdateProjectArea.tsx
index 5ce850c7a4..85097f8619 100644
--- a/src/frontend/main/src/components/editproject/UpdateProjectArea.tsx
+++ b/src/frontend/src/components/editproject/UpdateProjectArea.tsx
@@ -1,7 +1,7 @@
import React, { useEffect, useState } from 'react';
-import CoreModules from '../../shared/CoreModules.js';
-import AssetModules from '../../shared/AssetModules.js';
-import EditProjectArea from 'map/EditProjectArea';
+import CoreModules from '../../shared/CoreModules';
+import AssetModules from '../../shared/AssetModules';
+import EditProjectArea from '../../views/EditProjectArea';
import enviroment from '../../environment';
import { EditProjectBoundaryService, GetDividedTaskFromGeojson } from '../../api/CreateProjectService';
@@ -10,8 +10,6 @@ const UpdateProjectArea = ({ projectId }) => {
const [uploadAOI, setUploadAOI] = useState(null);
const [geojsonAOI, setGeojsonAOI] = useState(null);
const [projectBoundaryDetails, setProjectBoundaryDetails] = useState({ dimension: 10 });
- const outline_geojson = CoreModules.useAppSelector((state) => state.createproject.editProjectDetails.outline_geojson);
- const dividedTaskGeojson = CoreModules.useAppSelector((state) => state.createproject.dividedTaskGeojson);
const dividedTaskGeojsonLoading = CoreModules.useAppSelector((state) => state.createproject.dividedTaskLoading);
const updateBoundaryLoading = CoreModules.useAppSelector((state) => state.createproject.updateBoundaryLoading);
const defaultTheme: any = CoreModules.useAppSelector((state) => state.theme.hotTheme);
diff --git a/src/frontend/src/components/editproject/validation/EditProjectDetailsValidation.ts b/src/frontend/src/components/editproject/validation/EditProjectDetailsValidation.ts
new file mode 100644
index 0000000000..06a0c6676a
--- /dev/null
+++ b/src/frontend/src/components/editproject/validation/EditProjectDetailsValidation.ts
@@ -0,0 +1,56 @@
+interface ProjectValues {
+ organization: string;
+ name: string;
+ username: string;
+ id: string;
+ short_description: string;
+ description: string;
+ // odk_central_url: string;
+ // odk_central_user: string;
+ // odk_central_password: string;
+}
+interface ValidationErrors {
+ organization?: string;
+ name?: string;
+ username?: string;
+ id?: string;
+ short_description?: string;
+ description?: string;
+ // odk_central_url?: string;
+ // odk_central_user?: string;
+ // odk_central_password?: string;
+}
+const regexForSymbol = /_/g;
+
+function EditProjectValidation(values: ProjectValues) {
+ const errors: ValidationErrors = {};
+
+ // if (!values?.organization) {
+ // errors.organization = 'Organization is Required.';
+ // }
+ // if (!values?.odk_central_url) {
+ // errors.odk_central_url = 'ODK Central Url is Required.';
+ // }
+ // if (!values?.odk_central_user) {
+ // errors.odk_central_user = 'ODK Central User is Required.';
+ // }
+ // if (!values?.odk_central_password) {
+ // errors.odk_central_password = 'ODK Central Password is Required.';
+ // }
+ if (!values?.name) {
+ errors.name = 'Project Name is Required.';
+ }
+ if (values?.name && regexForSymbol.test(values.name)) {
+ errors.name = 'Project Name should not contain _.';
+ }
+ if (!values?.short_description) {
+ errors.short_description = 'Short Description is Required.';
+ }
+ if (!values?.description) {
+ errors.description = 'Description is Required.';
+ }
+
+ return errors;
+}
+
+export default EditProjectValidation;
diff --git a/src/frontend/main/src/components/home/ExploreProjectCard.tsx b/src/frontend/src/components/home/ExploreProjectCard.tsx
similarity index 100%
rename from src/frontend/main/src/components/home/ExploreProjectCard.tsx
rename to src/frontend/src/components/home/ExploreProjectCard.tsx
diff --git a/src/frontend/main/src/components/home/HomePageFilters.tsx b/src/frontend/src/components/home/HomePageFilters.tsx
similarity index 100%
rename from src/frontend/main/src/components/home/HomePageFilters.tsx
rename to src/frontend/src/components/home/HomePageFilters.tsx
diff --git a/src/frontend/src/components/home/ProjectCardSkeleton.tsx b/src/frontend/src/components/home/ProjectCardSkeleton.tsx
new file mode 100755
index 0000000000..ef1d1c0174
--- /dev/null
+++ b/src/frontend/src/components/home/ProjectCardSkeleton.tsx
@@ -0,0 +1,68 @@
+import React from 'react';
+import CoreModules from '../../shared/CoreModules';
+// Skeleton card main purpose is to perfom loading in case of any delay in retrieving project
+const ProjectCardSkeleton = ({ cardsPerRow, defaultTheme }) => {
+ return cardsPerRow.map((data, index) => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ });
+};
+export default ProjectCardSkeleton;
diff --git a/src/frontend/main/src/components/home/ProjectListMap.tsx b/src/frontend/src/components/home/ProjectListMap.tsx
similarity index 100%
rename from src/frontend/main/src/components/home/ProjectListMap.tsx
rename to src/frontend/src/components/home/ProjectListMap.tsx
diff --git a/src/frontend/main/src/components/organization/OrganizationAddForm.tsx b/src/frontend/src/components/organization/OrganizationAddForm.tsx
similarity index 100%
rename from src/frontend/main/src/components/organization/OrganizationAddForm.tsx
rename to src/frontend/src/components/organization/OrganizationAddForm.tsx
diff --git a/src/frontend/main/src/components/organization/Validation/OrganizationAddValidation.tsx b/src/frontend/src/components/organization/Validation/OrganizationAddValidation.tsx
similarity index 100%
rename from src/frontend/main/src/components/organization/Validation/OrganizationAddValidation.tsx
rename to src/frontend/src/components/organization/Validation/OrganizationAddValidation.tsx
diff --git a/src/frontend/src/constants/EditProjectSidebarContent.ts b/src/frontend/src/constants/EditProjectSidebarContent.ts
new file mode 100644
index 0000000000..cd93e9fcbc
--- /dev/null
+++ b/src/frontend/src/constants/EditProjectSidebarContent.ts
@@ -0,0 +1,26 @@
+interface ISidebarContent {
+ id: number;
+ name: string;
+ slug: string;
+ type?: string;
+}
+
+const SidebarContent: ISidebarContent[] = [
+ {
+ id: 1,
+ name: 'Project Description',
+ slug: 'project-description',
+ },
+ {
+ id: 2,
+ name: 'Form Update',
+ slug: 'form-update',
+ },
+ {
+ id: 3,
+ name: 'Update Project Boundary',
+ slug: 'update-project-boundary',
+ },
+];
+
+export default SidebarContent;
diff --git a/src/frontend/src/constants/StepFormConstants.ts b/src/frontend/src/constants/StepFormConstants.ts
new file mode 100644
index 0000000000..110ee9bcb3
--- /dev/null
+++ b/src/frontend/src/constants/StepFormConstants.ts
@@ -0,0 +1,43 @@
+interface ICreateProjectSteps {
+ url: string;
+ step: number;
+ label: string;
+ name: string;
+ children?: number;
+}
+
+export const createProjectSteps: ICreateProjectSteps[] = [
+ {
+ url: '/create-project',
+ step: 1,
+ label: '01',
+ name: 'Project Details',
+ },
+ {
+ url: '/upload-area',
+ step: 2,
+ label: '02',
+ name: 'Upload Area',
+ children: 1,
+ },
+ {
+ url: '/data-extract',
+ step: 3,
+ label: '03',
+ name: 'Data Extract',
+ children: 1,
+ },
+ {
+ url: '/define-tasks',
+ step: 4,
+ label: '04',
+ name: 'Split Tasks',
+ children: 1,
+ },
+ {
+ url: '/select-form',
+ step: 5,
+ label: '05',
+ name: 'Select Form',
+ },
+];
diff --git a/src/frontend/main/src/constants/geojsonObjectModal.ts b/src/frontend/src/constants/geojsonObjectModal.ts
similarity index 100%
rename from src/frontend/main/src/constants/geojsonObjectModal.ts
rename to src/frontend/src/constants/geojsonObjectModal.ts
diff --git a/src/frontend/main/src/environment.ts b/src/frontend/src/environment.ts
similarity index 97%
rename from src/frontend/main/src/environment.ts
rename to src/frontend/src/environment.ts
index ecc7b554c6..72b95bf22d 100755
--- a/src/frontend/main/src/environment.ts
+++ b/src/frontend/src/environment.ts
@@ -1,5 +1,4 @@
export default {
- main_url: process.env.FRONTEND_MAP_URL,
nodeEnv: process.env.NODE_ENV,
baseApiUrl: process.env.API_URL,
decode: (id: any) => {
diff --git a/src/frontend/src/hooks/MapStyles.js b/src/frontend/src/hooks/MapStyles.js
new file mode 100755
index 0000000000..57c18fe183
--- /dev/null
+++ b/src/frontend/src/hooks/MapStyles.js
@@ -0,0 +1,121 @@
+import Fill from 'ol/style/Fill';
+import Stroke from 'ol/style/Stroke';
+import { Icon, Style } from 'ol/style';
+import React, { useEffect, useState } from 'react';
+import CoreModules from '../shared/CoreModules';
+import AssetModules from '../shared/AssetModules';
+import { getCenter } from 'ol/extent';
+import Point from 'ol/geom/Point.js';
+import { transform } from 'ol/proj';
+
+function createPolygonStyle(fillColor, strokeColor) {
+ return new Style({
+ stroke: new Stroke({
+ color: strokeColor,
+ width: 3,
+ }),
+ fill: new Fill({
+ color: fillColor,
+ }),
+ });
+}
+function createIconStyle(iconSrc) {
+ return new Style({
+ image: new Icon({
+ anchor: [0.5, 1],
+ scale: 0.8,
+ anchorXUnits: 'fraction',
+ anchorYUnits: 'pixels',
+ src: iconSrc,
+ }),
+ geometry: function (feature) {
+ // return the coordinates of the centroid of the polygon
+ // const coordinates = feature.getGeometry().getExtent();
+ // const center = getCenter(coordinates);
+ const convertedCenter = transform(feature.values_.centroid, 'EPSG:4326', 'EPSG:3857');
+ return new Point(convertedCenter);
+ },
+ });
+}
+export default function MapStyles() {
+ const mapTheme = CoreModules.useAppSelector((state) => state.theme.hotTheme);
+ const [style, setStyle] = useState({});
+ const strokeColor = 'rgb(0,0,0,0.5)';
+
+ useEffect(() => {
+ // Example usage:
+ const lockedPolygonStyle = createPolygonStyle(
+ mapTheme.palette.mapFeatureColors.locked_for_mapping_rgb,
+ strokeColor,
+ );
+ const lockedValidationStyle = createPolygonStyle(
+ mapTheme.palette.mapFeatureColors.locked_for_validation_rgb,
+ strokeColor,
+ );
+ const iconStyle = createIconStyle(AssetModules.LockPng);
+ const redIconStyle = createIconStyle(AssetModules.RedLockPng);
+
+ const geojsonStyles = {
+ READY: new Style({
+ stroke: new Stroke({
+ color: strokeColor,
+ width: 3,
+ }),
+ fill: new Fill({
+ color: mapTheme.palette.mapFeatureColors.ready_rgb,
+ }),
+ }),
+ LOCKED_FOR_MAPPING: [lockedPolygonStyle, iconStyle],
+ MAPPED: new Style({
+ stroke: new Stroke({
+ color: strokeColor,
+ width: 3,
+ }),
+ fill: new Fill({
+ color: mapTheme.palette.mapFeatureColors.mapped_rgb,
+ }),
+ }),
+ LOCKED_FOR_VALIDATION: [lockedValidationStyle, redIconStyle],
+
+ VALIDATED: new Style({
+ stroke: new Stroke({
+ color: strokeColor,
+ width: 3,
+ }),
+ fill: new Fill({
+ color: mapTheme.palette.mapFeatureColors.validated_rgb,
+ }),
+ }),
+ INVALIDATED: new Style({
+ stroke: new Stroke({
+ color: strokeColor,
+ width: 3,
+ }),
+ fill: new Fill({
+ color: mapTheme.palette.mapFeatureColors.invalidated_rgb,
+ }),
+ }),
+ BAD: new Style({
+ stroke: new Stroke({
+ color: strokeColor,
+ width: 3,
+ }),
+ fill: new Fill({
+ color: mapTheme.palette.mapFeatureColors.bad_rgb,
+ }),
+ }),
+ SPLIT: new Style({
+ stroke: new Stroke({
+ color: strokeColor,
+ width: 3,
+ }),
+ fill: new Fill({
+ color: mapTheme.palette.mapFeatureColors.split_rgb,
+ }),
+ }),
+ };
+ setStyle(geojsonStyles);
+ }, []);
+
+ return style;
+}
diff --git a/src/frontend/src/hooks/OnScroll.tsx b/src/frontend/src/hooks/OnScroll.tsx
new file mode 100755
index 0000000000..7228a66d80
--- /dev/null
+++ b/src/frontend/src/hooks/OnScroll.tsx
@@ -0,0 +1,24 @@
+import React, { useEffect, useState } from 'react';
+
+export default function OnScroll(element, dep) {
+ const [scrollTop, setScrollTop] = useState(0);
+ useEffect(() => {
+ const doc = document.getElementsByClassName('mainview')[0];
+
+ const handleScroll = (event) => {
+ if (element != undefined) {
+ setScrollTop(element.getTargetElement().getBoundingClientRect().y);
+ }
+ };
+
+ doc.addEventListener('scroll', handleScroll);
+ window.addEventListener('resize', handleScroll);
+
+ return () => {
+ doc.removeEventListener('scroll', handleScroll);
+ window.removeEventListener('resize', handleScroll);
+ };
+ }, [element, dep]);
+
+ return { y: scrollTop };
+}
diff --git a/src/frontend/src/hooks/WindowDimension.tsx b/src/frontend/src/hooks/WindowDimension.tsx
new file mode 100755
index 0000000000..04f70765b5
--- /dev/null
+++ b/src/frontend/src/hooks/WindowDimension.tsx
@@ -0,0 +1,50 @@
+import React, { useEffect, useState } from 'react';
+// Extra small.col - < 576px Mobile Display
+// Small.col - sm - ≥576px Mobile Display
+// Medium.col - md - ≥768px Tablet Display
+// Large.col - lg - ≥992px Desktop Display
+// Extra large.col - xl - ≥1200px Desktop Display
+function calculateWidthType(width) {
+ if (width >= 1700) {
+ return 'xl';
+ } else if (width >= 1332) {
+ return 'lg';
+ } else if (width >= 1200) {
+ return 'md';
+ } else if (width >= 855) {
+ return 'sm';
+ } else if (width >= 632) {
+ return 's';
+ } else if (width < 632) {
+ return 'xs';
+ }
+}
+
+const windowDimention = () => {
+ const [windowSize, setWindowSize] = useState({
+ width: 0,
+ height: 0,
+ });
+
+ useEffect(() => {
+ const handleResize = () => {
+ setWindowSize({
+ width: window.innerWidth,
+ height: window.innerHeight,
+ });
+ };
+
+ handleResize();
+ window.addEventListener('resize', handleResize);
+
+ const cleanUp = () => {
+ window.removeEventListener('resize', handleResize);
+ };
+
+ return cleanUp;
+ }, []);
+
+ return { windowSize, type: calculateWidthType(windowSize.width) };
+};
+
+export default windowDimention;
diff --git a/src/frontend/main/src/hooks/useForm.tsx b/src/frontend/src/hooks/useForm.tsx
similarity index 100%
rename from src/frontend/main/src/hooks/useForm.tsx
rename to src/frontend/src/hooks/useForm.tsx
diff --git a/src/frontend/fmtm_openlayer_map/src/hooks/useOlMap.ts b/src/frontend/src/hooks/useOlMap.ts
similarity index 77%
rename from src/frontend/fmtm_openlayer_map/src/hooks/useOlMap.ts
rename to src/frontend/src/hooks/useOlMap.ts
index 3e75266bd5..70344efb12 100644
--- a/src/frontend/fmtm_openlayer_map/src/hooks/useOlMap.ts
+++ b/src/frontend/src/hooks/useOlMap.ts
@@ -1,9 +1,9 @@
/* eslint-disable consistent-return */
-import React, { useRef, useState, useEffect } from "react";
-import Map from "ol/Map";
-import { View } from "ol";
-import * as olExtent from "ol/extent";
-import VectorLayer from "ol/layer/Vector";
+import React, { useRef, useState, useEffect } from 'react';
+import Map from 'ol/Map';
+import { View } from 'ol';
+import * as olExtent from 'ol/extent';
+import VectorLayer from 'ol/layer/Vector';
const defaultProps = {
center: [0, 0],
@@ -53,14 +53,14 @@ const useOLMap = (props) => {
setTimeout(() => {
setRenderComplete(true);
}, 500);
- map.un("rendercomplete", onRenderComplete);
+ map.un('rendercomplete', onRenderComplete);
}
}
- map.on("rendercomplete", onRenderComplete);
+ map.on('rendercomplete', onRenderComplete);
return () => {
if (map) {
- map.un("rendercomplete", onRenderComplete);
+ map.un('rendercomplete', onRenderComplete);
}
};
}, [map]);
diff --git a/src/frontend/main/src/index.css b/src/frontend/src/index.css
similarity index 100%
rename from src/frontend/main/src/index.css
rename to src/frontend/src/index.css
diff --git a/src/frontend/src/index.html b/src/frontend/src/index.html
new file mode 100755
index 0000000000..9383bccfae
--- /dev/null
+++ b/src/frontend/src/index.html
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+ Field Mapping Tasking Manager
+
+
+
+
+
+
diff --git a/src/frontend/src/index.ts b/src/frontend/src/index.ts
new file mode 100755
index 0000000000..0189803552
--- /dev/null
+++ b/src/frontend/src/index.ts
@@ -0,0 +1 @@
+import('./App');
diff --git a/src/frontend/src/models/createproject/createProjectModel.ts b/src/frontend/src/models/createproject/createProjectModel.ts
new file mode 100755
index 0000000000..dfa5ba502a
--- /dev/null
+++ b/src/frontend/src/models/createproject/createProjectModel.ts
@@ -0,0 +1,81 @@
+export interface ProjectDetailsModel {
+ id: number;
+ odkid: number;
+ author: {
+ username: string;
+ id: number;
+ };
+ default_locale: string;
+ project_info: {
+ locale: string;
+ name: string;
+ short_description: string;
+ description: string;
+ instructions: string;
+ per_task_instructions: string;
+ }[];
+ status: number;
+ xform_title: string;
+ location_str: string;
+ outline_geojson: {
+ type: string;
+ geometry: {
+ coordinates: [string, string];
+ type: string;
+ };
+ properties: Record;
+ id: string;
+ bbox: [string, string, string, string];
+ };
+ project_tasks: {
+ id: number;
+ project_id: number;
+ project_task_index: number;
+ project_task_name: string;
+ outline_geojson: {
+ type: string;
+ geometry: {
+ coordinates: [string, string];
+ type: string;
+ };
+ properties: Record;
+ id: string;
+ bbox: [string, string, string, string];
+ };
+ outline_centroid: {
+ type: string;
+ geometry: {
+ coordinates: [string, string];
+ type: string;
+ };
+ properties: Record;
+ id: string;
+ bbox: [string, string, string, string];
+ };
+ task_status: number;
+ locked_by_uid: number;
+ locked_by_username: string;
+ task_history: {
+ id: number;
+ action_text: string;
+ action_date: string;
+ }[];
+ qr_code_base64: string;
+ task_status_str: string;
+ }[];
+}
+
+export interface FormCategoryListModel {
+ id: number;
+ title: string;
+}
+export interface OrganisationListModel {
+ name: string;
+ slug: string;
+ description: string;
+ type: number;
+ subscription_tier: null | string;
+ id: number;
+ logo: string;
+ url: string;
+}
diff --git a/src/frontend/src/models/geojsonObjectModel.js b/src/frontend/src/models/geojsonObjectModel.js
new file mode 100755
index 0000000000..89d8b06cbf
--- /dev/null
+++ b/src/frontend/src/models/geojsonObjectModel.js
@@ -0,0 +1,10 @@
+export const geojsonObjectModel = {
+ type: 'FeatureCollection',
+ SRID: {
+ type: 'name',
+ properties: {
+ name: 'EPSG:3857',
+ },
+ },
+ features: [],
+};
diff --git a/src/frontend/src/models/home/homeModel.ts b/src/frontend/src/models/home/homeModel.ts
new file mode 100755
index 0000000000..924259b2ac
--- /dev/null
+++ b/src/frontend/src/models/home/homeModel.ts
@@ -0,0 +1,11 @@
+export interface HomeProjectCardModel {
+ id: number;
+ priority: number;
+ title: string;
+ location_str: string;
+ description: string;
+ total_tasks: number;
+ tasks_mapped: number;
+ tasks_validated: number;
+ tasks_bad_imagery: number;
+}
diff --git a/src/frontend/src/models/organization/organizationModel.ts b/src/frontend/src/models/organization/organizationModel.ts
new file mode 100644
index 0000000000..a7795a55b1
--- /dev/null
+++ b/src/frontend/src/models/organization/organizationModel.ts
@@ -0,0 +1,42 @@
+export interface OrganizationModal {
+ name: string;
+ description: string;
+ url: string;
+ type: number;
+}
+
+export interface FormCategoryListModel {
+ id: number;
+ title: string;
+}
+export interface OrganisationListModel {
+ name: string;
+ slug: string;
+ description: string;
+ type: number;
+ subscription_tier: null | string;
+ id: number;
+ logo: string;
+ url: string;
+}
+
+export interface GetOrganizationDataModel {
+ name: string;
+ slug: string;
+ description: string;
+ type: number;
+ subscription_tier: null;
+ id: number;
+ logo: string;
+ url: string;
+}
+export interface PostOrganizationDataModel {
+ name: string;
+ slug: string;
+ description: string;
+ type: number;
+ subscription_tier: null;
+ id: number;
+ logo: string;
+ url: string;
+}
diff --git a/src/frontend/main/src/models/submission/submissionModel.ts b/src/frontend/src/models/submission/submissionModel.ts
similarity index 100%
rename from src/frontend/main/src/models/submission/submissionModel.ts
rename to src/frontend/src/models/submission/submissionModel.ts
diff --git a/src/frontend/main/src/routes.jsx b/src/frontend/src/routes.jsx
similarity index 75%
rename from src/frontend/main/src/routes.jsx
rename to src/frontend/src/routes.jsx
index 0710f68236..f2f1ed5aae 100755
--- a/src/frontend/main/src/routes.jsx
+++ b/src/frontend/src/routes.jsx
@@ -14,12 +14,13 @@ import Organization from './views/Organization';
import CreateOrganization from './views/CreateOrganization';
import Authorized from './views/Authorized';
import SubmissionDetails from './views/SubmissionDetails';
-import ProjectDetails from 'map/ProjectDetails';
+import CreateNewProject from './views/CreateNewProject';
+import ProjectDetails from './views/ProjectDetails';
-// const ProjectDetails = React.lazy(() => import('map/ProjectDetails'));
-const Submissions = React.lazy(() => import('map/Submissions'));
-const Tasks = React.lazy(() => import('map/Tasks'));
-const ProjectInfo = React.lazy(() => import('map/ProjectInfo'));
+// const ProjectDetails = React.lazy(() => import('./views/ProjectDetails'));
+const Submissions = React.lazy(() => import('./views/Submissions'));
+const Tasks = React.lazy(() => import('./views/Tasks'));
+const ProjectInfo = React.lazy(() => import('./views/ProjectInfo'));
const routes = createBrowserRouter([
{
@@ -163,6 +164,66 @@ const routes = createBrowserRouter([
),
},
+ {
+ path: '/new-create-project',
+ element: (
+
+ Loading... }>
+
+
+
+ ),
+ },
+ {
+ path: '/new-upload-area',
+ element: (
+
+ Loading... }>
+
+
+
+ ),
+ },
+ {
+ path: '/new-data-extract',
+ element: (
+
+ Loading... }>
+