Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature level comment #24

Merged
merged 1 commit into from
Feb 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/frontend/src/components/ProjectDetailsV2/Comments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<div className="fmtm-bg-white fmtm-rounded-xl fmtm-p-6">
Expand All @@ -34,15 +41,21 @@ const SubmissionComments = () => {
</div>
) : filteredTaskCommentsList?.length > 0 ? (
filteredTaskCommentsList?.map((entry) => (
<div className="fmtm-py-[0.875rem] fmtm-border-b fmtm-border-[#D4D4D4] fmtm-flex fmtm-flex-col fmtm-gap-2">
<div
key={entry?.event_id}
className="fmtm-py-[0.875rem] fmtm-border-b fmtm-border-[#D4D4D4] fmtm-flex fmtm-flex-col fmtm-gap-2"
>
<div className="fmtm-flex fmtm-justify-between fmtm-items-center">
<p className="fmtm-text-base fmtm-font-bold fmtm-text-[#555]">{entry?.username}</p>
<div className="fmtm-flex fmtm-items-center fmtm-gap-1">
<AssetModules.CalendarTodayOutlinedIcon style={{ fontSize: '12px' }} className="fmtm-text-[#D73F37]" />
<p className="fmtm-text-xs fmtm-text-[#555]">{entry?.created_at?.split('T')[0]}</p>
</div>
</div>
<p className="fmtm-text-[#555] fmtm-text-sm">{entry?.comment?.split('-SUBMISSION_INST-')[1]}</p>
<p className="fmtm-text-[#555] fmtm-text-sm">
{entry?.comment?.split('-SUBMISSION_INST-')?.[1] ||
entry?.comment?.replace(/#submissionId:uuid:[\w-]+|#featureId:[\w-]+/g, '')?.trim()}
</p>
</div>
))
) : (
Expand Down
6 changes: 4 additions & 2 deletions src/frontend/src/store/types/IProject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
136 changes: 121 additions & 15 deletions src/mapper/src/lib/components/dialog-entities-actions.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,55 @@
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;
selectedTab: string;
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;
Expand Down Expand Up @@ -118,7 +145,9 @@

{#if isTaskActionModalOpen && selectedTab === 'map' && selectedEntity}
<div class="font-barlow flex justify-center !w-[100vw] absolute bottom-[4rem] left-0 pointer-events-none z-50">
<div class="bg-white w-full font-regular md:max-w-[580px] pointer-events-auto px-4 py-3 sm:py-4 rounded-t-3xl">
<div
class="bg-white w-full font-regular md:max-w-[580px] pointer-events-auto px-4 py-3 sm:py-4 rounded-t-3xl max-h-[60vh] overflow-y-scroll"
>
<div class="flex justify-end">
<hot-icon
name="close"
Expand All @@ -134,21 +163,72 @@
></hot-icon>
</div>
<div class="flex flex-col gap-4">
<div class="flex items-center justify-between">
<p class="text-[#333] text-xl font-semibold">Feature {selectedEntity?.osmid}</p>
</div>
<div class="flex flex-col gap-1">
<p><span class="font-medium">Task Id:</span> {selectedEntity?.task_id}</p>
<p><span class="font-medium">Entity Uuid:</span> {selectedEntity?.entity_id}</p>
<p>
<span class="font-medium">Status:</span>
{selectedEntity?.status?.replaceAll('_', ' ')}
</p>
<p class="text-[#333] text-lg font-semibold">Feature {selectedEntity?.osmid}</p>
<div class="flex flex-col gap-2">
<div class="flex">
<p class="min-w-[6.25rem] text-[#2B2B2B]">Task Id</p>
:
<p class="text-[#161616] font-medium ml-2">{selectedEntity?.task_id}</p>
</div>
<div class="flex">
<p class="min-w-[6.25rem] text-[#2B2B2B]">Entity Uuid</p>
:
<p class="break-all text-[#161616] font-medium ml-2">{selectedEntity?.entity_id}</p>
</div>
<div class="flex items-center">
<p class="min-w-[6.25rem] text-[#2B2B2B]">Status</p>
:
<p
class={`text-[#161616] font-medium capitalize border-[1px] border-solid ml-2 py-1 px-3 rounded-full ${getStatusStyle(selectedEntity?.status)}`}
>
{selectedEntity?.status?.replaceAll('_', ' ')?.toLowerCase()}
</p>
</div>
{#if entityComments?.length > 0}
<div class="flex">
<p class="min-w-[6.25rem] text-[#2B2B2B]">Comments</p>
:
<div class="flex flex-col ml-2 gap-2 flex-1">
{#each entityComments?.slice(0, 2) as comment}
<div class="bg-[#F6F5F5] rounded px-2 py-1">
<div class="flex items-center justify-between mb-1">
<p>{comment?.username}</p>
<div class="flex items-center gap-2">
<hot-icon name="clock-history" class="!text-[0.8rem] text-red-600 cursor-pointer"></hot-icon>
<p class="text-sm">{comment?.created_at?.split(' ')[0]}</p>
</div>
</div>
<p class="font-medium">
{comment?.comment?.replace(/#submissionId:uuid:[\w-]+|#featureId:[\w-]+/g, '')?.trim()}
</p>
</div>
{/each}
{#if entityComments?.length > 2}
<div class="flex items-center gap-2">
<div class="h-[1px] bg-gray-200 flex flex-1"></div>
<div
class="text-sm text-gray-600 hover:text-gray-800 cursor-pointer font-light"
onclick={() => (showCommentsPopup = true)}
onkeydown={(e: KeyboardEvent) => {
if (e.key === 'Enter') {
showCommentsPopup = true;
}
}}
tabindex="0"
role="button"
>
See all comments
</div>
</div>
{/if}
</div>
</div>
{/if}
</div>
{#if selectedEntity?.status !== 'SURVEY_SUBMITTED'}
{#if selectedEntity?.status !== 'SURVEY_SUBMITTED' && selectedEntity?.status !== 'VALIDATED'}
<div class="flex gap-2">
<sl-button
disabled={entityToNavigate?.entityId === selectedEntity?.osmid}
disabled={entityToNavigate?.entityId === selectedEntity?.entity_id}
variant="default"
size="small"
class="secondary flex-grow"
Expand Down Expand Up @@ -254,3 +334,29 @@
</div>
</hot-dialog>
{/if}

<hot-dialog
label="Feature Comments"
class="dialog-overview z-50 font-barlow font-regular"
open={showCommentsPopup}
onsl-hide={() => {
showCommentsPopup = false;
}}
>
<div class="flex flex-col gap-3">
{#each entityComments as comment}
<div class="bg-[#F6F5F5] rounded px-2 py-1">
<div class="flex items-center justify-between mb-2">
<p>{comment?.username}</p>
<div class="flex items-center gap-2">
<hot-icon name="clock-history" class="!text-[0.8rem] text-red-600 cursor-pointer"></hot-icon>
<p class="text-sm">{comment?.created_at?.split(' ')[0]}</p>
</div>
</div>
<p class="font-medium">
{comment?.comment?.replace(/#submissionId:uuid:[\w-]+|#featureId:[\w-]+/g, '')?.trim()}
</p>
</div>
{/each}
</div>
</hot-dialog>
2 changes: 1 addition & 1 deletion src/mapper/src/lib/components/map/main.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
14 changes: 12 additions & 2 deletions src/mapper/src/lib/components/more/index.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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:'),
);
}
});
</script>
Expand Down
4 changes: 2 additions & 2 deletions src/mapper/src/store/entities.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

Expand Down Expand Up @@ -42,7 +42,7 @@ type newBadGeomType<T> = {
};

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([]);
Expand Down