Skip to content

Commit

Permalink
fix: lock map feature if 'Map Feature In ODK' clicked (#1516)
Browse files Browse the repository at this point in the history
* feat(project): updateEntityStatus add to update entity status

* feat(project): reducer function to update entityStatus and track entityUpdateLoading state

* fix(taskSlice): initial state for selectedFeatureProps

* feat(ITask): taskFeatureSelectionProperties  type add

* feat(projectDetailsV2): taskId, taskFeature props pass to featureSlectionPopup component

* feat(featureSlectionPopup): post update entity status on Map In ODK button click

* feat (project): update projectTaskBoundries locked by uid and username

* fix(projectDetailsV2): pass map & view to featureSelectionPopup as props

* feat(featureSelectionPopup): lock parent task on child entity lock for map, disable map feature in odk button under certain conditions
  • Loading branch information
NSUWAL123 authored May 24, 2024
1 parent 441dc9b commit dfb5e7b
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 36 deletions.
16 changes: 16 additions & 0 deletions src/frontend/src/api/Project.js
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,22 @@ export const GetProjectTaskActivity = (url) => {
};
};

export const UpdateEntityStatus = (url, payload) => {
return async (dispatch) => {
const updateEntityStatus = async (url, payload) => {
try {
dispatch(ProjectActions.UpdateEntityStatusLoading(true));
const response = await CoreModules.axios.post(url, payload);
dispatch(ProjectActions.UpdateEntityStatus(response.data));
dispatch(ProjectActions.UpdateEntityStatusLoading(false));
} catch (error) {
dispatch(ProjectActions.UpdateEntityStatusLoading(false));
}
};
await updateEntityStatus(url, payload);
};
};

export const DownloadSubmissionGeojson = (url, projectName) => {
return async (dispatch) => {
dispatch(ProjectActions.SetDownloadSubmissionGeojsonLoading(true));
Expand Down
8 changes: 8 additions & 0 deletions src/frontend/src/api/ProjectTaskStatus.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ const UpdateTaskStatus = (url, style, existingData, currentProjectId, feature, m
const updatedProperties = { ...prevProperties, locked_by_user: isTaskLocked ? body.id : null };
feature.setProperties(updatedProperties);

dispatch(
ProjectActions.UpdateProjectTaskBoundries({
projectId: currentProjectId,
taskId,
locked_by_uid: body?.id,
locked_by_username: body?.username,
}),
);
dispatch(CommonActions.SetLoading(false));
dispatch(
HomeActions.SetSnackBar({
Expand Down
130 changes: 99 additions & 31 deletions src/frontend/src/components/ProjectDetailsV2/FeatureSelectionPopup.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,65 @@
// Popup used to display task feature info & link to ODK Collect

import React from 'react';
import React, { useEffect, useState } from 'react';
import CoreModules from '@/shared/CoreModules';
import AssetModules from '@/shared/AssetModules';
import Button from '@/components/common/Button';
import { ProjectActions } from '@/store/slices/ProjectSlice';

type TaskFeatureSelectionProperties = {
osm_id: number;
tags: string;
timestamp: string;
version: number;
changeset: number;
};
import environment from '@/environment';
import { useParams } from 'react-router-dom';
import { UpdateEntityStatus } from '@/api/Project';
import { TaskFeatureSelectionProperties } from '@/store/types/ITask';
import ProjectTaskStatus from '@/api/ProjectTaskStatus';
import MapStyles from '@/hooks/MapStyles';

type TaskFeatureSelectionPopupPropType = {
featureProperties: TaskFeatureSelectionProperties | null;
taskId: number;
taskFeature: Record<string, any>;
map: any;
view: any;
};

const TaskFeatureSelectionPopup = ({ featureProperties }: TaskFeatureSelectionPopupPropType) => {
const TaskFeatureSelectionPopup = ({
featureProperties,
taskId,
taskFeature,
map,
view,
}: TaskFeatureSelectionPopupPropType) => {
const dispatch = CoreModules.useAppDispatch();
const params = useParams();
const geojsonStyles = MapStyles();
const taskModalStatus = CoreModules.useAppSelector((state) => state.project.taskModalStatus);
const projectInfo = CoreModules.useAppSelector((state) => state.project.projectInfo);
const entityOsmMap = CoreModules.useAppSelector((state) => state.project.entityOsmMap);

const authDetails = CoreModules.useAppSelector((state) => state.login.authDetails);
const currentProjectId = params.id;
const [task_status, set_task_status] = useState('READY');
const projectData = CoreModules.useAppSelector((state) => state.project.projectTaskBoundries);
const projectIndex = projectData.findIndex((project) => project.id == currentProjectId);
const projectTaskActivityList = CoreModules.useAppSelector((state) => state?.project?.projectTaskActivity);
const taskBoundaryData = CoreModules.useAppSelector((state) => state.project.projectTaskBoundries);
const updateEntityStatusLoading = CoreModules.useAppSelector((state) => state.project.updateEntityStatusLoading);
const currentTaskInfo = {
...taskBoundaryData?.[projectIndex]?.taskBoundries?.filter((task) => {
return task?.index == taskId;
})?.[0],
};
const geoStyle = geojsonStyles['LOCKED_FOR_MAPPING'];
const entity = entityOsmMap.find((x) => x.osm_id === featureProperties?.osm_id);

useEffect(() => {
if (projectIndex != -1) {
const currentStatus = projectTaskActivityList.length > 0 ? projectTaskActivityList[0].status : 'READY';
const findCorrectTaskStatusIndex = environment.tasksStatus.findIndex((data) => data?.label == currentStatus);
const tasksStatus =
taskFeature?.id_ != undefined ? environment?.tasksStatus[findCorrectTaskStatusIndex]?.['label'] : '';
set_task_status(tasksStatus);
}
}, [projectTaskActivityList, taskId, taskFeature, entityOsmMap]);

return (
<div
className={`fmtm-duration-1000 fmtm-z-[10002] fmtm-h-fit ${
Expand Down Expand Up @@ -76,30 +113,61 @@ const TaskFeatureSelectionPopup = ({ featureProperties }: TaskFeatureSelectionPo
</p>
</div>
</div>
{(task_status === 'READY' || task_status === 'LOCKED_FOR_MAPPING') && (
<div className="fmtm-p-2 sm:fmtm-p-5 fmtm-border-t">
<Button
btnText="MAP FEATURE IN ODK"
btnType="primary"
type="submit"
className="fmtm-font-bold !fmtm-rounded fmtm-text-sm !fmtm-py-2 !fmtm-w-full fmtm-flex fmtm-justify-center"
disabled={
(task_status === 'LOCKED_FOR_MAPPING' &&
authDetails &&
currentTaskInfo?.locked_by_uid !== authDetails?.id) ||
entity?.status !== 0
}
isLoading={updateEntityStatusLoading}
onClick={() => {
// XForm name is constructed from lower case project title with underscores
const projectName = projectInfo.title.toLowerCase().split(' ').join('_');
const projectCategory = projectInfo.xform_category;
const formName = `${projectName}_${projectCategory}`;

<div className="fmtm-p-2 sm:fmtm-p-5 fmtm-border-t">
<Button
btnText="MAP FEATURE IN ODK"
btnType="primary"
type="submit"
className="fmtm-font-bold !fmtm-rounded fmtm-text-sm !fmtm-py-2 !fmtm-w-full fmtm-flex fmtm-justify-center"
onClick={() => {
// XForm name is constructed from lower case project title with underscores
const projectName = projectInfo.title.toLowerCase().split(' ').join('_');
const projectCategory = projectInfo.xform_category;
const formName = `${projectName}_${projectCategory}`;

const entity = entityOsmMap.find((x) => x.osm_id === featureProperties?.osm_id);
const entityUuid = entity ? entity.id : null;
const entity = entityOsmMap.find((x) => x.osm_id === featureProperties?.osm_id);
const entityUuid = entity ? entity.id : null;

if (!formName || !entityUuid) {
return;
}
if (!formName || !entityUuid) {
return;
}
dispatch(
UpdateEntityStatus(`${import.meta.env.VITE_API_URL}/projects/${currentProjectId}/entity/status`, {
entity_id: entityUuid,
status: 1,
label: '',
}),
);
if (task_status === 'READY') {
dispatch(
ProjectTaskStatus(
`${import.meta.env.VITE_API_URL}/tasks/${currentTaskInfo?.id}/new-status/1`,
geoStyle,
taskBoundaryData,
currentProjectId,
taskFeature,
map,
view,
taskId,
authDetails,
{ project_id: currentProjectId },
),
);
}

document.location.href = `odkcollect://form/${formName}?existing=${entityUuid}`;
}}
/>
</div>
document.location.href = `odkcollect://form/${formName}?existing=${entityUuid}`;
}}
/>
</div>
)}
</div>
</div>
);
Expand Down
32 changes: 32 additions & 0 deletions src/frontend/src/store/slices/ProjectSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const initialState: ProjectStateTypes = {
},
entityOsmMap: [],
entityOsmMapLoading: false,
updateEntityStatusLoading: false,
projectDashboardLoading: false,
geolocationStatus: false,
projectCommentsList: [],
Expand Down Expand Up @@ -142,6 +143,37 @@ const ProjectSlice = createSlice({
UpdateProjectTaskActivity(state, action) {
state.projectTaskActivity = [action.payload, ...state.projectTaskActivity];
},
UpdateEntityStatusLoading(state, action) {
state.updateEntityStatusLoading = action.payload;
},
UpdateEntityStatus(state, action) {
const updatedEntityOsmMap = state.entityOsmMap?.map((entity) => {
if (entity.id === action.payload.id) {
return action.payload;
}
return entity;
});
state.entityOsmMap = updatedEntityOsmMap;
},
UpdateProjectTaskBoundries(state, action) {
const updatedProjectTaskBoundries = state.projectTaskBoundries?.map((boundary) => {
if (boundary.id == action.payload.projectId) {
const updatedBoundary = boundary?.taskBoundries?.map((taskBoundary) => {
if (taskBoundary?.index === action.payload.taskId) {
return {
...taskBoundary,
locked_by_uid: action.payload.locked_by_uid,
locked_by_username: action.payload.locked_by_username,
};
}
return taskBoundary;
});
return { id: boundary.id, taskBoundries: updatedBoundary };
}
return boundary;
});
state.projectTaskBoundries = updatedProjectTaskBoundries;
},
SetDownloadSubmissionGeojsonLoading(state, action) {
state.downloadSubmissionLoading = action.payload;
},
Expand Down
8 changes: 7 additions & 1 deletion src/frontend/src/store/slices/TaskSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ const initialState: TaskStateTypes = {
taskLoading: false,
taskInfo: [],
selectedTask: null,
selectedFeatureProps: null,
selectedFeatureProps: {
osm_id: 0,
tags: '',
timestamp: '',
version: 0,
changeset: 0,
},
projectBoundaryLoading: false,
projectBoundary: [],
convertToOsmLoading: false,
Expand Down
1 change: 1 addition & 0 deletions src/frontend/src/store/types/IProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export type ProjectStateTypes = {
projectDashboardDetail: projectDashboardDetailTypes;
entityOsmMap: EntityOsmMap[];
entityOsmMapLoading: boolean;
updateEntityStatusLoading: boolean;
projectDashboardLoading: boolean;
geolocationStatus: boolean;
projectCommentsList: projectCommentsListTypes[];
Expand Down
10 changes: 9 additions & 1 deletion src/frontend/src/store/types/ITask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export type TaskStateTypes = {
taskLoading: boolean;
taskInfo: taskSubmissionInfoType[];
selectedTask: number | null;
selectedFeatureProps: number | null;
selectedFeatureProps: TaskFeatureSelectionProperties;
projectBoundaryLoading: boolean;
projectBoundary: [];
convertToOsmLoading: boolean;
Expand All @@ -18,3 +18,11 @@ type downloadSubmissionLoadingTypes = {
type: '' | 'json' | 'csv';
loading: boolean;
};

export type TaskFeatureSelectionProperties = {
osm_id: number;
tags: string;
timestamp: string;
version: number;
changeset: number;
};
14 changes: 11 additions & 3 deletions src/frontend/src/views/ProjectDetailsV2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ const Home = () => {
*/
const projectClickOnTaskFeature = (properties, feature) => {
// Close task area popup, open task feature popup
setSelectedTaskArea(undefined);
// setSelectedTaskArea(undefined);
setSelectedTaskFeature(feature);

dispatch(CoreModules.TaskActions.SetSelectedFeatureProps(properties));
Expand Down Expand Up @@ -562,7 +562,7 @@ const Home = () => {
</div>
)}
</div>
{selectedTaskArea != undefined && (
{selectedTaskArea != undefined && selectedTaskFeature === undefined && (
<TaskSelectionPopup
taskId={selectedTask}
feature={selectedTaskArea}
Expand All @@ -573,7 +573,15 @@ const Home = () => {
}
/>
)}
{selectedTaskFeature != undefined && <FeatureSelectionPopup featureProperties={selectedFeatureProps} />}
{selectedTaskFeature != undefined && selectedTask && selectedTaskArea && (
<FeatureSelectionPopup
map={map}
view={mainView}
featureProperties={selectedFeatureProps}
taskId={selectedTask}
taskFeature={selectedTaskArea}
/>
)}
</div>
);
};
Expand Down

0 comments on commit dfb5e7b

Please sign in to comment.