-
-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feature validation button add (#2040)
* fix(featureSelectionPopup): remove map feature in odk from management frontend * fix(project): update function name GetEntityInfo to GetEntityStatusList * fix(projectModel): update projectDashboardDetailTypes type * fix(projectDetails): small medium screen style fix * fix(project): update ts type * fix(projectDetailsV2): remove taskFeature prop * feat(featureSelectionPopup): remove taskFeature prop, refactor code, add validate feature button
- Loading branch information
Showing
12 changed files
with
88 additions
and
166 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
199 changes: 67 additions & 132 deletions
199
src/frontend/src/components/ProjectDetailsV2/FeatureSelectionPopup.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,176 +1,111 @@ | ||
// Popup used to display task feature info & link to ODK Collect | ||
|
||
import React, { useEffect, useState } from 'react'; | ||
import React from 'react'; | ||
import { Link, useParams } from 'react-router-dom'; | ||
import { useAppSelector } from '@/types/reduxTypes'; | ||
import CoreModules from '@/shared/CoreModules'; | ||
import AssetModules from '@/shared/AssetModules'; | ||
import { CommonActions } from '@/store/slices/CommonSlice'; | ||
import Button from '@/components/common/Button'; | ||
import { ProjectActions } from '@/store/slices/ProjectSlice'; | ||
import environment from '@/environment'; | ||
import { useParams } from 'react-router-dom'; | ||
import { UpdateEntityState } from '@/api/Project'; | ||
import { TaskFeatureSelectionProperties } from '@/store/types/ITask'; | ||
import { CreateTaskEvent } from '@/api/TaskEvent'; | ||
import MapStyles from '@/hooks/MapStyles'; | ||
import { task_event, task_state as taskStateEnum } from '@/types/enums'; | ||
|
||
type TaskFeatureSelectionPopupPropType = { | ||
type FeatureSelectionPopupPropType = { | ||
taskId: number; | ||
featureProperties: TaskFeatureSelectionProperties | null; | ||
taskFeature: Record<string, any>; | ||
}; | ||
|
||
const TaskFeatureSelectionPopup = ({ featureProperties, taskId, taskFeature }: TaskFeatureSelectionPopupPropType) => { | ||
const FeatureSelectionPopup = ({ featureProperties, taskId }: FeatureSelectionPopupPropType) => { | ||
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_state, set_task_state] = useState(taskStateEnum.UNLOCKED_TO_MAP); | ||
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 updateEntityStateLoading = CoreModules.useAppSelector((state) => state.project.updateEntityStateLoading); | ||
const currentTaskInfo = { | ||
...projectData?.[projectIndex]?.taskBoundries?.filter((task) => { | ||
return task?.id == taskId; | ||
})?.[0], | ||
}; | ||
const taskModalStatus = useAppSelector((state) => state.project.taskModalStatus); | ||
const entityOsmMap = useAppSelector((state) => state.project.entityOsmMap); | ||
const projectId = params.id || ''; | ||
const entity = entityOsmMap.find((x) => x.osm_id === featureProperties?.osm_id); | ||
|
||
useEffect(() => { | ||
if (projectIndex != -1) { | ||
const currentStatus = | ||
projectTaskActivityList.length > 0 ? projectTaskActivityList[0].state : taskStateEnum.UNLOCKED_TO_MAP; | ||
const findCorrectTaskStatusIndex = environment.tasksStatus.findIndex((data) => data?.label == currentStatus); | ||
const tasksStatus = | ||
taskFeature?.id_ != undefined ? environment?.tasksStatus[findCorrectTaskStatusIndex]?.['label'] : ''; | ||
set_task_state(tasksStatus); | ||
} | ||
}, [projectTaskActivityList, taskId, taskFeature, entityOsmMap]); | ||
const submissionIds = entity?.submission_ids ? entity?.submission_ids?.split(',') : []; | ||
|
||
return ( | ||
<div | ||
className={`fmtm-duration-1000 fmtm-z-[10002] fmtm-h-fit ${ | ||
taskModalStatus | ||
? 'fmtm-bottom-[4.4rem] md:fmtm-top-[50%] md:-fmtm-translate-y-[35%] fmtm-right-0 fmtm-w-[100vw] md:fmtm-w-[50vw] md:fmtm-max-w-[25rem]' | ||
: 'fmtm-top-[calc(100vh)] md:fmtm-top-[calc(40vh)] md:fmtm-left-[calc(100vw)] fmtm-w-[100vw]' | ||
} fmtm-fixed | ||
fmtm-rounded-t-3xl fmtm-border-opacity-50`} | ||
? 'fmtm-bottom-[4.4rem] md:fmtm-top-[50%] md:-fmtm-translate-y-[35%] fmtm-right-0 fmtm-w-[100vw] md:fmtm-w-[50vw] md:fmtm-max-w-fit' | ||
: 'fmtm-top-[calc(100vh)] md:fmtm-top-[calc(40vh)] md:fmtm-left-[calc(100vw)] fmtm-w-[100vw] md:fmtm-max-w-[23rem]' | ||
} fmtm-fixed fmtm-rounded-t-3xl fmtm-border-opacity-50`} | ||
> | ||
<div | ||
className={`fmtm-absolute fmtm-top-[17px] fmtm-right-[20px] ${ | ||
taskModalStatus ? '' : 'fmtm-hidden' | ||
} fmtm-cursor-pointer fmtm-flex fmtm-items-center fmtm-gap-3`} | ||
> | ||
<div title="Close"> | ||
<AssetModules.CloseIcon | ||
style={{ width: '20px' }} | ||
className="hover:fmtm-text-primaryRed" | ||
onClick={() => dispatch(ProjectActions.ToggleTaskModalStatus(false))} | ||
/> | ||
</div> | ||
</div> | ||
<div | ||
className={`fmtm-bg-[#fbfbfb] ${ | ||
taskModalStatus ? 'sm:fmtm-shadow-[-20px_0px_60px_25px_rgba(0,0,0,0.2)] fmtm-border-b sm:fmtm-border-b-0' : '' | ||
} fmtm-rounded-t-2xl md:fmtm-rounded-tr-none md:fmtm-rounded-l-2xl`} | ||
> | ||
<div className="fmtm-flex fmtm-flex-col fmtm-gap-2 fmtm-p-3 sm:fmtm-p-5"> | ||
<div className="fmtm-flex fmtm-justify-between fmtm-items-center fmtm-gap-2 fmtm-px-3 sm:fmtm-px-5 fmtm-py-2"> | ||
<h4 className="fmtm-text-lg fmtm-font-bold">Feature: {featureProperties?.osm_id}</h4> | ||
<div title="Close"> | ||
<AssetModules.CloseIcon | ||
style={{ width: '20px' }} | ||
className="hover:fmtm-text-primaryRed fmtm-cursor-pointer" | ||
onClick={() => dispatch(ProjectActions.ToggleTaskModalStatus(false))} | ||
/> | ||
</div> | ||
</div> | ||
|
||
<div className="fmtm-h-fit fmtm-p-2 sm:fmtm-p-5 fmtm-border-t"> | ||
<div className="fmtm-h-fit fmtm-px-2 sm:fmtm-px-5 fmtm-py-2 fmtm-border-t"> | ||
<div className="fmtm-flex fmtm-flex-col fmtm-gap-1 fmtm-mt-1"> | ||
<p> | ||
<span className="fmtm-font-semibold">Tags: </span> | ||
<span className="fmtm-text-primaryRed fmtm-overflow-hidden fmtm-line-clamp-2"> | ||
{featureProperties?.tags} | ||
</span> | ||
<span>Tags: </span> | ||
<span className="fmtm-overflow-hidden fmtm-line-clamp-2">{featureProperties?.tags}</span> | ||
</p> | ||
<p> | ||
<span className="fmtm-font-semibold">Timestamp: </span> | ||
<span className="fmtm-text-primaryRed">{featureProperties?.timestamp}</span> | ||
<span>Timestamp: </span> | ||
<span>{featureProperties?.timestamp}</span> | ||
</p> | ||
<p> | ||
<span className="fmtm-font-semibold">Changeset: </span> | ||
<span className="fmtm-text-primaryRed">{featureProperties?.changeset}</span> | ||
<span>Changeset: </span> | ||
<span>{featureProperties?.changeset}</span> | ||
</p> | ||
<p> | ||
<span className="fmtm-font-semibold">Version: </span> | ||
<span className="fmtm-text-primaryRed">{featureProperties?.version}</span> | ||
<span>Version: </span> | ||
<span>{featureProperties?.version}</span> | ||
</p> | ||
</div> | ||
</div> | ||
{(task_state === taskStateEnum.UNLOCKED_TO_MAP || task_state === taskStateEnum.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={entity?.status !== 0} | ||
isLoading={updateEntityStateLoading} | ||
onClick={() => { | ||
const xformId = projectInfo.odk_form_id; | ||
const entity = entityOsmMap.find((x) => x.osm_id === featureProperties?.osm_id); | ||
const entityUuid = entity ? entity.id : null; | ||
|
||
if (!xformId || !entityUuid) { | ||
return; | ||
} | ||
|
||
dispatch( | ||
UpdateEntityState(`${import.meta.env.VITE_API_URL}/projects/${currentProjectId}/entity/status`, { | ||
entity_id: entityUuid, | ||
status: 1, | ||
label: `Task ${taskId} Feature ${entity.osm_id}`, | ||
}), | ||
); | ||
|
||
if (task_state === taskStateEnum.UNLOCKED_TO_MAP) { | ||
dispatch( | ||
CreateTaskEvent( | ||
`${import.meta.env.VITE_API_URL}/tasks/${currentTaskInfo?.id}/event`, | ||
task_event.MAP, | ||
currentProjectId, | ||
taskId.toString(), | ||
authDetails, | ||
{ project_id: currentProjectId }, | ||
geojsonStyles, | ||
taskFeature, | ||
), | ||
); | ||
} | ||
|
||
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test( | ||
navigator.userAgent, | ||
); | ||
|
||
if (isMobile) { | ||
// Load entity in ODK Collect by intent | ||
document.location.href = `odkcollect://form/${xformId}?feature=${entityUuid}`; | ||
} else { | ||
dispatch( | ||
CommonActions.SetSnackBar({ | ||
open: true, | ||
message: 'Requires a mobile phone with ODK Collect.', | ||
variant: 'warning', | ||
duration: 3000, | ||
}), | ||
); | ||
} | ||
}} | ||
/> | ||
</div> | ||
)} | ||
{!submissionIds || | ||
(submissionIds?.length !== 0 && ( | ||
<div className="fmtm-px-2 sm:fmtm-px-5 fmtm-py-3 fmtm-border-t fmtm-flex fmtm-flex-col fmtm-gap-3"> | ||
{submissionIds?.length > 1 ? ( | ||
<> | ||
{submissionIds?.map((submissionId, index) => ( | ||
<div | ||
key={submissionId} | ||
className="fmtm-flex fmtm-flex-col sm:fmtm-flex-row md:fmtm-flex-col sm:fmtm-justify-between sm:fmtm-items-end md:fmtm-items-stretch fmtm-gap-1" | ||
> | ||
<div> | ||
<p className="fmtm-border-b fmtm-w-fit fmtm-border-primaryRed fmtm-leading-5 fmtm-mb-1"> | ||
Submission #{index + 1} | ||
</p> | ||
<p className="">ID: {submissionId?.replace('uuid:', '')}</p> | ||
</div> | ||
<Link to={`/project-submissions/${projectId}/tasks/${taskId}/submission/${submissionId}`}> | ||
<Button | ||
btnText="validate this feature" | ||
btnType="other" | ||
className="fmtm-font-bold !fmtm-rounded fmtm-text-sm fmtm-flex fmtm-justify-center fmtm-uppercase !fmtm-w-fit md:!fmtm-w-full" | ||
/> | ||
</Link> | ||
</div> | ||
))} | ||
</> | ||
) : ( | ||
<Link to={`/project-submissions/${projectId}/tasks/${taskId}/submission/${submissionIds}`}> | ||
<Button | ||
btnText="validate this feature" | ||
btnType="other" | ||
className="fmtm-font-bold !fmtm-rounded fmtm-text-sm fmtm-flex fmtm-justify-center fmtm-uppercase fmtm-w-fit md:!fmtm-w-full fmtm-mx-auto" | ||
/> | ||
</Link> | ||
)} | ||
</div> | ||
))} | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default TaskFeatureSelectionPopup; | ||
export default FeatureSelectionPopup; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.