diff --git a/apps/app/components/core/calendar-view/calendar.tsx b/apps/app/components/core/calendar-view/calendar.tsx index fa29eb9f7..9da6e5873 100644 --- a/apps/app/components/core/calendar-view/calendar.tsx +++ b/apps/app/components/core/calendar-view/calendar.tsx @@ -170,7 +170,7 @@ export const CalendarView: React.FC = ({ const isNotAllowed = userAuth.isGuest || userAuth.isViewer || isCompleted; return calendarIssues ? ( -
+
void; +}; + +type TFormValues = { + filterType: "before" | "after" | "range"; + date1: Date; + date2: Date; +}; + +const defaultValues: TFormValues = { + filterType: "range", + date1: new Date(), + date2: new Date(new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()), +}; + +export const DueDateFilterModal: React.FC = ({ isOpen, handleClose }) => { + const { filters, setFilters } = useIssuesView(); + + const router = useRouter(); + const { viewId } = router.query; + + const { handleSubmit, watch, control } = useForm({ + defaultValues, + }); + + const handleFormSubmit = (formData: TFormValues) => { + const { filterType, date1, date2 } = formData; + + if (filterType === "range") { + setFilters( + { target_date: [`${renderDateFormat(date1)};after`, `${renderDateFormat(date2)};before`] }, + !Boolean(viewId) + ); + } else { + const filteredArray = filters?.target_date?.filter((item) => { + if (item?.includes(filterType)) return false; + + return true; + }); + + const filterOne = filteredArray && filteredArray?.length > 0 ? filteredArray[0] : null; + if (filterOne) + setFilters( + { target_date: [filterOne, `${renderDateFormat(date1)};${filterType}`] }, + !Boolean(viewId) + ); + else + setFilters( + { + target_date: [`${renderDateFormat(date1)};${filterType}`], + }, + !Boolean(viewId) + ); + } + handleClose(); + }; + + const isInvalid = + watch("filterType") === "range" ? new Date(watch("date1")) > new Date(watch("date2")) : false; + + const nextDay = new Date(watch("date1")); + nextDay.setDate(nextDay.getDate() + 1); + + return ( + + + +
+ +
+
+ + +
+
+ ( + + )} + /> + +
+
+ ( + onChange(val)} + dateFormat="dd-MM-yyyy" + calendarClassName="h-full" + inline + /> + )} + /> + {watch("filterType") === "range" && ( + ( + + )} + /> + )} +
+ {watch("filterType") === "range" && ( +
+ After: + {renderShortDateWithYearFormat(watch("date1"))} + Before: + {!isInvalid && {renderShortDateWithYearFormat(watch("date2"))}} +
+ )} +
+ + Cancel + + + Apply + +
+
+
+
+
+
+
+
+ ); +}; diff --git a/apps/app/components/core/filters/due-date-filter-select.tsx b/apps/app/components/core/filters/due-date-filter-select.tsx new file mode 100644 index 000000000..480a77619 --- /dev/null +++ b/apps/app/components/core/filters/due-date-filter-select.tsx @@ -0,0 +1,58 @@ +import React from "react"; + +// ui +import { CustomSelect } from "components/ui"; +// icons +import { CalendarBeforeIcon, CalendarAfterIcon, CalendarMonthIcon } from "components/icons"; +// fetch-keys + +type Props = { + value: string; + onChange: (value: string) => void; +}; + +type DueDate = { + name: string; + value: string; + icon: any; +}; + +const dueDateRange: DueDate[] = [ + { + name: "Due date before", + value: "before", + icon: , + }, + { + name: "Due date after", + value: "after", + icon: , + }, + { + name: "Due date range", + value: "range", + icon: , + }, +]; + +export const DueDateFilterSelect: React.FC = ({ value, onChange }) => ( + + {dueDateRange.find((item) => item.value === value)?.icon} + {dueDateRange.find((item) => item.value === value)?.name} +
+ } + onChange={onChange} + > + {dueDateRange.map((option, index) => ( + + <> + {option.icon} + {option.name} + + + ))} + +); diff --git a/apps/app/components/core/filters-list.tsx b/apps/app/components/core/filters/filters-list.tsx similarity index 86% rename from apps/app/components/core/filters-list.tsx rename to apps/app/components/core/filters/filters-list.tsx index e080c8e9a..7254f8072 100644 --- a/apps/app/components/core/filters-list.tsx +++ b/apps/app/components/core/filters/filters-list.tsx @@ -17,6 +17,7 @@ import stateService from "services/state.service"; // types import { PROJECT_ISSUE_LABELS, PROJECT_MEMBERS, STATES_LIST } from "constants/fetch-keys"; import { IIssueFilterOptions } from "types"; +import { renderShortDateWithYearFormat } from "helpers/date-time.helper"; export const FilterList: React.FC = ({ filters, setFilters }) => { const router = useRouter(); @@ -60,7 +61,7 @@ export const FilterList: React.FC = ({ filters, setFilters }) => { className="flex items-center gap-x-2 rounded-full border border-brand-base bg-brand-surface-2 px-2 py-1" > - {replaceUnderscoreIfSnakeCase(key)}: + {key === "target_date" ? "Due Date" : replaceUnderscoreIfSnakeCase(key)}: {filters[key as keyof IIssueFilterOptions] === null || (filters[key as keyof IIssueFilterOptions]?.length ?? 0) <= 0 ? ( @@ -299,6 +300,51 @@ export const FilterList: React.FC = ({ filters, setFilters }) => {
+ ) : key === "target_date" ? ( +
+ {filters.target_date?.map((date: string) => { + if (filters.target_date.length <= 0) return null; + + const splitDate = date.split(";"); + + return ( +
+
+ + {splitDate[1]} {renderShortDateWithYearFormat(splitDate[0])} + + + setFilters( + { + target_date: filters.target_date?.filter( + (d: any) => d !== date + ), + }, + !Boolean(viewId) + ) + } + > + + +
+ ); + })} + +
) : ( (filters[key as keyof IIssueFilterOptions] as any)?.join(", ") )} @@ -332,6 +378,7 @@ export const FilterList: React.FC = ({ filters, setFilters }) => { assignees: null, labels: null, created_by: null, + target_date: null, }) } className="flex items-center gap-x-1 rounded-full border border-brand-base bg-brand-surface-2 px-3 py-1.5 text-xs" diff --git a/apps/app/components/core/filters/index.ts b/apps/app/components/core/filters/index.ts new file mode 100644 index 000000000..01c371911 --- /dev/null +++ b/apps/app/components/core/filters/index.ts @@ -0,0 +1,4 @@ +export * from "./due-date-filter-modal"; +export * from "./due-date-filter-select"; +export * from "./filters-list"; +export * from "./issues-view-filter"; diff --git a/apps/app/components/core/issues-view-filter.tsx b/apps/app/components/core/filters/issues-view-filter.tsx similarity index 92% rename from apps/app/components/core/issues-view-filter.tsx rename to apps/app/components/core/filters/issues-view-filter.tsx index a6996793c..4452bfb61 100644 --- a/apps/app/components/core/issues-view-filter.tsx +++ b/apps/app/components/core/filters/issues-view-filter.tsx @@ -2,11 +2,12 @@ import React from "react"; import { useRouter } from "next/router"; +// headless ui +import { Popover, Transition } from "@headlessui/react"; // hooks import useIssuesProperties from "hooks/use-issue-properties"; import useIssuesView from "hooks/use-issues-view"; -// headless ui -import { Popover, Transition } from "@headlessui/react"; +import useEstimateOption from "hooks/use-estimate-option"; // components import { SelectFilters } from "components/views"; // ui @@ -17,15 +18,14 @@ import { ListBulletIcon, Squares2X2Icon, CalendarDaysIcon, - ChartBarIcon, } from "@heroicons/react/24/outline"; // helpers import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper"; +import { checkIfArraysHaveSameElements } from "helpers/array.helper"; // types import { Properties } from "types"; // constants import { GROUP_BY_OPTIONS, ORDER_BY_OPTIONS, FILTER_ISSUE_OPTIONS } from "constants/issue"; -import useEstimateOption from "hooks/use-estimate-option"; export const IssuesFilterView: React.FC = () => { const router = useRouter(); @@ -109,26 +109,34 @@ export const IssuesFilterView: React.FC = () => { onSelect={(option) => { const key = option.key as keyof typeof filters; - const valueExists = filters[key]?.includes(option.value); + if (key === "target_date") { + const valueExists = checkIfArraysHaveSameElements( + filters.target_date ?? [], + option.value + ); - if (valueExists) { - setFilters( - { - ...(filters ?? {}), - [option.key]: ((filters[key] ?? []) as any[])?.filter( - (val) => val !== option.value - ), - }, - !Boolean(viewId) - ); + setFilters({ + target_date: valueExists ? null : option.value, + }); } else { - setFilters( - { - ...(filters ?? {}), - [option.key]: [...((filters[key] ?? []) as any[]), option.value], - }, - !Boolean(viewId) - ); + const valueExists = filters[key]?.includes(option.value); + + if (valueExists) + setFilters( + { + [option.key]: ((filters[key] ?? []) as any[])?.filter( + (val) => val !== option.value + ), + }, + !Boolean(viewId) + ); + else + setFilters( + { + [option.key]: [...((filters[key] ?? []) as any[]), option.value], + }, + !Boolean(viewId) + ); } }} direction="left" diff --git a/apps/app/components/core/index.ts b/apps/app/components/core/index.ts index c50ce7251..1eb52590c 100644 --- a/apps/app/components/core/index.ts +++ b/apps/app/components/core/index.ts @@ -1,17 +1,12 @@ export * from "./board-view"; export * from "./calendar-view"; +export * from "./filters"; export * from "./gantt-chart-view"; export * from "./list-view"; +export * from "./modals"; export * from "./spreadsheet-view"; export * from "./sidebar"; -export * from "./bulk-delete-issues-modal"; -export * from "./existing-issues-list-modal"; -export * from "./filters-list"; -export * from "./gpt-assistant-modal"; -export * from "./image-upload-modal"; -export * from "./issues-view-filter"; export * from "./issues-view"; -export * from "./link-modal"; export * from "./image-picker-popover"; export * from "./feeds"; export * from "./theme-switch"; diff --git a/apps/app/components/core/list-view/all-lists.tsx b/apps/app/components/core/list-view/all-lists.tsx index fd063728a..fcedf169a 100644 --- a/apps/app/components/core/list-view/all-lists.tsx +++ b/apps/app/components/core/list-view/all-lists.tsx @@ -38,7 +38,7 @@ export const AllLists: React.FC = ({ return ( <> {groupedByIssues && ( -
+
{Object.keys(groupedByIssues).map((singleGroup) => { const currentState = selectedGroup === "state" ? states?.find((s) => s.id === singleGroup) : null; diff --git a/apps/app/components/core/bulk-delete-issues-modal.tsx b/apps/app/components/core/modals/bulk-delete-issues-modal.tsx similarity index 100% rename from apps/app/components/core/bulk-delete-issues-modal.tsx rename to apps/app/components/core/modals/bulk-delete-issues-modal.tsx diff --git a/apps/app/components/core/existing-issues-list-modal.tsx b/apps/app/components/core/modals/existing-issues-list-modal.tsx similarity index 100% rename from apps/app/components/core/existing-issues-list-modal.tsx rename to apps/app/components/core/modals/existing-issues-list-modal.tsx diff --git a/apps/app/components/core/gpt-assistant-modal.tsx b/apps/app/components/core/modals/gpt-assistant-modal.tsx similarity index 100% rename from apps/app/components/core/gpt-assistant-modal.tsx rename to apps/app/components/core/modals/gpt-assistant-modal.tsx diff --git a/apps/app/components/core/image-upload-modal.tsx b/apps/app/components/core/modals/image-upload-modal.tsx similarity index 100% rename from apps/app/components/core/image-upload-modal.tsx rename to apps/app/components/core/modals/image-upload-modal.tsx diff --git a/apps/app/components/core/modals/index.ts b/apps/app/components/core/modals/index.ts new file mode 100644 index 000000000..5f55020e4 --- /dev/null +++ b/apps/app/components/core/modals/index.ts @@ -0,0 +1,5 @@ +export * from "./bulk-delete-issues-modal"; +export * from "./existing-issues-list-modal"; +export * from "./gpt-assistant-modal"; +export * from "./image-upload-modal"; +export * from "./link-modal"; diff --git a/apps/app/components/core/link-modal.tsx b/apps/app/components/core/modals/link-modal.tsx similarity index 100% rename from apps/app/components/core/link-modal.tsx rename to apps/app/components/core/modals/link-modal.tsx diff --git a/apps/app/components/icons/calendar-after-icon.tsx b/apps/app/components/icons/calendar-after-icon.tsx new file mode 100644 index 000000000..278e500f0 --- /dev/null +++ b/apps/app/components/icons/calendar-after-icon.tsx @@ -0,0 +1,26 @@ +import React from "react"; + +import type { Props } from "./types"; + +export const CalendarAfterIcon: React.FC = ({ width = "24", height = "24", className }) => ( + + + + + + + + + + +); \ No newline at end of file diff --git a/apps/app/components/icons/calendar-before-icon.tsx b/apps/app/components/icons/calendar-before-icon.tsx new file mode 100644 index 000000000..f2651c084 --- /dev/null +++ b/apps/app/components/icons/calendar-before-icon.tsx @@ -0,0 +1,37 @@ +import React from "react"; + +import type { Props } from "./types"; + +export const CalendarBeforeIcon: React.FC = ({ width = "24", height = "24", className }) => ( + + + + + + + + + + + + + + + + +); diff --git a/apps/app/components/icons/calendar-month-icon.tsx b/apps/app/components/icons/calendar-month-icon.tsx index a9f5042c9..dbfc43c50 100644 --- a/apps/app/components/icons/calendar-month-icon.tsx +++ b/apps/app/components/icons/calendar-month-icon.tsx @@ -3,17 +3,17 @@ import React from "react"; import type { Props } from "./types"; export const CalendarMonthIcon: React.FC = ({ width = "24", height = "24", className }) => ( - - - - ); + + + +); diff --git a/apps/app/components/icons/index.ts b/apps/app/components/icons/index.ts index 07ecafd24..db7aad041 100644 --- a/apps/app/components/icons/index.ts +++ b/apps/app/components/icons/index.ts @@ -4,6 +4,8 @@ export * from "./backlog-state-icon"; export * from "./blocked-icon"; export * from "./blocker-icon"; export * from "./bolt-icon"; +export * from "./calendar-before-icon"; +export * from "./calendar-after-icon"; export * from "./calendar-month-icon"; export * from "./cancel-icon"; export * from "./cancelled-state-icon"; diff --git a/apps/app/components/ui/multi-level-dropdown.tsx b/apps/app/components/ui/multi-level-dropdown.tsx index ee096774f..e0d5b0f17 100644 --- a/apps/app/components/ui/multi-level-dropdown.tsx +++ b/apps/app/components/ui/multi-level-dropdown.tsx @@ -18,6 +18,7 @@ type MultiLevelDropdownProps = { label: string | JSX.Element; value: any; selected?: boolean; + element?: JSX.Element; }[]; }[]; onSelect: (value: any) => void; @@ -35,117 +36,121 @@ export const MultiLevelDropdown: React.FC = ({ const [openChildFor, setOpenChildFor] = useState(null); return ( - - {({ open }) => ( - <> -
- setOpenChildFor(null)} - className={`group flex items-center justify-between gap-2 rounded-md border border-brand-base px-3 py-1.5 text-xs shadow-sm duration-300 focus:outline-none ${ - open ? "bg-brand-surface-1 text-brand-base" : "text-brand-secondary" - }`} + <> + + {({ open }) => ( + <> +
+ setOpenChildFor(null)} + className={`group flex items-center justify-between gap-2 rounded-md border border-brand-base px-3 py-1.5 text-xs shadow-sm duration-300 focus:outline-none ${ + open ? "bg-brand-surface-1 text-brand-base" : "text-brand-secondary" + }`} + > + {label} + +
+ - {label} -
- - - {options.map((option) => ( -
- { - if (option.children) { - e.stopPropagation(); - e.preventDefault(); + + {options.map((option) => ( +
+ { + if (option.children) { + e.stopPropagation(); + e.preventDefault(); - if (openChildFor === option.id) setOpenChildFor(null); - else setOpenChildFor(option.id); - } else { - onSelect(option.value); - } - }} - className="w-full" - > - {({ active }) => ( - <> -
- {direction === "left" && option.children && ( -
- - )} -
- {option.children && option.id === openChildFor && ( -
-
- {option.children.map((child) => ( - - ))} + {direction === "left" && option.children && ( +
+ + )} + + {option.children && option.id === openChildFor && ( +
+
+ {option.children.map((child) => { + if (child.element) return child.element; + else + return ( + + ); + })} +
-
- )} -
- ))} -
- - - )} -
+ )} +
+ ))} + + + + )} + + ); }; diff --git a/apps/app/components/views/select-filters.tsx b/apps/app/components/views/select-filters.tsx index 164c4f58c..6c2c04954 100644 --- a/apps/app/components/views/select-filters.tsx +++ b/apps/app/components/views/select-filters.tsx @@ -1,3 +1,5 @@ +import { useState } from "react"; + import { useRouter } from "next/router"; import useSWR from "swr"; @@ -6,18 +8,22 @@ import useSWR from "swr"; import stateService from "services/state.service"; import projectService from "services/project.service"; import issuesService from "services/issues.service"; +// components +import { DueDateFilterModal } from "components/core"; // ui import { Avatar, MultiLevelDropdown } from "components/ui"; // icons import { getPriorityIcon, getStateGroupIcon } from "components/icons"; // helpers import { getStatesList } from "helpers/state.helper"; +import { checkIfArraysHaveSameElements } from "helpers/array.helper"; // types import { IIssueFilterOptions, IQuery } from "types"; // fetch-keys import { PROJECT_ISSUE_LABELS, PROJECT_MEMBERS, STATES_LIST } from "constants/fetch-keys"; // constants import { PRIORITIES } from "constants/project"; +import { DUE_DATES } from "constants/due-dates"; type Props = { filters: Partial | IQuery; @@ -32,6 +38,8 @@ export const SelectFilters: React.FC = ({ direction = "right", height = "md", }) => { + const [isDueDateFilterModalOpen, setIsDueDateFilterModalOpen] = useState(false); + const router = useRouter(); const { workspaceSlug, projectId } = router.query; @@ -58,125 +66,163 @@ export const SelectFilters: React.FC = ({ ); return ( - ({ - id: priority === null ? "null" : priority, - label: ( -
- {getPriorityIcon(priority)} {priority ?? "None"} -
- ), - value: { - key: "priority", - value: priority === null ? "null" : priority, + <> + {isDueDateFilterModalOpen && ( + setIsDueDateFilterModalOpen(false)} + /> + )} + ({ + id: priority === null ? "null" : priority, + label: ( +
+ {getPriorityIcon(priority)} {priority ?? "None"} +
+ ), + value: { + key: "priority", + value: priority === null ? "null" : priority, + }, + selected: filters?.priority?.includes(priority === null ? "null" : priority), + })), + ], + }, + { + id: "state", + label: "State", + value: statesList, + children: [ + ...statesList.map((state) => ({ + id: state.id, + label: ( +
+ {getStateGroupIcon(state.group, "16", "16", state.color)} {state.name} +
+ ), + value: { + key: "state", + value: state.id, + }, + selected: filters?.state?.includes(state.id), + })), + ], + }, + { + id: "assignees", + label: "Assignees", + value: members, + children: [ + ...(members?.map((member) => ({ + id: member.member.id, + label: ( +
+ + {member.member.first_name && member.member.first_name !== "" + ? member.member.first_name + : member.member.email} +
+ ), + value: { + key: "assignees", + value: member.member.id, + }, + selected: filters?.assignees?.includes(member.member.id), + })) ?? []), + ], + }, + { + id: "created_by", + label: "Created by", + value: members, + children: [ + ...(members?.map((member) => ({ + id: member.member.id, + label: ( +
+ + {member.member.first_name && member.member.first_name !== "" + ? member.member.first_name + : member.member.email} +
+ ), + value: { + key: "created_by", + value: member.member.id, + }, + selected: filters?.created_by?.includes(member.member.id), + })) ?? []), + ], + }, + { + id: "labels", + label: "Labels", + value: issueLabels, + children: [ + ...(issueLabels?.map((label) => ({ + id: label.id, + label: ( +
+
+ {label.name} +
+ ), + value: { + key: "labels", + value: label.id, + }, + selected: filters?.labels?.includes(label.id), + })) ?? []), + ], + }, + { + id: "target_date", + label: "Due date", + value: DUE_DATES, + children: [ + ...(DUE_DATES?.map((option) => ({ + id: option.name, + label: option.name, + value: { + key: "target_date", + value: option.value, + }, + selected: checkIfArraysHaveSameElements(filters?.target_date ?? [], option.value), + })) ?? []), + { + id: "custom", + label: "Custom", + value: "custom", + element: ( + + ), }, - selected: filters?.priority?.includes(priority === null ? "null" : priority), - })), - ], - }, - { - id: "state", - label: "State", - value: statesList, - children: [ - ...statesList.map((state) => ({ - id: state.id, - label: ( -
- {getStateGroupIcon(state.group, "16", "16", state.color)} {state.name} -
- ), - value: { - key: "state", - value: state.id, - }, - selected: filters?.state?.includes(state.id), - })), - ], - }, - { - id: "assignees", - label: "Assignees", - value: members, - children: [ - ...(members?.map((member) => ({ - id: member.member.id, - label: ( -
- - {member.member.first_name && member.member.first_name !== "" - ? member.member.first_name - : member.member.email} -
- ), - value: { - key: "assignees", - value: member.member.id, - }, - selected: filters?.assignees?.includes(member.member.id), - })) ?? []), - ], - }, - { - id: "created_by", - label: "Created By", - value: members, - children: [ - ...(members?.map((member) => ({ - id: member.member.id, - label: ( -
- - {member.member.first_name && member.member.first_name !== "" - ? member.member.first_name - : member.member.email} -
- ), - value: { - key: "created_by", - value: member.member.id, - }, - selected: filters?.created_by?.includes(member.member.id), - })) ?? []), - ], - }, - { - id: "labels", - label: "Labels", - value: issueLabels, - children: [ - ...(issueLabels?.map((label) => ({ - id: label.id, - label: ( -
-
- {label.name} -
- ), - value: { - key: "labels", - value: label.id, - }, - selected: filters?.labels?.includes(label.id), - })) ?? []), - ], - }, - ]} - /> + ], + }, + ]} + /> + ); }; diff --git a/apps/app/constants/due-dates.ts b/apps/app/constants/due-dates.ts new file mode 100644 index 000000000..362fd41a5 --- /dev/null +++ b/apps/app/constants/due-dates.ts @@ -0,0 +1,37 @@ +// helper +import { renderDateFormat } from "helpers/date-time.helper"; + +export const DUE_DATES = [ + { + name: "Last week", + value: [ + `${renderDateFormat(new Date(new Date().getTime() - 7 * 24 * 60 * 60 * 1000))};after`, + `${renderDateFormat(new Date())};before`, + ], + }, + { + name: "2 weeks from now", + value: [ + `${renderDateFormat(new Date())};after`, + `${renderDateFormat(new Date(new Date().getTime() + 14 * 24 * 60 * 60 * 1000))};before`, + ], + }, + { + name: "1 month from now", + value: [ + `${renderDateFormat(new Date())};after`, + `${renderDateFormat( + new Date(new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()) + )};before`, + ], + }, + { + name: "2 months from now", + value: [ + `${renderDateFormat(new Date())};after`, + `${renderDateFormat( + new Date(new Date().getFullYear(), new Date().getMonth() + 2, new Date().getDate()) + )};before`, + ], + }, +]; diff --git a/apps/app/constants/project.ts b/apps/app/constants/project.ts index 9ddae96c8..41688f7a7 100644 --- a/apps/app/constants/project.ts +++ b/apps/app/constants/project.ts @@ -1,3 +1,4 @@ + export const NETWORK_CHOICES = { "0": "Secret", "2": "Public" }; export const GROUP_CHOICES = { diff --git a/apps/app/contexts/issue-view.context.tsx b/apps/app/contexts/issue-view.context.tsx index d2a4496c9..454023d54 100644 --- a/apps/app/contexts/issue-view.context.tsx +++ b/apps/app/contexts/issue-view.context.tsx @@ -89,6 +89,7 @@ export const initialState: StateType = { issue__assignees__id: null, issue__labels__id: null, created_by: null, + target_date: null, }, }; diff --git a/apps/app/helpers/array.helper.ts b/apps/app/helpers/array.helper.ts index 2432f88ad..f8134b440 100644 --- a/apps/app/helpers/array.helper.ts +++ b/apps/app/helpers/array.helper.ts @@ -42,3 +42,11 @@ export const findStringWithMostCharacters = (strings: string[]) => strings.reduce((longestString, currentString) => currentString.length > longestString.length ? currentString : longestString ); + +export const checkIfArraysHaveSameElements = (arr1: any[] | null, arr2: any[] | null): boolean => { + if (!arr1 || !arr2) return false; + if (!Array.isArray(arr1) || !Array.isArray(arr2)) return false; + if (arr1.length === 0 && arr2.length === 0) return true; + + return arr1.length === arr2.length && arr1.every((e) => arr2.includes(e)); +}; diff --git a/apps/app/hooks/use-issues-view.tsx b/apps/app/hooks/use-issues-view.tsx index 0e27ed049..cbd550053 100644 --- a/apps/app/hooks/use-issues-view.tsx +++ b/apps/app/hooks/use-issues-view.tsx @@ -60,6 +60,7 @@ const useIssuesView = () => { ? filters?.issue__labels__id.join(",") : undefined, created_by: filters?.created_by ? filters?.created_by.join(",") : undefined, + target_date: filters?.target_date ? filters?.target_date.join(",") : undefined, }; const { data: projectIssues } = useSWR( diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx index 78af8e9e1..917515931 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx @@ -159,7 +159,7 @@ const SingleCycle: React.FC = () => { > setAnalyticsModal(false)} />
diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/issues/index.tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/issues/index.tsx index 2270ce7f4..8fdab7f4f 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/issues/index.tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/issues/index.tsx @@ -99,7 +99,9 @@ const ProjectIssues: NextPage = () => { } > setAnalyticsModal(false)} /> - +
+ +
); diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/modules/[moduleId].tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/modules/[moduleId].tsx index 51b6b7a5b..8e15cc6f3 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/modules/[moduleId].tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/modules/[moduleId].tsx @@ -164,7 +164,7 @@ const SingleModule: React.FC = () => { setAnalyticsModal(false)} />
diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/views/[viewId].tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/views/[viewId].tsx index 080aa9011..b1cbf97f2 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/views/[viewId].tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/views/[viewId].tsx @@ -101,7 +101,9 @@ const SingleView: React.FC = () => {
} > - +
+ +
); diff --git a/apps/app/styles/globals.css b/apps/app/styles/globals.css index ccfda2248..75692e97b 100644 --- a/apps/app/styles/globals.css +++ b/apps/app/styles/globals.css @@ -179,25 +179,6 @@ body { outline: none; } -/* react datepicker styling */ -.react-datepicker-wrapper input::placeholder { - color: rgba(var(--color-text-secondary)); - opacity: 1; -} - -.react-datepicker-wrapper input:-ms-input-placeholder { - color: rgba(var(--color-text-secondary)); -} - -.react-datepicker-wrapper .react-datepicker__close-icon::after { - background: transparent; - color: rgba(var(--color-text-secondary)); -} - -.react-datepicker-popper { - z-index: 30 !important; -} - .conical-gradient { background: conic-gradient( from 180deg at 50% 50%, diff --git a/apps/app/styles/react-datepicker.css b/apps/app/styles/react-datepicker.css index 918f4ed66..2c45fda44 100644 --- a/apps/app/styles/react-datepicker.css +++ b/apps/app/styles/react-datepicker.css @@ -81,7 +81,7 @@ } .react-datepicker__day-name { - color: rgba(var(--color-text-base)) !important; + color: rgba(var(--color-text-secondary)) !important; } .react-datepicker__week { diff --git a/apps/app/types/issues.d.ts b/apps/app/types/issues.d.ts index a33a04ffc..aac0ec4eb 100644 --- a/apps/app/types/issues.d.ts +++ b/apps/app/types/issues.d.ts @@ -239,6 +239,7 @@ export interface IIssueLite { export interface IIssueFilterOptions { type: "active" | "backlog" | null; assignees: string[] | null; + target_date: string[] | null; state: string[] | null; labels: string[] | null; issue__assignees__id: string[] | null;