From feae2a28b0546d88b0c29826eb62c66284a871be Mon Sep 17 00:00:00 2001
From: Nishit Suwal <81785002+NSUWAL123@users.noreply.github.com>
Date: Thu, 16 Jan 2025 15:26:23 +0545
Subject: [PATCH] feat(frontend): submission table date range filter (#2091)
* fix(ISubmissions): update ts type
* fix(customDatePicker): add start and end date range
* fix(submissionTable): update submitted date with dateRange
* fix(customDatePicker): update component name
* fix(submissionsTable): pass query strings as object instead of static string
* fix(task): update getDownloadProjectSubmission service function
* fix(submissionsTable): update initial filter state, clear dateRange on filter clear
* fix(projectSubmissionsSkeletonLoader): fix index clash error
* fix(submissionsTable): remove useEffect block calculating no of filters applied
* fix(select): update ts type
* fix(submissionsTable): fallback to empty string if not values present on custom select
* fix(index): update datepicker css
---
src/frontend/src/api/task.ts | 17 ++---
.../ProjectSubmissionsSkeletonLoader.tsx | 4 +-
.../ProjectSubmissions/SubmissionsTable.tsx | 72 +++++++++----------
...stomDatePicker.tsx => DateRangePicker.tsx} | 24 +++++--
src/frontend/src/components/common/Select.tsx | 2 +-
src/frontend/src/index.css | 12 ++--
src/frontend/src/store/types/ISubmissions.ts | 2 +-
7 files changed, 73 insertions(+), 60 deletions(-)
rename src/frontend/src/components/common/{CustomDatePicker.tsx => DateRangePicker.tsx} (51%)
diff --git a/src/frontend/src/api/task.ts b/src/frontend/src/api/task.ts
index 5f2a7dabed..c77f0111b5 100644
--- a/src/frontend/src/api/task.ts
+++ b/src/frontend/src/api/task.ts
@@ -2,11 +2,14 @@ import { AppDispatch } from '@/store/Store';
import CoreModules from '@/shared/CoreModules';
import { TaskActions } from '@/store/slices/TaskSlice';
-export const getDownloadProjectSubmission = (url: string, projectName: string) => {
+export const getDownloadProjectSubmission = (
+ url: string,
+ projectName: string,
+ params: { project_id: string; export_json: boolean; submitted_date_range: string | null },
+) => {
return async (dispatch: AppDispatch) => {
- const params = new URLSearchParams(url.split('?')[1]);
- const isExportJson = params.get('export_json');
- const isJsonOrCsv = isExportJson === 'true' ? 'json' : 'csv';
+ const isExportJson = params.export_json;
+ const isJsonOrCsv = isExportJson ? 'json' : 'csv';
dispatch(
TaskActions.GetDownloadProjectSubmissionLoading({
type: isJsonOrCsv,
@@ -16,12 +19,10 @@ export const getDownloadProjectSubmission = (url: string, projectName: string) =
const getProjectSubmission = async (url: string) => {
try {
- const response = await CoreModules.axios.get(url, {
- responseType: 'blob',
- });
+ const response = await CoreModules.axios.get(url, { params, responseType: 'blob' });
var a = document.createElement('a');
a.href = window.URL.createObjectURL(response.data);
- a.download = isExportJson === 'true' ? `${projectName}.json` : `${projectName}.zip`;
+ a.download = isExportJson ? `${projectName}.json` : `${projectName}.zip`;
a.click();
} catch (error) {
} finally {
diff --git a/src/frontend/src/components/ProjectSubmissions/ProjectSubmissionsSkeletonLoader.tsx b/src/frontend/src/components/ProjectSubmissions/ProjectSubmissionsSkeletonLoader.tsx
index b143545f1b..9912f1e918 100644
--- a/src/frontend/src/components/ProjectSubmissions/ProjectSubmissionsSkeletonLoader.tsx
+++ b/src/frontend/src/components/ProjectSubmissions/ProjectSubmissionsSkeletonLoader.tsx
@@ -29,8 +29,8 @@ export const SubmissionsTableSkeletonLoader = () => {
{Array.from({ length: 10 }).map((_, i) => (
- {Array.from({ length: 15 }).map(() => (
-
+ {Array.from({ length: 15 }).map((_, ind) => (
+
))}
))}
diff --git a/src/frontend/src/components/ProjectSubmissions/SubmissionsTable.tsx b/src/frontend/src/components/ProjectSubmissions/SubmissionsTable.tsx
index 21b6d09675..77798be1ec 100644
--- a/src/frontend/src/components/ProjectSubmissions/SubmissionsTable.tsx
+++ b/src/frontend/src/components/ProjectSubmissions/SubmissionsTable.tsx
@@ -11,7 +11,7 @@ import windowDimention from '@/hooks/WindowDimension';
import Button from '@/components/common/Button';
import { Modal } from '@/components/common/Modal';
import { CustomSelect } from '@/components/common/Select.js';
-import CustomDatePicker from '@/components/common/CustomDatePicker';
+import DateRangePicker from '@/components/common/DateRangePicker';
import Table, { TableHeader } from '@/components/common/CustomTable';
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/common/Dropdown';
import { SubmissionsTableSkeletonLoader } from '@/components/ProjectSubmissions/ProjectSubmissionsSkeletonLoader.js';
@@ -39,7 +39,7 @@ const SubmissionsTable = ({ toggleView }) => {
task_id: searchParams.get('task_id') ? searchParams?.get('task_id') || null : null,
submitted_by: searchParams.get('submitted_by'),
review_state: searchParams.get('review_state'),
- submitted_date: searchParams.get('submitted_date'),
+ submitted_date_range: searchParams.get('submitted_date_range'),
};
const [filter, setFilter] = useState(initialFilterState);
@@ -70,20 +70,25 @@ const SubmissionsTable = ({ toggleView }) => {
};
const taskList = projectData[projectIndex]?.taskBoundries;
- const [numberOfFilters, setNumberOfFilters] = useState(0);
const [paginationPage, setPaginationPage] = useState(1);
const [submittedBy, setSubmittedBy] = useState(null);
+ const [dateRange, setDateRange] = useState<{ start: Date | null; end: Date | null }>({
+ start: initialFilterState?.submitted_date_range
+ ? new Date(initialFilterState.submitted_date_range.split(',')[0])
+ : null,
+ end: initialFilterState?.submitted_date_range
+ ? new Date(initialFilterState.submitted_date_range.split(',')[1])
+ : null,
+ });
useEffect(() => {
- let count = 0;
- const filters = Object.keys(filter);
- filters?.map((fltr) => {
- if (filter[fltr]) {
- count = count + 1;
- }
- });
- setNumberOfFilters(count);
- }, [filter]);
+ if (!dateRange.start || !dateRange.end) return;
+
+ setFilter((prev) => ({
+ ...prev,
+ submitted_date_range: `${format(new Date(dateRange.start as Date), 'yyyy-MM-dd')},${format(new Date(dateRange.end as Date), 'yyyy-MM-dd')}`,
+ }));
+ }, [dateRange]);
const updatedSubmissionFormFields = submissionFormFields
//filter necessary fields only
@@ -163,7 +168,8 @@ const SubmissionsTable = ({ toggleView }) => {
const clearFilters = () => {
setSearchParams({ tab: 'table' });
- setFilter({ task_id: null, submitted_by: null, review_state: null, submitted_date: null });
+ setFilter({ task_id: null, submitted_by: null, review_state: null, submitted_date_range: null });
+ setDateRange({ start: null, end: null });
};
function getValueByPath(obj: any, path: string) {
@@ -203,21 +209,13 @@ const SubmissionsTable = ({ toggleView }) => {
};
const handleDownload = (downloadType: 'csv' | 'json') => {
- if (downloadType === 'csv') {
- dispatch(
- getDownloadProjectSubmission(
- `${import.meta.env.VITE_API_URL}/submission/download?project_id=${projectId}&export_json=false`,
- projectInfo.name!,
- ),
- );
- } else if (downloadType === 'json') {
- dispatch(
- getDownloadProjectSubmission(
- `${import.meta.env.VITE_API_URL}/submission/download?project_id=${projectId}&export_json=true`,
- projectInfo.name!,
- ),
- );
- }
+ dispatch(
+ getDownloadProjectSubmission(`${import.meta.env.VITE_API_URL}/submission/download`, projectInfo.name!, {
+ project_id: projectId,
+ submitted_date_range: filter?.submitted_date_range,
+ export_json: downloadType === 'json',
+ }),
+ );
};
const handleTaskMap = async () => {
@@ -270,7 +268,7 @@ const SubmissionsTable = ({ toggleView }) => {
>
FILTER
{' '}
-
{numberOfFilters}
+
{Object.values(filter).filter((filterObjValue) => filterObjValue).length}
@@ -284,7 +282,7 @@ const SubmissionsTable = ({ toggleView }) => {
placeholder="Select"
data={taskInfo}
dataKey="value"
- value={filter?.task_id?.toString() || undefined}
+ value={filter?.task_id?.toString() || ''}
valueKey="task_id"
label="task_id"
onValueChange={(value) => value && setFilter((prev) => ({ ...prev, task_id: value.toString() }))}
@@ -297,7 +295,7 @@ const SubmissionsTable = ({ toggleView }) => {
placeholder="Select"
data={reviewStateData}
dataKey="value"
- value={filter?.review_state || undefined}
+ value={filter?.review_state || ''}
valueKey="value"
label="label"
onValueChange={(value) =>
@@ -307,13 +305,13 @@ const SubmissionsTable = ({ toggleView }) => {
className="fmtm-text-grey-700 fmtm-text-sm !fmtm-mb-0 fmtm-bg-white"
/>
-
-
+
- setFilter((prev) => ({ ...prev, submitted_date: format(new Date(date), 'yyyy-MM-dd') }))
- }
+ startDate={dateRange?.start}
+ endDate={dateRange?.end}
+ setStartDate={(date) => setDateRange((prev) => ({ ...prev, start: date }))}
+ setEndDate={(date) => setDateRange((prev) => ({ ...prev, end: date }))}
className="fmtm-text-grey-700 fmtm-text-sm !fmtm-mb-0 fmtm-w-full"
/>
diff --git a/src/frontend/src/components/common/CustomDatePicker.tsx b/src/frontend/src/components/common/DateRangePicker.tsx
similarity index 51%
rename from src/frontend/src/components/common/CustomDatePicker.tsx
rename to src/frontend/src/components/common/DateRangePicker.tsx
index a83e524b35..4320539eeb 100644
--- a/src/frontend/src/components/common/CustomDatePicker.tsx
+++ b/src/frontend/src/components/common/DateRangePicker.tsx
@@ -2,22 +2,32 @@ import React from 'react';
import DatePicker from 'react-datepicker';
import 'react-datepicker/dist/react-datepicker.css';
-type CustomDatePickerType = {
+type DateRangePickerType = {
title: string;
className: string;
- selectedDate: string | null;
- setSelectedDate: (date: Date) => void;
+ setStartDate: (date: Date | null) => void;
+ setEndDate: (date: Date | null) => void;
+ startDate: Date | null;
+ endDate: Date | null;
};
-const CustomDatePicker = ({ title, className, selectedDate, setSelectedDate }: CustomDatePickerType) => {
+const DateRangePicker = ({ title, className, setStartDate, setEndDate, startDate, endDate }: DateRangePickerType) => {
+ const onChange = (dates: [Date | null, Date | null]) => {
+ const [start, end] = dates;
+ setStartDate(start);
+ setEndDate(end);
+ };
return (
{title && (
{title}
)}
setSelectedDate(date)}
+ selected={startDate}
+ onChange={onChange}
+ startDate={startDate}
+ endDate={endDate}
+ selectsRange
className="fmtm-outline-none fmtm-border-[1px] fmtm-border-gray-300 fmtm-h-[2rem] fmtm-w-full fmtm-z-50 fmtm-px-2 fmtm-text-base fmtm-pt-1 hover"
placeholderText="YYYY/MM/DD"
dateFormat="yyyy/MM/dd"
@@ -26,4 +36,4 @@ const CustomDatePicker = ({ title, className, selectedDate, setSelectedDate }: C
);
};
-export default CustomDatePicker;
+export default DateRangePicker;
diff --git a/src/frontend/src/components/common/Select.tsx b/src/frontend/src/components/common/Select.tsx
index 7adbc14d42..074aad8070 100644
--- a/src/frontend/src/components/common/Select.tsx
+++ b/src/frontend/src/components/common/Select.tsx
@@ -97,7 +97,7 @@ interface ICustomSelect {
placeholder: string;
data: any;
dataKey: string;
- value?: string | undefined;
+ value?: string;
valueKey: string;
label: string;
onValueChange?: (value: string | null | number) => void;
diff --git a/src/frontend/src/index.css b/src/frontend/src/index.css
index 067a7d4d57..f97aa1a9a8 100755
--- a/src/frontend/src/index.css
+++ b/src/frontend/src/index.css
@@ -247,12 +247,16 @@ button {
.react-datepicker-popper {
z-index: 9000;
}
-.react-datepicker__day--selected {
- background-color: #d73f3e;
+.react-datepicker__day--selected,
+.react-datepicker__day--in-range {
+ background-color: #d73f3e !important;
+}
+.react-datepicker__day--in-selecting-range {
+ background-color: #df6565 !important;
}
.react-datepicker__day--selected:hover {
- background-color: #bc2c2c;
+ background-color: #bc2c2c !important;
}
.react-datepicker__day--keyboard-selected {
- background-color: #fcc2c2;
+ background-color: #fcc2c2 !important;
}
diff --git a/src/frontend/src/store/types/ISubmissions.ts b/src/frontend/src/store/types/ISubmissions.ts
index 0baecd7a12..ed0577e47e 100644
--- a/src/frontend/src/store/types/ISubmissions.ts
+++ b/src/frontend/src/store/types/ISubmissions.ts
@@ -35,7 +35,7 @@ export type filterType = {
task_id: string | null;
submitted_by: string | null;
review_state: string | null;
- submitted_date: string | null;
+ submitted_date_range: string | null;
};
type mappedVsValidatedTaskType = {