forked from github/plane
feat: start date filter added across the platform (#1955)
Co-authored-by: Aaryan Khandelwal <aaryan610@Aaryans-MacBook-Pro.local>
This commit is contained in:
parent
802e6b3e8e
commit
d18ac83909
@ -11,15 +11,18 @@ import { Dialog, Transition } from "@headlessui/react";
|
|||||||
// hooks
|
// hooks
|
||||||
import useIssuesView from "hooks/use-issues-view";
|
import useIssuesView from "hooks/use-issues-view";
|
||||||
// components
|
// components
|
||||||
import { DueDateFilterSelect } from "./due-date-filter-select";
|
import { DateFilterSelect } from "./date-filter-select";
|
||||||
// ui
|
// ui
|
||||||
import { PrimaryButton, SecondaryButton } from "components/ui";
|
import { PrimaryButton, SecondaryButton } from "components/ui";
|
||||||
// icons
|
// icons
|
||||||
import { XMarkIcon } from "@heroicons/react/20/solid";
|
import { XMarkIcon } from "@heroicons/react/20/solid";
|
||||||
// helpers
|
// helpers
|
||||||
import { renderDateFormat, renderShortDateWithYearFormat } from "helpers/date-time.helper";
|
import { renderDateFormat, renderShortDateWithYearFormat } from "helpers/date-time.helper";
|
||||||
|
import { IIssueFilterOptions } from "types";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
title: string;
|
||||||
|
field: keyof IIssueFilterOptions;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
handleClose: () => void;
|
handleClose: () => void;
|
||||||
};
|
};
|
||||||
@ -36,7 +39,7 @@ const defaultValues: TFormValues = {
|
|||||||
date2: new Date(new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()),
|
date2: new Date(new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DueDateFilterModal: React.FC<Props> = ({ isOpen, handleClose }) => {
|
export const DateFilterModal: React.FC<Props> = ({ title, field, isOpen, handleClose }) => {
|
||||||
const { filters, setFilters } = useIssuesView();
|
const { filters, setFilters } = useIssuesView();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -51,11 +54,11 @@ export const DueDateFilterModal: React.FC<Props> = ({ isOpen, handleClose }) =>
|
|||||||
|
|
||||||
if (filterType === "range") {
|
if (filterType === "range") {
|
||||||
setFilters(
|
setFilters(
|
||||||
{ target_date: [`${renderDateFormat(date1)};after`, `${renderDateFormat(date2)};before`] },
|
{ [field]: [`${renderDateFormat(date1)};after`, `${renderDateFormat(date2)};before`] },
|
||||||
!Boolean(viewId)
|
!Boolean(viewId)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const filteredArray = filters?.target_date?.filter((item) => {
|
const filteredArray = (filters?.[field] as string[])?.filter((item) => {
|
||||||
if (item?.includes(filterType)) return false;
|
if (item?.includes(filterType)) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -64,13 +67,13 @@ export const DueDateFilterModal: React.FC<Props> = ({ isOpen, handleClose }) =>
|
|||||||
const filterOne = filteredArray && filteredArray?.length > 0 ? filteredArray[0] : null;
|
const filterOne = filteredArray && filteredArray?.length > 0 ? filteredArray[0] : null;
|
||||||
if (filterOne)
|
if (filterOne)
|
||||||
setFilters(
|
setFilters(
|
||||||
{ target_date: [filterOne, `${renderDateFormat(date1)};${filterType}`] },
|
{ [field]: [filterOne, `${renderDateFormat(date1)};${filterType}`] },
|
||||||
!Boolean(viewId)
|
!Boolean(viewId)
|
||||||
);
|
);
|
||||||
else
|
else
|
||||||
setFilters(
|
setFilters(
|
||||||
{
|
{
|
||||||
target_date: [`${renderDateFormat(date1)};${filterType}`],
|
[field]: [`${renderDateFormat(date1)};${filterType}`],
|
||||||
},
|
},
|
||||||
!Boolean(viewId)
|
!Boolean(viewId)
|
||||||
);
|
);
|
||||||
@ -116,7 +119,7 @@ export const DueDateFilterModal: React.FC<Props> = ({ isOpen, handleClose }) =>
|
|||||||
control={control}
|
control={control}
|
||||||
name="filterType"
|
name="filterType"
|
||||||
render={({ field: { value, onChange } }) => (
|
render={({ field: { value, onChange } }) => (
|
||||||
<DueDateFilterSelect value={value} onChange={onChange} />
|
<DateFilterSelect title={title} value={value} onChange={onChange} />
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<XMarkIcon
|
<XMarkIcon
|
@ -7,6 +7,7 @@ import { CalendarBeforeIcon, CalendarAfterIcon, CalendarMonthIcon } from "compon
|
|||||||
// fetch-keys
|
// fetch-keys
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
title: string;
|
||||||
value: string;
|
value: string;
|
||||||
onChange: (value: string) => void;
|
onChange: (value: string) => void;
|
||||||
};
|
};
|
||||||
@ -19,29 +20,31 @@ type DueDate = {
|
|||||||
|
|
||||||
const dueDateRange: DueDate[] = [
|
const dueDateRange: DueDate[] = [
|
||||||
{
|
{
|
||||||
name: "Due date before",
|
name: "before",
|
||||||
value: "before",
|
value: "before",
|
||||||
icon: <CalendarBeforeIcon className="h-4 w-4 " />,
|
icon: <CalendarBeforeIcon className="h-4 w-4 " />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Due date after",
|
name: "after",
|
||||||
value: "after",
|
value: "after",
|
||||||
icon: <CalendarAfterIcon className="h-4 w-4 " />,
|
icon: <CalendarAfterIcon className="h-4 w-4 " />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Due date range",
|
name: "range",
|
||||||
value: "range",
|
value: "range",
|
||||||
icon: <CalendarMonthIcon className="h-4 w-4 " />,
|
icon: <CalendarMonthIcon className="h-4 w-4 " />,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const DueDateFilterSelect: React.FC<Props> = ({ value, onChange }) => (
|
export const DateFilterSelect: React.FC<Props> = ({ title, value, onChange }) => (
|
||||||
<CustomSelect
|
<CustomSelect
|
||||||
value={value}
|
value={value}
|
||||||
label={
|
label={
|
||||||
<div className="flex items-center gap-2 text-xs">
|
<div className="flex items-center gap-2 text-xs">
|
||||||
{dueDateRange.find((item) => item.value === value)?.icon}
|
{dueDateRange.find((item) => item.value === value)?.icon}
|
||||||
<span>{dueDateRange.find((item) => item.value === value)?.name}</span>
|
<span>
|
||||||
|
{title} {dueDateRange.find((item) => item.value === value)?.name}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
@ -50,7 +53,7 @@ export const DueDateFilterSelect: React.FC<Props> = ({ value, onChange }) => (
|
|||||||
<CustomSelect.Option key={index} value={option.value}>
|
<CustomSelect.Option key={index} value={option.value}>
|
||||||
<>
|
<>
|
||||||
<span>{option.icon}</span>
|
<span>{option.icon}</span>
|
||||||
{option.name}
|
{title} {option.name}
|
||||||
</>
|
</>
|
||||||
</CustomSelect.Option>
|
</CustomSelect.Option>
|
||||||
))}
|
))}
|
@ -240,6 +240,34 @@ export const FiltersList: React.FC<Props> = ({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
: key === "start_date"
|
||||||
|
? filters.start_date?.map((date: string) => {
|
||||||
|
if (filters.start_date && filters.start_date.length <= 0) return null;
|
||||||
|
|
||||||
|
const splitDate = date.split(";");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={date}
|
||||||
|
className="inline-flex items-center gap-x-1 rounded-full border border-custom-border-200 bg-custom-background-100 px-1 py-0.5"
|
||||||
|
>
|
||||||
|
<div className="h-1.5 w-1.5 rounded-full" />
|
||||||
|
<span className="capitalize">
|
||||||
|
{splitDate[1]} {renderShortDateWithYearFormat(splitDate[0])}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className="cursor-pointer"
|
||||||
|
onClick={() =>
|
||||||
|
setFilters({
|
||||||
|
start_date: filters.start_date?.filter((d: any) => d !== date),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<XMarkIcon className="h-3 w-3" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
: key === "target_date"
|
: key === "target_date"
|
||||||
? filters.target_date?.map((date: string) => {
|
? filters.target_date?.map((date: string) => {
|
||||||
if (filters.target_date && filters.target_date.length <= 0) return null;
|
if (filters.target_date && filters.target_date.length <= 0) return null;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
export * from "./due-date-filter-modal";
|
export * from "./date-filter-modal";
|
||||||
export * from "./due-date-filter-select";
|
export * from "./date-filter-select";
|
||||||
export * from "./filters-list";
|
export * from "./filters-list";
|
||||||
export * from "./issues-view-filter";
|
export * from "./issues-view-filter";
|
||||||
|
@ -119,14 +119,11 @@ export const IssuesFilterView: React.FC = () => {
|
|||||||
onSelect={(option) => {
|
onSelect={(option) => {
|
||||||
const key = option.key as keyof typeof filters;
|
const key = option.key as keyof typeof filters;
|
||||||
|
|
||||||
if (key === "target_date") {
|
if (key === "start_date" || key === "target_date") {
|
||||||
const valueExists = checkIfArraysHaveSameElements(
|
const valueExists = checkIfArraysHaveSameElements(filters[key] ?? [], option.value);
|
||||||
filters.target_date ?? [],
|
|
||||||
option.value
|
|
||||||
);
|
|
||||||
|
|
||||||
setFilters({
|
setFilters({
|
||||||
target_date: valueExists ? null : option.value,
|
[key]: valueExists ? null : option.value,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const valueExists = filters[key]?.includes(option.value);
|
const valueExists = filters[key]?.includes(option.value);
|
||||||
|
@ -478,6 +478,7 @@ export const IssuesView: React.FC<Props> = ({
|
|||||||
labels: null,
|
labels: null,
|
||||||
priority: null,
|
priority: null,
|
||||||
state: null,
|
state: null,
|
||||||
|
start_date: null,
|
||||||
target_date: null,
|
target_date: null,
|
||||||
type: null,
|
type: null,
|
||||||
})
|
})
|
||||||
|
@ -7,7 +7,7 @@ import useSWR from "swr";
|
|||||||
// services
|
// services
|
||||||
import issuesService from "services/issues.service";
|
import issuesService from "services/issues.service";
|
||||||
// components
|
// components
|
||||||
import { DueDateFilterModal } from "components/core";
|
import { DateFilterModal } from "components/core";
|
||||||
// ui
|
// ui
|
||||||
import { MultiLevelDropdown } from "components/ui";
|
import { MultiLevelDropdown } from "components/ui";
|
||||||
// icons
|
// icons
|
||||||
@ -20,7 +20,7 @@ import { IIssueFilterOptions, IQuery } from "types";
|
|||||||
import { WORKSPACE_LABELS } from "constants/fetch-keys";
|
import { WORKSPACE_LABELS } from "constants/fetch-keys";
|
||||||
// constants
|
// constants
|
||||||
import { GROUP_CHOICES, PRIORITIES } from "constants/project";
|
import { GROUP_CHOICES, PRIORITIES } from "constants/project";
|
||||||
import { DUE_DATES } from "constants/due-dates";
|
import { DATE_FILTER_OPTIONS } from "constants/filters";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
filters: Partial<IIssueFilterOptions> | IQuery;
|
filters: Partial<IIssueFilterOptions> | IQuery;
|
||||||
@ -35,7 +35,14 @@ export const MyIssuesSelectFilters: React.FC<Props> = ({
|
|||||||
direction = "right",
|
direction = "right",
|
||||||
height = "md",
|
height = "md",
|
||||||
}) => {
|
}) => {
|
||||||
const [isDueDateFilterModalOpen, setIsDueDateFilterModalOpen] = useState(false);
|
const [isDateFilterModalOpen, setIsDateFilterModalOpen] = useState(false);
|
||||||
|
const [dateFilterType, setDateFilterType] = useState<{
|
||||||
|
title: string;
|
||||||
|
type: "start_date" | "target_date";
|
||||||
|
}>({
|
||||||
|
title: "",
|
||||||
|
type: "start_date",
|
||||||
|
});
|
||||||
const [fetchLabels, setFetchLabels] = useState(false);
|
const [fetchLabels, setFetchLabels] = useState(false);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -50,10 +57,12 @@ export const MyIssuesSelectFilters: React.FC<Props> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isDueDateFilterModalOpen && (
|
{isDateFilterModalOpen && (
|
||||||
<DueDateFilterModal
|
<DateFilterModal
|
||||||
isOpen={isDueDateFilterModalOpen}
|
title={dateFilterType.title}
|
||||||
handleClose={() => setIsDueDateFilterModalOpen(false)}
|
field={dateFilterType.type}
|
||||||
|
isOpen={isDateFilterModalOpen}
|
||||||
|
handleClose={() => setIsDateFilterModalOpen(false)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<MultiLevelDropdown
|
<MultiLevelDropdown
|
||||||
@ -132,12 +141,48 @@ export const MyIssuesSelectFilters: React.FC<Props> = ({
|
|||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "target_date",
|
id: "start_date",
|
||||||
label: "Due date",
|
label: "Start date",
|
||||||
value: DUE_DATES,
|
value: DATE_FILTER_OPTIONS,
|
||||||
hasChildren: true,
|
hasChildren: true,
|
||||||
children: [
|
children: [
|
||||||
...(DUE_DATES?.map((option) => ({
|
...(DATE_FILTER_OPTIONS?.map((option) => ({
|
||||||
|
id: option.name,
|
||||||
|
label: option.name,
|
||||||
|
value: {
|
||||||
|
key: "start_date",
|
||||||
|
value: option.value,
|
||||||
|
},
|
||||||
|
selected: checkIfArraysHaveSameElements(filters?.start_date ?? [], option.value),
|
||||||
|
})) ?? []),
|
||||||
|
{
|
||||||
|
id: "custom",
|
||||||
|
label: "Custom",
|
||||||
|
value: "custom",
|
||||||
|
element: (
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setIsDateFilterModalOpen(true);
|
||||||
|
setDateFilterType({
|
||||||
|
title: "Start date",
|
||||||
|
type: "start_date",
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
className="w-full rounded px-1 py-1.5 text-left text-custom-text-200 hover:bg-custom-background-80"
|
||||||
|
>
|
||||||
|
Custom
|
||||||
|
</button>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "target_date",
|
||||||
|
label: "Due date",
|
||||||
|
value: DATE_FILTER_OPTIONS,
|
||||||
|
hasChildren: true,
|
||||||
|
children: [
|
||||||
|
...(DATE_FILTER_OPTIONS?.map((option) => ({
|
||||||
id: option.name,
|
id: option.name,
|
||||||
label: option.name,
|
label: option.name,
|
||||||
value: {
|
value: {
|
||||||
@ -152,7 +197,13 @@ export const MyIssuesSelectFilters: React.FC<Props> = ({
|
|||||||
value: "custom",
|
value: "custom",
|
||||||
element: (
|
element: (
|
||||||
<button
|
<button
|
||||||
onClick={() => setIsDueDateFilterModalOpen(true)}
|
onClick={() => {
|
||||||
|
setIsDateFilterModalOpen(true);
|
||||||
|
setDateFilterType({
|
||||||
|
title: "Due date",
|
||||||
|
type: "target_date",
|
||||||
|
});
|
||||||
|
}}
|
||||||
className="w-full rounded px-1 py-1.5 text-left text-custom-text-200 hover:bg-custom-background-80"
|
className="w-full rounded px-1 py-1.5 text-left text-custom-text-200 hover:bg-custom-background-80"
|
||||||
>
|
>
|
||||||
Custom
|
Custom
|
||||||
|
@ -89,14 +89,11 @@ export const MyIssuesViewOptions: React.FC = () => {
|
|||||||
onSelect={(option) => {
|
onSelect={(option) => {
|
||||||
const key = option.key as keyof typeof filters;
|
const key = option.key as keyof typeof filters;
|
||||||
|
|
||||||
if (key === "target_date") {
|
if (key === "start_date" || key === "target_date") {
|
||||||
const valueExists = checkIfArraysHaveSameElements(
|
const valueExists = checkIfArraysHaveSameElements(filters?.[key] ?? [], option.value);
|
||||||
filters?.target_date ?? [],
|
|
||||||
option.value
|
|
||||||
);
|
|
||||||
|
|
||||||
setFilters({
|
setFilters({
|
||||||
target_date: valueExists ? null : option.value,
|
[key]: valueExists ? null : option.value,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const valueExists = filters[key]?.includes(option.value);
|
const valueExists = filters[key]?.includes(option.value);
|
||||||
|
@ -249,6 +249,7 @@ export const MyIssuesView: React.FC<Props> = ({
|
|||||||
labels: null,
|
labels: null,
|
||||||
priority: null,
|
priority: null,
|
||||||
state_group: null,
|
state_group: null,
|
||||||
|
start_date: null,
|
||||||
target_date: null,
|
target_date: null,
|
||||||
type: null,
|
type: null,
|
||||||
})
|
})
|
||||||
|
@ -115,14 +115,11 @@ export const ProfileIssuesViewOptions: React.FC = () => {
|
|||||||
onSelect={(option) => {
|
onSelect={(option) => {
|
||||||
const key = option.key as keyof typeof filters;
|
const key = option.key as keyof typeof filters;
|
||||||
|
|
||||||
if (key === "target_date") {
|
if (key === "start_date" || key === "target_date") {
|
||||||
const valueExists = checkIfArraysHaveSameElements(
|
const valueExists = checkIfArraysHaveSameElements(filters?.[key] ?? [], option.value);
|
||||||
filters?.target_date ?? [],
|
|
||||||
option.value
|
|
||||||
);
|
|
||||||
|
|
||||||
setFilters({
|
setFilters({
|
||||||
target_date: valueExists ? null : option.value,
|
[key]: valueExists ? null : option.value,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const valueExists = filters[key]?.includes(option.value);
|
const valueExists = filters[key]?.includes(option.value);
|
||||||
|
@ -263,6 +263,7 @@ export const ProfileIssuesView = () => {
|
|||||||
labels: null,
|
labels: null,
|
||||||
priority: null,
|
priority: null,
|
||||||
state_group: null,
|
state_group: null,
|
||||||
|
start_date: null,
|
||||||
target_date: null,
|
target_date: null,
|
||||||
type: null,
|
type: null,
|
||||||
})
|
})
|
||||||
|
@ -94,6 +94,7 @@ export const ViewForm: React.FC<Props> = ({
|
|||||||
labels: null,
|
labels: null,
|
||||||
priority: null,
|
priority: null,
|
||||||
state: null,
|
state: null,
|
||||||
|
start_date: null,
|
||||||
target_date: null,
|
target_date: null,
|
||||||
type: null,
|
type: null,
|
||||||
});
|
});
|
||||||
@ -155,14 +156,15 @@ export const ViewForm: React.FC<Props> = ({
|
|||||||
onSelect={(option) => {
|
onSelect={(option) => {
|
||||||
const key = option.key as keyof typeof filters;
|
const key = option.key as keyof typeof filters;
|
||||||
|
|
||||||
if (key === "target_date") {
|
if (key === "start_date" || key === "target_date") {
|
||||||
const valueExists = checkIfArraysHaveSameElements(
|
const valueExists = checkIfArraysHaveSameElements(
|
||||||
filters?.target_date ?? [],
|
filters?.[key] ?? [],
|
||||||
option.value
|
option.value
|
||||||
);
|
);
|
||||||
|
|
||||||
setValue("query", {
|
setValue("query", {
|
||||||
target_date: valueExists ? null : option.value,
|
...filters,
|
||||||
|
[key]: valueExists ? null : option.value,
|
||||||
} as IQuery);
|
} as IQuery);
|
||||||
} else {
|
} else {
|
||||||
if (!filters?.[key]?.includes(option.value))
|
if (!filters?.[key]?.includes(option.value))
|
||||||
|
@ -9,7 +9,7 @@ import stateService from "services/state.service";
|
|||||||
import projectService from "services/project.service";
|
import projectService from "services/project.service";
|
||||||
import issuesService from "services/issues.service";
|
import issuesService from "services/issues.service";
|
||||||
// components
|
// components
|
||||||
import { DueDateFilterModal } from "components/core";
|
import { DateFilterModal } from "components/core";
|
||||||
// ui
|
// ui
|
||||||
import { Avatar, MultiLevelDropdown } from "components/ui";
|
import { Avatar, MultiLevelDropdown } from "components/ui";
|
||||||
// icons
|
// icons
|
||||||
@ -23,7 +23,7 @@ import { IIssueFilterOptions, IQuery } from "types";
|
|||||||
import { PROJECT_ISSUE_LABELS, PROJECT_MEMBERS, STATES_LIST } from "constants/fetch-keys";
|
import { PROJECT_ISSUE_LABELS, PROJECT_MEMBERS, STATES_LIST } from "constants/fetch-keys";
|
||||||
// constants
|
// constants
|
||||||
import { PRIORITIES } from "constants/project";
|
import { PRIORITIES } from "constants/project";
|
||||||
import { DUE_DATES } from "constants/due-dates";
|
import { DATE_FILTER_OPTIONS } from "constants/filters";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
filters: Partial<IIssueFilterOptions> | IQuery;
|
filters: Partial<IIssueFilterOptions> | IQuery;
|
||||||
@ -38,7 +38,14 @@ export const SelectFilters: React.FC<Props> = ({
|
|||||||
direction = "right",
|
direction = "right",
|
||||||
height = "md",
|
height = "md",
|
||||||
}) => {
|
}) => {
|
||||||
const [isDueDateFilterModalOpen, setIsDueDateFilterModalOpen] = useState(false);
|
const [isDateFilterModalOpen, setIsDateFilterModalOpen] = useState(false);
|
||||||
|
const [dateFilterType, setDateFilterType] = useState<{
|
||||||
|
title: string;
|
||||||
|
type: "start_date" | "target_date";
|
||||||
|
}>({
|
||||||
|
title: "",
|
||||||
|
type: "start_date",
|
||||||
|
});
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
@ -67,10 +74,12 @@ export const SelectFilters: React.FC<Props> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isDueDateFilterModalOpen && (
|
{isDateFilterModalOpen && (
|
||||||
<DueDateFilterModal
|
<DateFilterModal
|
||||||
isOpen={isDueDateFilterModalOpen}
|
title={dateFilterType.title}
|
||||||
handleClose={() => setIsDueDateFilterModalOpen(false)}
|
field={dateFilterType.type}
|
||||||
|
isOpen={isDateFilterModalOpen}
|
||||||
|
handleClose={() => setIsDateFilterModalOpen(false)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<MultiLevelDropdown
|
<MultiLevelDropdown
|
||||||
@ -183,12 +192,48 @@ export const SelectFilters: React.FC<Props> = ({
|
|||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "target_date",
|
id: "start_date",
|
||||||
label: "Due date",
|
label: "Start date",
|
||||||
value: DUE_DATES,
|
value: DATE_FILTER_OPTIONS,
|
||||||
hasChildren: true,
|
hasChildren: true,
|
||||||
children: [
|
children: [
|
||||||
...DUE_DATES.map((option) => ({
|
...DATE_FILTER_OPTIONS.map((option) => ({
|
||||||
|
id: option.name,
|
||||||
|
label: option.name,
|
||||||
|
value: {
|
||||||
|
key: "start_date",
|
||||||
|
value: option.value,
|
||||||
|
},
|
||||||
|
selected: checkIfArraysHaveSameElements(filters?.start_date ?? [], option.value),
|
||||||
|
})),
|
||||||
|
{
|
||||||
|
id: "custom",
|
||||||
|
label: "Custom",
|
||||||
|
value: "custom",
|
||||||
|
element: (
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setIsDateFilterModalOpen(true);
|
||||||
|
setDateFilterType({
|
||||||
|
title: "Start date",
|
||||||
|
type: "start_date",
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
className="w-full rounded px-1 py-1.5 text-left text-custom-text-200 hover:bg-custom-background-80"
|
||||||
|
>
|
||||||
|
Custom
|
||||||
|
</button>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "target_date",
|
||||||
|
label: "Due date",
|
||||||
|
value: DATE_FILTER_OPTIONS,
|
||||||
|
hasChildren: true,
|
||||||
|
children: [
|
||||||
|
...DATE_FILTER_OPTIONS.map((option) => ({
|
||||||
id: option.name,
|
id: option.name,
|
||||||
label: option.name,
|
label: option.name,
|
||||||
value: {
|
value: {
|
||||||
@ -203,7 +248,13 @@ export const SelectFilters: React.FC<Props> = ({
|
|||||||
value: "custom",
|
value: "custom",
|
||||||
element: (
|
element: (
|
||||||
<button
|
<button
|
||||||
onClick={() => setIsDueDateFilterModalOpen(true)}
|
onClick={() => {
|
||||||
|
setIsDateFilterModalOpen(true);
|
||||||
|
setDateFilterType({
|
||||||
|
title: "Due date",
|
||||||
|
type: "target_date",
|
||||||
|
});
|
||||||
|
}}
|
||||||
className="w-full rounded px-1 py-1.5 text-left text-custom-text-200 hover:bg-custom-background-80"
|
className="w-full rounded px-1 py-1.5 text-left text-custom-text-200 hover:bg-custom-background-80"
|
||||||
>
|
>
|
||||||
Custom
|
Custom
|
||||||
|
@ -8,6 +8,7 @@ const paramsToKey = (params: any) => {
|
|||||||
assignees,
|
assignees,
|
||||||
created_by,
|
created_by,
|
||||||
labels,
|
labels,
|
||||||
|
start_date,
|
||||||
target_date,
|
target_date,
|
||||||
sub_issue,
|
sub_issue,
|
||||||
start_target_date,
|
start_target_date,
|
||||||
@ -19,6 +20,7 @@ const paramsToKey = (params: any) => {
|
|||||||
let createdByKey = created_by ? created_by.split(",") : [];
|
let createdByKey = created_by ? created_by.split(",") : [];
|
||||||
let labelsKey = labels ? labels.split(",") : [];
|
let labelsKey = labels ? labels.split(",") : [];
|
||||||
const startTargetDate = start_target_date ? `${start_target_date}`.toUpperCase() : "FALSE";
|
const startTargetDate = start_target_date ? `${start_target_date}`.toUpperCase() : "FALSE";
|
||||||
|
const startDateKey = start_date ?? "";
|
||||||
const targetDateKey = target_date ?? "";
|
const targetDateKey = target_date ?? "";
|
||||||
const type = params.type ? params.type.toUpperCase() : "NULL";
|
const type = params.type ? params.type.toUpperCase() : "NULL";
|
||||||
const groupBy = params.group_by ? params.group_by.toUpperCase() : "NULL";
|
const groupBy = params.group_by ? params.group_by.toUpperCase() : "NULL";
|
||||||
@ -31,7 +33,7 @@ const paramsToKey = (params: any) => {
|
|||||||
createdByKey = createdByKey.sort().join("_");
|
createdByKey = createdByKey.sort().join("_");
|
||||||
labelsKey = labelsKey.sort().join("_");
|
labelsKey = labelsKey.sort().join("_");
|
||||||
|
|
||||||
return `${stateKey}_${priorityKey}_${assigneesKey}_${createdByKey}_${type}_${groupBy}_${orderBy}_${labelsKey}_${targetDateKey}_${sub_issue}_${startTargetDate}`;
|
return `${stateKey}_${priorityKey}_${assigneesKey}_${createdByKey}_${type}_${groupBy}_${orderBy}_${labelsKey}_${startDateKey}_${targetDateKey}_${sub_issue}_${startTargetDate}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const inboxParamsToKey = (params: any) => {
|
const inboxParamsToKey = (params: any) => {
|
||||||
@ -48,7 +50,16 @@ const inboxParamsToKey = (params: any) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const myIssuesParamsToKey = (params: any) => {
|
const myIssuesParamsToKey = (params: any) => {
|
||||||
const { assignees, created_by, labels, priority, state_group, subscriber, target_date } = params;
|
const {
|
||||||
|
assignees,
|
||||||
|
created_by,
|
||||||
|
labels,
|
||||||
|
priority,
|
||||||
|
state_group,
|
||||||
|
subscriber,
|
||||||
|
start_date,
|
||||||
|
target_date,
|
||||||
|
} = params;
|
||||||
|
|
||||||
let assigneesKey = assignees ? assignees.split(",") : [];
|
let assigneesKey = assignees ? assignees.split(",") : [];
|
||||||
let createdByKey = created_by ? created_by.split(",") : [];
|
let createdByKey = created_by ? created_by.split(",") : [];
|
||||||
@ -56,6 +67,7 @@ const myIssuesParamsToKey = (params: any) => {
|
|||||||
let subscriberKey = subscriber ? subscriber.split(",") : [];
|
let subscriberKey = subscriber ? subscriber.split(",") : [];
|
||||||
let priorityKey = priority ? priority.split(",") : [];
|
let priorityKey = priority ? priority.split(",") : [];
|
||||||
let labelsKey = labels ? labels.split(",") : [];
|
let labelsKey = labels ? labels.split(",") : [];
|
||||||
|
const startDateKey = start_date ?? "";
|
||||||
const targetDateKey = target_date ?? "";
|
const targetDateKey = target_date ?? "";
|
||||||
const type = params.type ? params.type.toUpperCase() : "NULL";
|
const type = params.type ? params.type.toUpperCase() : "NULL";
|
||||||
const groupBy = params.group_by ? params.group_by.toUpperCase() : "NULL";
|
const groupBy = params.group_by ? params.group_by.toUpperCase() : "NULL";
|
||||||
@ -69,7 +81,7 @@ const myIssuesParamsToKey = (params: any) => {
|
|||||||
priorityKey = priorityKey.sort().join("_");
|
priorityKey = priorityKey.sort().join("_");
|
||||||
labelsKey = labelsKey.sort().join("_");
|
labelsKey = labelsKey.sort().join("_");
|
||||||
|
|
||||||
return `${assigneesKey}_${createdByKey}_${stateGroupKey}_${subscriberKey}_${priorityKey}_${type}_${groupBy}_${orderBy}_${labelsKey}_${targetDateKey}`;
|
return `${assigneesKey}_${createdByKey}_${stateGroupKey}_${subscriberKey}_${priorityKey}_${type}_${groupBy}_${orderBy}_${labelsKey}_${startDateKey}_${targetDateKey}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CURRENT_USER = "CURRENT_USER";
|
export const CURRENT_USER = "CURRENT_USER";
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// helper
|
// helper
|
||||||
import { renderDateFormat } from "helpers/date-time.helper";
|
import { renderDateFormat } from "helpers/date-time.helper";
|
||||||
|
|
||||||
export const DUE_DATES = [
|
export const DATE_FILTER_OPTIONS = [
|
||||||
{
|
{
|
||||||
name: "Last week",
|
name: "Last week",
|
||||||
value: [
|
value: [
|
@ -94,6 +94,7 @@ export const initialState: StateType = {
|
|||||||
state_group: null,
|
state_group: null,
|
||||||
subscriber: null,
|
subscriber: null,
|
||||||
created_by: null,
|
created_by: null,
|
||||||
|
start_date: null,
|
||||||
target_date: null,
|
target_date: null,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -72,6 +72,7 @@ export const initialState: StateType = {
|
|||||||
state_group: null,
|
state_group: null,
|
||||||
subscriber: null,
|
subscriber: null,
|
||||||
created_by: null,
|
created_by: null,
|
||||||
|
start_date: null,
|
||||||
target_date: null,
|
target_date: null,
|
||||||
},
|
},
|
||||||
properties: {
|
properties: {
|
||||||
|
@ -26,6 +26,7 @@ const initialValues: IWorkspaceViewProps = {
|
|||||||
priority: null,
|
priority: null,
|
||||||
state_group: null,
|
state_group: null,
|
||||||
subscriber: null,
|
subscriber: null,
|
||||||
|
start_date: null,
|
||||||
target_date: null,
|
target_date: null,
|
||||||
type: null,
|
type: null,
|
||||||
},
|
},
|
||||||
|
@ -27,6 +27,7 @@ const useMyIssues = (workspaceSlug: string | undefined) => {
|
|||||||
priority: filters?.priority ? filters?.priority.join(",") : undefined,
|
priority: filters?.priority ? filters?.priority.join(",") : undefined,
|
||||||
state_group: filters?.state_group ? filters?.state_group.join(",") : undefined,
|
state_group: filters?.state_group ? filters?.state_group.join(",") : undefined,
|
||||||
subscriber: filters?.subscriber ? filters?.subscriber.join(",") : undefined,
|
subscriber: filters?.subscriber ? filters?.subscriber.join(",") : undefined,
|
||||||
|
start_date: filters?.start_date ? filters?.start_date.join(",") : undefined,
|
||||||
target_date: filters?.target_date ? filters?.target_date.join(",") : undefined,
|
target_date: filters?.target_date ? filters?.target_date.join(",") : undefined,
|
||||||
type: filters?.type ? filters?.type : undefined,
|
type: filters?.type ? filters?.type : undefined,
|
||||||
};
|
};
|
||||||
|
@ -58,6 +58,7 @@ const useIssuesView = () => {
|
|||||||
type: filters?.type ? filters?.type : undefined,
|
type: filters?.type ? filters?.type : undefined,
|
||||||
labels: filters?.labels ? filters?.labels.join(",") : undefined,
|
labels: filters?.labels ? filters?.labels.join(",") : undefined,
|
||||||
created_by: filters?.created_by ? filters?.created_by.join(",") : undefined,
|
created_by: filters?.created_by ? filters?.created_by.join(",") : undefined,
|
||||||
|
start_date: filters?.start_date ? filters?.start_date.join(",") : undefined,
|
||||||
target_date: filters?.target_date ? filters?.target_date.join(",") : undefined,
|
target_date: filters?.target_date ? filters?.target_date.join(",") : undefined,
|
||||||
sub_issue: showSubIssues,
|
sub_issue: showSubIssues,
|
||||||
};
|
};
|
||||||
|
@ -44,6 +44,7 @@ const useProfileIssues = (workspaceSlug: string | undefined, userId: string | un
|
|||||||
order_by: orderBy,
|
order_by: orderBy,
|
||||||
priority: filters?.priority ? filters?.priority.join(",") : undefined,
|
priority: filters?.priority ? filters?.priority.join(",") : undefined,
|
||||||
state_group: filters?.state_group ? filters?.state_group.join(",") : undefined,
|
state_group: filters?.state_group ? filters?.state_group.join(",") : undefined,
|
||||||
|
start_date: filters?.start_date ? filters?.start_date.join(",") : undefined,
|
||||||
target_date: filters?.target_date ? filters?.target_date.join(",") : undefined,
|
target_date: filters?.target_date ? filters?.target_date.join(",") : undefined,
|
||||||
type: filters?.type ? filters?.type : undefined,
|
type: filters?.type ? filters?.type : undefined,
|
||||||
subscriber: filters?.subscriber ? filters?.subscriber.join(",") : undefined,
|
subscriber: filters?.subscriber ? filters?.subscriber.join(",") : undefined,
|
||||||
|
1
apps/app/types/issues.d.ts
vendored
1
apps/app/types/issues.d.ts
vendored
@ -215,6 +215,7 @@ export interface IIssueLite {
|
|||||||
export interface IIssueFilterOptions {
|
export interface IIssueFilterOptions {
|
||||||
type: "active" | "backlog" | null;
|
type: "active" | "backlog" | null;
|
||||||
assignees: string[] | null;
|
assignees: string[] | null;
|
||||||
|
start_date: string[] | null;
|
||||||
target_date: string[] | null;
|
target_date: string[] | null;
|
||||||
state: string[] | null;
|
state: string[] | null;
|
||||||
state_group: TStateGroups[] | null;
|
state_group: TStateGroups[] | null;
|
||||||
|
1
apps/app/types/views.d.ts
vendored
1
apps/app/types/views.d.ts
vendored
@ -20,6 +20,7 @@ export interface IQuery {
|
|||||||
labels: string[] | null;
|
labels: string[] | null;
|
||||||
priority: string[] | null;
|
priority: string[] | null;
|
||||||
state: string[] | null;
|
state: string[] | null;
|
||||||
|
start_date: string[] | null;
|
||||||
target_date: string[] | null;
|
target_date: string[] | null;
|
||||||
type: "active" | "backlog" | null;
|
type: "active" | "backlog" | null;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user