Skip to content

Commit

Permalink
Feature validation button add (#2040)
Browse files Browse the repository at this point in the history
* 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
NSUWAL123 authored Dec 30, 2024
1 parent 681343b commit f28296e
Show file tree
Hide file tree
Showing 12 changed files with 88 additions and 166 deletions.
2 changes: 1 addition & 1 deletion src/frontend/src/api/Project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ export const GetSubmissionDashboard = (url: string) => {
};
};

export const GetEntityInfo = (url: string) => {
export const GetEntityStatusList = (url: string) => {
return async (dispatch) => {
const getEntityOsmMap = async (url: string) => {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ const ActivitiesPanel = ({ defaultTheme, state, params, map }: activitiesPanelTy

return (
<div className="fmtm-w-full fmtm-px-2 sm:fmtm-px-0 fmtm-relative sm:fmtm-overflow-y-scroll scrollbar">
<div className="fmtm-sticky fmtm-overflow-y-scroll scrollbar sm:fmtm-overflow-visible -fmtm-top-[2px] sm:fmtm-top-0 fmtm-bg-white sm:fmtm-bg-[#F5F5F5]">
<div className="fmtm-sticky fmtm-overflow-y-scroll scrollbar sm:fmtm-overflow-visible -fmtm-top-[2px] sm:fmtm-top-0 fmtm-bg-white md:fmtm-bg-[#F5F5F5]">
<div className="fmtm-flex fmtm-items-center fmtm-w-full fmtm-justify-between fmtm-gap-4">
<input
type="text"
Expand Down
199 changes: 67 additions & 132 deletions src/frontend/src/components/ProjectDetailsV2/FeatureSelectionPopup.tsx
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;
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ const MobileFooter = () => {
);
};
return (
<div className="fmtm-absolute fmtm-bottom-0 sm:fmtm-hidden fmtm-w-full fmtm-border-t-[1px] fmtm-z-[10008]">
<div className="fmtm-absolute fmtm-bottom-0 md:fmtm-hidden fmtm-w-full fmtm-border-t-[1px] fmtm-z-[10008]">
<div
className={`fmtm-w-full fmtm-grid ${
taskModalStatus ? 'fmtm-grid-cols-5' : 'fmtm-grid-cols-4'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const TaskSelectionPopup = ({ taskId, body, feature }: TaskSelectionPopupPropTyp
<div
className={`fmtm-duration-1000 fmtm-z-[10002] fmtm-h-fit ${
taskModalStatus
? 'fmtm-bottom-[4.4rem] sm:fmtm-bottom-0 lg:fmtm-top-[50%] md:-fmtm-translate-y-[35%] fmtm-right-0 fmtm-w-[100vw] md:fmtm-w-[50vw] md:fmtm-max-w-[25rem]'
? 'fmtm-bottom-[4.4rem] md:fmtm-bottom-0 lg: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`}
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/src/components/common/BottomSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const BottomSheet = ({ body, onClose }: bottomSheetType) => {
}, [currSheetHeight]);

return (
<div className="fmtm-absolute fmtm-bottom-[200px] fmtm-bg-white sm:fmtm-hidden fmtm-z-[10005]">
<div className="fmtm-absolute fmtm-bottom-[200px] fmtm-bg-white md:fmtm-hidden fmtm-z-[10005]">
<div
className={`bottom-sheet fmtm-fixed fmtm-w-full fmtm-left-0 fmtm-bottom-0 fmtm-flex fmtm-items-center fmtm-flex-col fmtm-justify-end fmtm-duration-100 fmtm-ease-linear ${
!show ? 'fmtm-opacity-0 fmtm-pointer-events-none' : 'fmtm-opacity-100 fmtm-pointer-events-auto'
Expand Down
3 changes: 1 addition & 2 deletions src/frontend/src/models/project/projectModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,9 @@ export type projectInfoType = {
export type downloadProjectFormLoadingType = { type: 'form' | 'geojson' | 'csv' | 'json'; loading: boolean };

export type projectDashboardDetailTypes = {
name: string;
slug: string;
organisation_name: string;
total_tasks: number | null;
total_tasks: number;
created_at: string;
organisation_logo: string;
total_submissions: number | null;
Expand Down
11 changes: 1 addition & 10 deletions src/frontend/src/store/slices/ProjectSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,7 @@ const initialState: ProjectStateTypes = {
toggleGenerateMbTilesModal: false,
mobileFooterSelection: 'explore',
projectDetailsLoading: true,
projectDashboardDetail: {
slug: '',
organisation_name: '',
total_tasks: null,
created_at: '',
organisation_logo: '',
total_submissions: null,
total_contributors: null,
last_active: '',
},
projectDashboardDetail: null,
entityOsmMap: [],
entityOsmMapLoading: false,
updateEntityStateLoading: false,
Expand Down
5 changes: 3 additions & 2 deletions src/frontend/src/store/types/IProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export type ProjectStateTypes = {
toggleGenerateMbTilesModal: boolean;
mobileFooterSelection: string;
projectDetailsLoading: boolean;
projectDashboardDetail: projectDashboardDetailTypes;
projectDashboardDetail: projectDashboardDetailTypes | null;
entityOsmMap: EntityOsmMap[];
entityOsmMapLoading: boolean;
updateEntityStateLoading: boolean;
Expand All @@ -41,10 +41,11 @@ export type ProjectStateTypes = {

export type EntityOsmMap = {
id: string;
osm_id: string;
osm_id: number;
status: number;
task_id: number;
updated_at: string;
submission_ids: string;
};

type tilesListTypes = {
Expand Down
6 changes: 3 additions & 3 deletions src/frontend/src/views/MainView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const MainView = () => {
<CoreModules.Paper>
<CoreModules.Container disableGutters={true} maxWidth={false}>
<CoreModules.Stack sx={{ height: '100vh' }}>
{popupInParams === 'true' || (pathname.includes('/project/') && windowSize.width <= 640) ? (
{popupInParams === 'true' || (pathname.includes('/project/') && windowSize.width <= 768) ? (
<div></div>
) : (
<div>
Expand All @@ -68,12 +68,12 @@ const MainView = () => {
) : (
<CoreModules.Stack
className={`${
pathname.includes('/project/') && windowSize.width < 640 ? '' : 'fmtm-p-6'
pathname.includes('/project/') && windowSize.width < 768 ? '' : 'fmtm-p-6'
} fmtm-bg-[#f5f5f5]`}
sx={{
height: popupInParams
? '100vh'
: pathname.includes('project/') && windowSize.width <= 640
: pathname.includes('project/') && windowSize.width <= 768
? '100vh'
: windowSize.width <= 599
? '90vh'
Expand Down
Loading

0 comments on commit f28296e

Please sign in to comment.