From 8df4020d42e9b2d7613d9e2ccceb348e08cb37dd Mon Sep 17 00:00:00 2001 From: NSUWAL123 Date: Thu, 27 Feb 2025 19:58:25 +0545 Subject: [PATCH] feat(featureComment): feature-level comment feature --- .../components/ProjectDetailsV2/Comments.tsx | 3 +- .../UpdateReviewStatusModal.tsx | 2 +- .../SubmissionInstance/SubmissionComments.tsx | 21 ++- src/frontend/src/store/types/IProject.ts | 6 +- .../components/dialog-entities-actions.svelte | 136 ++++++++++++++++-- src/mapper/src/lib/components/map/main.svelte | 2 +- .../src/lib/components/more/index.svelte | 14 +- src/mapper/src/store/entities.svelte.ts | 4 +- 8 files changed, 160 insertions(+), 28 deletions(-) diff --git a/src/frontend/src/components/ProjectDetailsV2/Comments.tsx b/src/frontend/src/components/ProjectDetailsV2/Comments.tsx index 45784eafaa..e6771a5dec 100644 --- a/src/frontend/src/components/ProjectDetailsV2/Comments.tsx +++ b/src/frontend/src/components/ProjectDetailsV2/Comments.tsx @@ -27,8 +27,9 @@ const Comments = () => { return task?.id == selectedTask; })?.[0], }; + // filter out submission/feature level comments const filteredProjectCommentsList = projectCommentsList?.filter( - (entry) => !entry?.comment?.includes('-SUBMISSION_INST-'), + (entry) => !entry?.comment?.includes('-SUBMISSION_INST-') && !entry?.comment?.startsWith('#submissionId:uuid:'), ); useEffect(() => { diff --git a/src/frontend/src/components/ProjectSubmissions/UpdateReviewStatusModal.tsx b/src/frontend/src/components/ProjectSubmissions/UpdateReviewStatusModal.tsx index 0d4cdc6826..6868299161 100644 --- a/src/frontend/src/components/ProjectSubmissions/UpdateReviewStatusModal.tsx +++ b/src/frontend/src/components/ProjectSubmissions/UpdateReviewStatusModal.tsx @@ -122,7 +122,7 @@ const UpdateReviewStatusModal = () => { `${import.meta.env.VITE_API_URL}/tasks/${updateReviewStatusModal?.taskUid}/event?project_id=${updateReviewStatusModal?.projectId}`, { task_id: +updateReviewStatusModal?.taskUid, - comment: `${updateReviewStatusModal?.instanceId}-SUBMISSION_INST-${noteComments}`, + comment: `#submissionId:${updateReviewStatusModal?.instanceId} #featureId:${updateReviewStatusModal?.entity_id} ${noteComments}`, event: task_event.COMMENT, }, ), diff --git a/src/frontend/src/components/SubmissionInstance/SubmissionComments.tsx b/src/frontend/src/components/SubmissionInstance/SubmissionComments.tsx index f83821646d..153855983f 100644 --- a/src/frontend/src/components/SubmissionInstance/SubmissionComments.tsx +++ b/src/frontend/src/components/SubmissionInstance/SubmissionComments.tsx @@ -9,10 +9,17 @@ const SubmissionComments = () => { const submissionInstanceId = params.instanceId; const taskCommentsList = useAppSelector((state) => state?.project?.projectCommentsList); - const filteredTaskCommentsList = taskCommentsList + const taskGetCommentsLoading = useAppSelector((state) => state?.project?.projectGetCommentsLoading); + + // handle for old project comments + const oldfilteredTaskCommentsList = taskCommentsList ?.filter((entry) => entry?.comment.includes('-SUBMISSION_INST-')) .filter((entry) => entry.comment.split('-SUBMISSION_INST-')[0] === submissionInstanceId); - const taskGetCommentsLoading = useAppSelector((state) => state?.project?.projectGetCommentsLoading); + const newfilteredTaskCommentsList = taskCommentsList?.filter( + (comment) => comment?.comment?.split(' ')?.[0] === `#submissionId:${submissionInstanceId}`, + ); + + const filteredTaskCommentsList = [...oldfilteredTaskCommentsList, ...newfilteredTaskCommentsList]; return (
@@ -34,7 +41,10 @@ const SubmissionComments = () => {
) : filteredTaskCommentsList?.length > 0 ? ( filteredTaskCommentsList?.map((entry) => ( -
+

{entry?.username}

@@ -42,7 +52,10 @@ const SubmissionComments = () => {

{entry?.created_at?.split('T')[0]}

-

{entry?.comment?.split('-SUBMISSION_INST-')[1]}

+

+ {entry?.comment?.split('-SUBMISSION_INST-')?.[1] || + entry?.comment?.replace(/#submissionId:uuid:[\w-]+|#featureId:[\w-]+/g, '')?.trim()} +

)) ) : ( diff --git a/src/frontend/src/store/types/IProject.ts b/src/frontend/src/store/types/IProject.ts index f8275fe30f..d1d098fd24 100644 --- a/src/frontend/src/store/types/IProject.ts +++ b/src/frontend/src/store/types/IProject.ts @@ -44,12 +44,14 @@ export type ProjectStateTypes = { }; type projectCommentsListTypes = { - id: number; + event_id: number; task_id: number; + event: string; + username: string; comment: string; created_at: string; - username: string; profile_img: string; + state: any; }; export type projectTaskActivity = { diff --git a/src/mapper/src/lib/components/dialog-entities-actions.svelte b/src/mapper/src/lib/components/dialog-entities-actions.svelte index 9fa83a60b9..bd0a74d964 100644 --- a/src/mapper/src/lib/components/dialog-entities-actions.svelte +++ b/src/mapper/src/lib/components/dialog-entities-actions.svelte @@ -8,6 +8,7 @@ import { mapTask } from '$lib/db/events'; import type { SlDialog } from '@shoelace-style/shoelace'; + type statusType = 'READY' | 'OPENED_IN_ODK' | 'SURVEY_SUBMITTED' | 'MARKED_BAD' | 'VALIDATED'; type Props = { isTaskActionModalOpen: boolean; toggleTaskActionModal: (value: boolean) => void; @@ -15,21 +16,47 @@ projectData: ProjectData; }; + function getStatusStyle(status: statusType) { + switch (status) { + case 'READY': + return 'bg-gray-100 text-gray-700'; + case 'OPENED_IN_ODK': + return 'bg-yellow-100 text-yellow-700'; + case 'SURVEY_SUBMITTED': + return 'bg-green-100 text-green-700'; + case 'MARKED_BAD': + return 'bg-red-100 text-red-700'; + case 'VALIDATED': + return 'bg-blue-100 text-blue-700'; + } + } + let { isTaskActionModalOpen, toggleTaskActionModal, selectedTab, projectData }: Props = $props(); let dialogRef: SlDialog | null = $state(null); let toggleDistanceWarningDialog = $state(false); + let showCommentsPopup: boolean = $state(false); const entitiesStore = getEntitiesStatusStore(); const alertStore = getAlertStore(); const taskStore = getTaskStore(); - const selectedEntityOsmId = $derived(entitiesStore.selectedEntity); + const selectedEntityId = $derived(entitiesStore.selectedEntity); const selectedEntity = $derived( - entitiesStore.entitiesStatusList?.find((entity) => entity.osmid === selectedEntityOsmId), + entitiesStore.entitiesStatusList?.find((entity) => entity.entity_id === selectedEntityId), ); const selectedEntityCoordinate = $derived(entitiesStore.selectedEntityCoordinate); const entityToNavigate = $derived(entitiesStore.entityToNavigate); + const entityComments = $derived( + taskStore.events + ?.filter( + (event) => + event.event === 'COMMENT' && + event.comment?.startsWith('#submissionId:uuid:') && + `#featureId:${entitiesStore.selectedEntity}` === event.comment?.split(' ')?.[1], + ) + ?.reverse(), + ); const mapFeature = () => { const xformId = projectData?.odk_form_id; @@ -118,7 +145,9 @@ {#if isTaskActionModalOpen && selectedTab === 'map' && selectedEntity}
-
+
-
-

Feature {selectedEntity?.osmid}

-
-
-

Task Id: {selectedEntity?.task_id}

-

Entity Uuid: {selectedEntity?.entity_id}

-

- Status: - {selectedEntity?.status?.replaceAll('_', ' ')} -

+

Feature {selectedEntity?.osmid}

+
+
+

Task Id

+ : +

{selectedEntity?.task_id}

+
+
+

Entity Uuid

+ : +

{selectedEntity?.entity_id}

+
+
+

Status

+ : +

+ {selectedEntity?.status?.replaceAll('_', ' ')?.toLowerCase()} +

+
+ {#if entityComments?.length > 0} +
+

Comments

+ : +
+ {#each entityComments?.slice(0, 2) as comment} +
+
+

{comment?.username}

+
+ +

{comment?.created_at?.split(' ')[0]}

+
+
+

+ {comment?.comment?.replace(/#submissionId:uuid:[\w-]+|#featureId:[\w-]+/g, '')?.trim()} +

+
+ {/each} + {#if entityComments?.length > 2} +
+
+
(showCommentsPopup = true)} + onkeydown={(e: KeyboardEvent) => { + if (e.key === 'Enter') { + showCommentsPopup = true; + } + }} + tabindex="0" + role="button" + > + See all comments +
+
+ {/if} +
+
+ {/if}
- {#if selectedEntity?.status !== 'SURVEY_SUBMITTED'} + {#if selectedEntity?.status !== 'SURVEY_SUBMITTED' && selectedEntity?.status !== 'VALIDATED'}
{/if} + + { + showCommentsPopup = false; + }} +> +
+ {#each entityComments as comment} +
+
+

{comment?.username}

+
+ +

{comment?.created_at?.split(' ')[0]}

+
+
+

+ {comment?.comment?.replace(/#submissionId:uuid:[\w-]+|#featureId:[\w-]+/g, '')?.trim()} +

+
+ {/each} +
+
diff --git a/src/mapper/src/lib/components/map/main.svelte b/src/mapper/src/lib/components/map/main.svelte index d300902685..3a402bfc99 100644 --- a/src/mapper/src/lib/components/map/main.svelte +++ b/src/mapper/src/lib/components/map/main.svelte @@ -157,7 +157,7 @@ // if clicked point contains entity then set it's osm id else set null to store if (clickedEntityFeature && clickedEntityFeature?.length > 0) { const entityCentroid = centroid(clickedEntityFeature[0].geometry); - const clickedEntityId = clickedEntityFeature[0]?.properties?.osm_id; + const clickedEntityId = clickedEntityFeature[0]?.properties?.entity_id; entitiesStore.setSelectedEntity(clickedEntityId); entitiesStore.setSelectedEntityCoordinate({ entityId: clickedEntityId, diff --git a/src/mapper/src/lib/components/more/index.svelte b/src/mapper/src/lib/components/more/index.svelte index 71fd9bb691..50485311e6 100644 --- a/src/mapper/src/lib/components/more/index.svelte +++ b/src/mapper/src/lib/components/more/index.svelte @@ -48,11 +48,21 @@ (event: TaskEventType) => event.event !== 'COMMENT' && event.task_id === taskStore?.selectedTaskId, ); comments = taskStore?.events?.filter( - (event: TaskEventType) => event.event === 'COMMENT' && event.task_id === taskStore?.selectedTaskId, + (event: TaskEventType) => + event.event === 'COMMENT' && + event.task_id === taskStore?.selectedTaskId && + !event.comment?.includes('-SUBMISSION_INST-') && + !event.comment?.startsWith('#submissionId:uuid:'), ); } else { taskEvents = taskStore?.events?.filter((event: TaskEventType) => event.event !== 'COMMENT'); - comments = taskStore?.events?.filter((event: TaskEventType) => event.event === 'COMMENT'); + comments = taskStore?.events?.filter( + (event: TaskEventType) => + event.event === 'COMMENT' && + event.task_id === taskStore?.selectedTaskId && + !event.comment?.includes('-SUBMISSION_INST-') && + !event.comment?.startsWith('#submissionId:uuid:'), + ); } }); diff --git a/src/mapper/src/store/entities.svelte.ts b/src/mapper/src/store/entities.svelte.ts index 63e3cecd10..70898d008a 100644 --- a/src/mapper/src/store/entities.svelte.ts +++ b/src/mapper/src/store/entities.svelte.ts @@ -9,7 +9,7 @@ type entitiesStatusListType = { osmid: number | undefined; entity_id: string; project_id: number; - status: string; + status: 'READY' | 'OPENED_IN_ODK' | 'SURVEY_SUBMITTED' | 'MARKED_BAD' | 'VALIDATED'; task_id: number; }; @@ -42,7 +42,7 @@ type newBadGeomType = { }; let userLocationCoord: LngLatLike | undefined = $state(); -let selectedEntity: number | null = $state(null); +let selectedEntity: string | null = $state(null); let entitiesShape: Shape; let geomShape: Shape; let entitiesStatusList: entitiesStatusListType[] = $state([]);