From 3c9e62d308204864eed03af93955a85ebde34c93 Mon Sep 17 00:00:00 2001 From: gurusainath Date: Thu, 14 Sep 2023 22:28:42 +0530 Subject: [PATCH] chore: filter render UI and Functionality implementation --- .../issue-layouts/calendar/index.tsx | 6 + .../issue-layouts/display-filters/index.tsx | 16 +- .../filters-preview/assignees.tsx | 88 ++++++++++ .../filters-preview/created-by.tsx | 72 ++++++++ .../filters-preview/helpers/clear.tsx | 17 ++ .../filters-preview/helpers/content.tsx | 32 ++++ .../filters-preview/helpers/header.tsx | 12 ++ .../filters-preview/index copy.tsx | 92 ++++++++++ .../issue-layouts/filters-preview/index.tsx | 68 ++++++++ .../issue-layouts/filters-preview/labels.tsx | 75 +++++++++ .../filters-preview/priority.tsx | 79 +++++++++ .../filters-preview/start-date.tsx | 56 ++++++ .../filters-preview/state-group.tsx | 159 ++++++++++++++++++ .../issue-layouts/filters-preview/state.tsx | 68 ++++++++ .../filters-preview/target-date.tsx | 56 ++++++ .../issue-layouts/filters/index.tsx | 25 ++- web/components/issue-layouts/gantt/index.tsx | 6 + .../issue-layouts/helpers/dropdown.tsx | 2 +- .../issue-layouts/layout-selection.tsx | 27 ++- web/components/issue-layouts/list/index.tsx | 6 + web/components/issue-layouts/root.tsx | 33 +++- .../issue-layouts/spreadsheet/index.tsx | 6 + .../m-store/[workspace_slug]/my-issues.tsx | 6 + web/store/issue-views/Issues.ts | 81 ++++++--- web/store/issue-views/issue_data.ts | 59 ++++++- web/store/issue-views/issue_filters.ts | 58 ++++++- 26 files changed, 1129 insertions(+), 76 deletions(-) create mode 100644 web/components/issue-layouts/calendar/index.tsx create mode 100644 web/components/issue-layouts/filters-preview/assignees.tsx create mode 100644 web/components/issue-layouts/filters-preview/created-by.tsx create mode 100644 web/components/issue-layouts/filters-preview/helpers/clear.tsx create mode 100644 web/components/issue-layouts/filters-preview/helpers/content.tsx create mode 100644 web/components/issue-layouts/filters-preview/helpers/header.tsx create mode 100644 web/components/issue-layouts/filters-preview/index copy.tsx create mode 100644 web/components/issue-layouts/filters-preview/index.tsx create mode 100644 web/components/issue-layouts/filters-preview/labels.tsx create mode 100644 web/components/issue-layouts/filters-preview/priority.tsx create mode 100644 web/components/issue-layouts/filters-preview/start-date.tsx create mode 100644 web/components/issue-layouts/filters-preview/state-group.tsx create mode 100644 web/components/issue-layouts/filters-preview/state.tsx create mode 100644 web/components/issue-layouts/filters-preview/target-date.tsx create mode 100644 web/components/issue-layouts/gantt/index.tsx create mode 100644 web/components/issue-layouts/list/index.tsx create mode 100644 web/components/issue-layouts/spreadsheet/index.tsx diff --git a/web/components/issue-layouts/calendar/index.tsx b/web/components/issue-layouts/calendar/index.tsx new file mode 100644 index 000000000..c3c4147f9 --- /dev/null +++ b/web/components/issue-layouts/calendar/index.tsx @@ -0,0 +1,6 @@ +import React from "react"; + +export const IssueCalendarViewRoot = () => { + console.log(); + return
IssueCalendarViewRoot
; +}; diff --git a/web/components/issue-layouts/display-filters/index.tsx b/web/components/issue-layouts/display-filters/index.tsx index d629327af..0c7ff05fd 100644 --- a/web/components/issue-layouts/display-filters/index.tsx +++ b/web/components/issue-layouts/display-filters/index.tsx @@ -37,35 +37,33 @@ export const DisplayFiltersSelection = observer(() => { ?.extra_options?.[issueFilterStore?.issueLayout].access; return ( -
-
- Search container -
-
+
+
Search container
+
{/* display properties */} {handleDisplayPropertiesSectionVisibility && ( -
+
)} {/* group by */} {handleDisplayFilterSectionVisibility("group_by") && ( -
+
)} {/* order by */} {handleDisplayFilterSectionVisibility("order_by") && ( -
+
)} {/* issue type */} {handleDisplayFilterSectionVisibility("issue_type") && ( -
+
)} diff --git a/web/components/issue-layouts/filters-preview/assignees.tsx b/web/components/issue-layouts/filters-preview/assignees.tsx new file mode 100644 index 000000000..80bfbc084 --- /dev/null +++ b/web/components/issue-layouts/filters-preview/assignees.tsx @@ -0,0 +1,88 @@ +import React from "react"; +// components +import { FilterPreviewHeader } from "./helpers/header"; +import { FilterPreviewContent } from "./helpers/content"; +import { FilterPreviewClear } from "./helpers/clear"; +// mobx react lite +import { observer } from "mobx-react-lite"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +import { RootStore } from "store/root"; + +export const MemberIcons = ({ + display_name, + avatar, +}: { + display_name: string; + avatar: string | null; +}) => ( +
+ {avatar ? ( + {display_name + ) : ( +
+ {(display_name ?? "U")[0]} +
+ )} +
+); + +export const FilterAssignees = observer(() => { + const store: RootStore = useMobxStore(); + const { issueFilters: issueFilterStore } = store; + + const handleFilter = (key: string, value: string) => { + let _value = + issueFilterStore?.userFilters?.filters?.[key] != null && + issueFilterStore?.userFilters?.filters?.[key].filter((p: string) => p != value); + _value = _value && _value.length > 0 ? _value : null; + issueFilterStore.handleUserFilter("filters", key, _value); + }; + + const clearFilter = () => { + issueFilterStore.handleUserFilter("filters", "assignees", null); + }; + + return ( + <> + {issueFilterStore?.userFilters?.filters?.assignees != null && ( +
+
+ +
+
+ {issueFilterStore?.projectMembers && + issueFilterStore?.projectMembers.length > 0 && + issueFilterStore?.projectMembers.map( + (_member) => + issueFilterStore?.userFilters?.filters?.assignees != null && + issueFilterStore?.userFilters?.filters?.assignees.includes( + _member?.member?.id + ) && ( + + } + title={`${_member?.member?.display_name}`} + onClick={() => handleFilter("assignees", _member?.member?.id)} + className="border border-custom-border-100 bg-custom-background-100" + /> + ) + )} +
+ +
+
+
+ )} + + ); +}); diff --git a/web/components/issue-layouts/filters-preview/created-by.tsx b/web/components/issue-layouts/filters-preview/created-by.tsx new file mode 100644 index 000000000..c69234b7c --- /dev/null +++ b/web/components/issue-layouts/filters-preview/created-by.tsx @@ -0,0 +1,72 @@ +import React from "react"; +// components +import { MemberIcons } from "./assignees"; +import { FilterPreviewHeader } from "./helpers/header"; +import { FilterPreviewContent } from "./helpers/content"; +import { FilterPreviewClear } from "./helpers/clear"; +// mobx react lite +import { observer } from "mobx-react-lite"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +import { RootStore } from "store/root"; + +export const FilterCreatedBy = observer(() => { + const store: RootStore = useMobxStore(); + const { issueFilters: issueFilterStore } = store; + + const handleFilter = (key: string, value: string) => { + let _value = + issueFilterStore?.userFilters?.filters?.[key] != null && + issueFilterStore?.userFilters?.filters?.[key].filter((p: string) => p != value); + _value = _value && _value.length > 0 ? _value : null; + issueFilterStore.handleUserFilter("filters", key, _value); + }; + + const clearFilter = () => { + issueFilterStore.handleUserFilter("filters", "created_by", null); + }; + + return ( + <> + {issueFilterStore?.userFilters?.filters?.created_by != null && ( +
+
+ +
+ +
+ {issueFilterStore?.projectMembers && + issueFilterStore?.projectMembers.length > 0 && + issueFilterStore?.projectMembers.map( + (_member) => + issueFilterStore?.userFilters?.filters?.created_by != null && + issueFilterStore?.userFilters?.filters?.created_by.includes( + _member?.member?.id + ) && ( + + } + onClick={() => handleFilter("created_by", _member?.member?.id)} + className="border border-custom-border-100 bg-custom-background-100" + /> + ) + )} +
+ +
+
+
+ )} + + ); +}); diff --git a/web/components/issue-layouts/filters-preview/helpers/clear.tsx b/web/components/issue-layouts/filters-preview/helpers/clear.tsx new file mode 100644 index 000000000..d2cc57a37 --- /dev/null +++ b/web/components/issue-layouts/filters-preview/helpers/clear.tsx @@ -0,0 +1,17 @@ +// lucide icons +import { X } from "lucide-react"; + +interface IFilterPreviewClear { + onClick?: () => void; +} + +export const FilterPreviewClear = ({ onClick }: IFilterPreviewClear) => ( +
{ + if (onClick) onClick(); + }} + > + +
+); diff --git a/web/components/issue-layouts/filters-preview/helpers/content.tsx b/web/components/issue-layouts/filters-preview/helpers/content.tsx new file mode 100644 index 000000000..dc56bc391 --- /dev/null +++ b/web/components/issue-layouts/filters-preview/helpers/content.tsx @@ -0,0 +1,32 @@ +import { FilterPreviewClear } from "./clear"; + +interface IFilterPreviewContent { + icon?: React.ReactNode; + title?: string; + onClick?: () => void; + className?: string; + style?: any; +} + +export const FilterPreviewContent = ({ + icon, + title, + onClick, + className, + style, +}: IFilterPreviewContent) => ( +
+
{icon}
+
{title}
+
+ { + if (onClick) onClick(); + }} + /> +
+
+); diff --git a/web/components/issue-layouts/filters-preview/helpers/header.tsx b/web/components/issue-layouts/filters-preview/helpers/header.tsx new file mode 100644 index 000000000..5fc9c9caa --- /dev/null +++ b/web/components/issue-layouts/filters-preview/helpers/header.tsx @@ -0,0 +1,12 @@ +interface IFilterPreviewHeader { + title: string; +} + +export const FilterPreviewHeader = ({ title }: IFilterPreviewHeader) => { + console.log(); + return ( +
+
{title}
+
+ ); +}; diff --git a/web/components/issue-layouts/filters-preview/index copy.tsx b/web/components/issue-layouts/filters-preview/index copy.tsx new file mode 100644 index 000000000..d83f3f725 --- /dev/null +++ b/web/components/issue-layouts/filters-preview/index copy.tsx @@ -0,0 +1,92 @@ +import React from "react"; +// components +import { FilterPriority } from "./priority"; +import { FilterState } from "./state"; +import { FilterStateGroup } from "./state-group"; +import { FilterAssignees } from "./assignees"; +import { FilterCreatedBy } from "./created-by"; +import { FilterLabels } from "./labels"; +import { FilterStartDate } from "./start-date"; +import { FilterTargetDate } from "./target-date"; +// mobx react lite +import { observer } from "mobx-react-lite"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +import { RootStore } from "store/root"; +// default data +import { issueFilterVisibilityData } from "store/issue-views/issue_data"; + +export const FilterSelection = observer(() => { + const store: RootStore = useMobxStore(); + const { issueFilters: issueFilterStore } = store; + + const handleFilterSectionVisibility = (section_key: string) => + issueFilterStore?.issueView && + issueFilterStore?.issueLayout && + issueFilterVisibilityData[ + issueFilterStore?.issueView === "my_issues" ? "my_issues" : "others" + ]?.filters?.[issueFilterStore?.issueLayout]?.includes(section_key); + + return ( +
+
Search container
+
+ {/* priority */} + {handleFilterSectionVisibility("priority") && ( +
+ +
+ )} + + {/* state group */} + {handleFilterSectionVisibility("state_group") && ( +
+ +
+ )} + + {/* state */} + {handleFilterSectionVisibility("state") && ( +
+ +
+ )} + + {/* assignees */} + {handleFilterSectionVisibility("assignees") && ( +
+ +
+ )} + + {/* created_by */} + {handleFilterSectionVisibility("created_by") && ( +
+ +
+ )} + + {/* labels */} + {handleFilterSectionVisibility("labels") && ( +
+ +
+ )} + + {/* start_date */} + {handleFilterSectionVisibility("start_date") && ( +
+ +
+ )} + + {/* due_date */} + {handleFilterSectionVisibility("due_date") && ( +
+ +
+ )} +
+
+ ); +}); diff --git a/web/components/issue-layouts/filters-preview/index.tsx b/web/components/issue-layouts/filters-preview/index.tsx new file mode 100644 index 000000000..fd202e11b --- /dev/null +++ b/web/components/issue-layouts/filters-preview/index.tsx @@ -0,0 +1,68 @@ +import React from "react"; +// components +import { FilterPriority } from "./priority"; +import { FilterState } from "./state"; +import { FilterStateGroup } from "./state-group"; +import { FilterAssignees } from "./assignees"; +import { FilterCreatedBy } from "./created-by"; +import { FilterLabels } from "./labels"; +import { FilterStartDate } from "./start-date"; +import { FilterTargetDate } from "./target-date"; +// mobx react lite +import { observer } from "mobx-react-lite"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +import { RootStore } from "store/root"; +// default data +import { issueFilterVisibilityData } from "store/issue-views/issue_data"; + +export const FilterPreview = observer(() => { + const store: RootStore = useMobxStore(); + const { issueFilters: issueFilterStore } = store; + + const handleFilterSectionVisibility = (section_key: string) => + issueFilterStore?.issueView && + issueFilterStore?.issueLayout && + issueFilterVisibilityData[ + issueFilterStore?.issueView === "my_issues" ? "my_issues" : "others" + ]?.filters?.[issueFilterStore?.issueLayout]?.includes(section_key); + + const validateFiltersAvailability = + issueFilterStore?.userFilters?.filters != null && + Object.keys(issueFilterStore?.userFilters?.filters).length > 0 && + Object.keys(issueFilterStore?.userFilters?.filters) + .map((key) => issueFilterStore?.userFilters?.filters?.[key]?.length) + .filter((v) => v != undefined || v != null).length > 0; + + return ( + <> + {validateFiltersAvailability && ( +
+ {/* priority */} + {handleFilterSectionVisibility("priority") && } + + {/* state group */} + {handleFilterSectionVisibility("state_group") && } + + {/* state */} + {handleFilterSectionVisibility("state") && } + + {/* assignees */} + {handleFilterSectionVisibility("assignees") && } + + {/* created_by */} + {handleFilterSectionVisibility("created_by") && } + + {/* labels */} + {handleFilterSectionVisibility("labels") && } + + {/* start_date */} + {handleFilterSectionVisibility("start_date") && } + + {/* due_date */} + {handleFilterSectionVisibility("due_date") && } +
+ )} + + ); +}); diff --git a/web/components/issue-layouts/filters-preview/labels.tsx b/web/components/issue-layouts/filters-preview/labels.tsx new file mode 100644 index 000000000..dbe825a7c --- /dev/null +++ b/web/components/issue-layouts/filters-preview/labels.tsx @@ -0,0 +1,75 @@ +import React from "react"; +// components +import { FilterPreviewHeader } from "./helpers/header"; +import { FilterPreviewContent } from "./helpers/content"; +import { FilterPreviewClear } from "./helpers/clear"; +// mobx react lite +import { observer } from "mobx-react-lite"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +import { RootStore } from "store/root"; + +const LabelIcons = ({ color }: { color: string }) => ( +
+
+
+); + +export const FilterLabels = observer(() => { + const store: RootStore = useMobxStore(); + const { issueFilters: issueFilterStore } = store; + + const stateStyles = (color: any) => ({ color: color, backgroundColor: `${color}20` }); + + const handleFilter = (key: string, value: string) => { + let _value = + issueFilterStore?.userFilters?.filters?.[key] != null && + issueFilterStore?.userFilters?.filters?.[key].filter((p: string) => p != value); + _value = _value && _value.length > 0 ? _value : null; + issueFilterStore.handleUserFilter("filters", key, _value); + }; + + const clearFilter = () => { + issueFilterStore.handleUserFilter("filters", "labels", null); + }; + + const handleLabels = + issueFilterStore.issueView && issueFilterStore.issueView === "my_issues" + ? issueFilterStore?.workspaceLabels + : issueFilterStore?.projectLabels; + + return ( + <> + {issueFilterStore?.userFilters?.filters?.labels != null && ( +
+
+ +
+ +
+ {handleLabels && + handleLabels.length > 0 && + handleLabels.map( + (_label) => + issueFilterStore?.userFilters?.filters?.labels != null && + issueFilterStore?.userFilters?.filters?.labels.includes(_label?.id) && ( + handleFilter("labels", _label?.id)} + icon={} + title={_label.name} + style={stateStyles(_label.color)} + /> + ) + )} +
+ +
+
+
+ )} + + ); +}); diff --git a/web/components/issue-layouts/filters-preview/priority.tsx b/web/components/issue-layouts/filters-preview/priority.tsx new file mode 100644 index 000000000..44780d81f --- /dev/null +++ b/web/components/issue-layouts/filters-preview/priority.tsx @@ -0,0 +1,79 @@ +import React from "react"; +// lucide icons +import { AlertCircle, SignalHigh, SignalMedium, SignalLow, Ban } from "lucide-react"; +// components +import { FilterPreviewHeader } from "./helpers/header"; +import { FilterPreviewContent } from "./helpers/content"; +import { FilterPreviewClear } from "./helpers/clear"; +// mobx react lite +import { observer } from "mobx-react-lite"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +import { RootStore } from "store/root"; + +const PriorityIcons = ({ priority }: { priority: string }) => { + if (priority === "urgent") return ; + if (priority === "high") return ; + if (priority === "medium") return ; + if (priority === "low") return ; + return ; +}; + +const classNamesStyling = (priority: string) => { + if (priority == "urgent") return "bg-red-500/20 text-red-500"; + if (priority == "high") return "bg-orange-500/20 text-orange-500 !-pt-[30px]"; + if (priority == "medium") return "bg-orange-500/20 text-orange-500 -pt-2"; + if (priority == "low") return "bg-green-500/20 text-green-500 -pt-2"; + return "bg-gray-500/10 text-gray-500"; +}; + +export const FilterPriority = observer(() => { + const store: RootStore = useMobxStore(); + const { issueFilters: issueFilterStore } = store; + + const handleFilter = (key: string, value: string) => { + let _value = + issueFilterStore?.userFilters?.filters?.[key] != null && + issueFilterStore?.userFilters?.filters?.[key].filter((p: string) => p != value); + _value = _value && _value.length > 0 ? _value : null; + issueFilterStore.handleUserFilter("filters", key, _value); + }; + + const clearFilter = () => { + issueFilterStore.handleUserFilter("filters", "priority", null); + }; + + return ( + <> + {issueFilterStore?.userFilters?.filters?.priority != null && ( +
+
+ +
+
+ {issueFilterStore?.issueRenderFilters?.priority && + issueFilterStore?.issueRenderFilters?.priority.length > 0 && + issueFilterStore?.issueRenderFilters?.priority.map( + (_priority) => + issueFilterStore?.userFilters?.filters?.priority != null && + issueFilterStore?.userFilters?.filters?.priority.includes(_priority?.key) && ( + } + title={_priority.title} + className={classNamesStyling(_priority?.key)} + onClick={() => handleFilter("priority", _priority?.key)} + /> + ) + )} +
+ +
+
+
+ )} + + ); +}); diff --git a/web/components/issue-layouts/filters-preview/start-date.tsx b/web/components/issue-layouts/filters-preview/start-date.tsx new file mode 100644 index 000000000..796791762 --- /dev/null +++ b/web/components/issue-layouts/filters-preview/start-date.tsx @@ -0,0 +1,56 @@ +import React from "react"; +// components +import { FilterPreviewHeader } from "./helpers/header"; +import { FilterPreviewContent } from "./helpers/content"; +import { FilterPreviewClear } from "./helpers/clear"; +// mobx react lite +import { observer } from "mobx-react-lite"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +import { RootStore } from "store/root"; + +export const FilterStartDate = observer(() => { + const store: RootStore = useMobxStore(); + const { issueFilters: issueFilterStore } = store; + + const handleFilter = (key: string, value: string) => { + let _value = + issueFilterStore?.userFilters?.filters?.[key] != null && + issueFilterStore?.userFilters?.filters?.[key].filter((p: string) => p != value); + _value = _value && _value.length > 0 ? _value : null; + + issueFilterStore.handleUserFilter("filters", key, _value); + }; + + const clearFilter = () => { + issueFilterStore.handleUserFilter("filters", "start_date", null); + }; + + return ( + <> + {issueFilterStore?.userFilters?.filters?.start_date != null && ( +
+
+ +
+ +
+ {issueFilterStore?.issueRenderFilters?.start_date && + issueFilterStore?.issueRenderFilters?.start_date.length > 0 && + issueFilterStore?.issueRenderFilters?.start_date.map((_startDate) => ( + handleFilter("start_date", _startDate?.key)} + /> + ))} +
+
+ +
+
+ )} + + ); +}); diff --git a/web/components/issue-layouts/filters-preview/state-group.tsx b/web/components/issue-layouts/filters-preview/state-group.tsx new file mode 100644 index 000000000..e461f2eb0 --- /dev/null +++ b/web/components/issue-layouts/filters-preview/state-group.tsx @@ -0,0 +1,159 @@ +import React from "react"; +import { + StateGroupBacklogIcon, + StateGroupCancelledIcon, + StateGroupCompletedIcon, + StateGroupStartedIcon, + StateGroupUnstartedIcon, +} from "components/icons"; +// components +import { FilterPreviewHeader } from "./helpers/header"; +import { FilterPreviewContent } from "./helpers/content"; +import { FilterPreviewClear } from "./helpers/clear"; +// mobx react lite +import { observer } from "mobx-react-lite"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +import { RootStore } from "store/root"; +// constants +import { STATE_GROUP_COLORS } from "constants/state"; + +export const StateGroupIcons = ({ + stateGroup, + color = null, +}: { + stateGroup: string; + color?: string | null; +}) => { + if (stateGroup === "cancelled") + return ( + + ); + if (stateGroup === "completed") + return ( + + ); + if (stateGroup === "started") + return ( + + ); + if (stateGroup === "unstarted") + return ( + + ); + if (stateGroup === "backlog") + return ( +
+ +
+ ); + return <>; +}; + +export const stateStyles = (stateGroup: string, color: any) => { + if (stateGroup === "cancelled") { + return { + color: color ? color : STATE_GROUP_COLORS[stateGroup], + backgroundColor: `${color ? color : STATE_GROUP_COLORS[stateGroup]}20`, + }; + } + if (stateGroup === "completed") { + return { + color: color ? color : STATE_GROUP_COLORS[stateGroup], + backgroundColor: `${color ? color : STATE_GROUP_COLORS[stateGroup]}20`, + }; + } + if (stateGroup === "started") { + return { + color: color ? color : STATE_GROUP_COLORS[stateGroup], + backgroundColor: `${color ? color : STATE_GROUP_COLORS[stateGroup]}20`, + }; + } + if (stateGroup === "unstarted") { + return { + color: color ? color : STATE_GROUP_COLORS[stateGroup], + backgroundColor: `${color ? color : STATE_GROUP_COLORS[stateGroup]}20`, + }; + } + if (stateGroup === "backlog") { + return { + color: color ? color : STATE_GROUP_COLORS[stateGroup], + backgroundColor: `${color ? color : STATE_GROUP_COLORS[stateGroup]}20`, + }; + } +}; + +export const FilterStateGroup = observer(() => { + const store: RootStore = useMobxStore(); + const { issueFilters: issueFilterStore } = store; + + const handleFilter = (key: string, value: string) => { + let _value = + issueFilterStore?.userFilters?.filters?.[key] != null && + issueFilterStore?.userFilters?.filters?.[key].filter((p: string) => p != value); + _value = _value && _value.length > 0 ? _value : null; + issueFilterStore.handleUserFilter("filters", key, _value); + }; + + const clearFilter = () => { + issueFilterStore.handleUserFilter("filters", "state_group", null); + }; + + return ( + <> + {issueFilterStore?.userFilters?.filters?.state_group != null && ( +
+
+ +
+
+ {issueFilterStore?.issueRenderFilters?.state_group && + issueFilterStore?.issueRenderFilters?.state_group.length > 0 && + issueFilterStore?.issueRenderFilters?.state_group.map( + (_stateGroup) => + issueFilterStore?.userFilters?.filters?.state_group != null && + issueFilterStore?.userFilters?.filters?.state_group.includes( + _stateGroup?.key + ) && ( + } + title={_stateGroup.title} + style={stateStyles(_stateGroup?.key, null)} + onClick={() => handleFilter("state_group", _stateGroup?.key)} + /> + ) + )} +
+ +
+
+
+ )} + + ); +}); diff --git a/web/components/issue-layouts/filters-preview/state.tsx b/web/components/issue-layouts/filters-preview/state.tsx new file mode 100644 index 000000000..3e4088974 --- /dev/null +++ b/web/components/issue-layouts/filters-preview/state.tsx @@ -0,0 +1,68 @@ +import React from "react"; +// components +import { StateGroupIcons, stateStyles } from "./state-group"; +import { FilterPreviewHeader } from "./helpers/header"; +import { FilterPreviewContent } from "./helpers/content"; +import { FilterPreviewClear } from "./helpers/clear"; +// mobx react lite +import { observer } from "mobx-react-lite"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +import { RootStore } from "store/root"; +// store default data +import { issueStateGroupKeys } from "store/issue-views/issue_data"; + +export const FilterState = observer(() => { + const store: RootStore = useMobxStore(); + const { issueFilters: issueFilterStore } = store; + + const handleFilter = (key: string, value: string) => { + let _value = + issueFilterStore?.userFilters?.filters?.[key] != null && + issueFilterStore?.userFilters?.filters?.[key].filter((p: string) => p != value); + _value = _value && _value.length > 0 ? _value : null; + issueFilterStore.handleUserFilter("filters", key, _value); + }; + + const clearFilter = () => { + issueFilterStore.handleUserFilter("filters", "state", null); + }; + + return ( + <> + {issueFilterStore?.userFilters?.filters?.state != null && ( +
+
+ +
+
+ {issueStateGroupKeys.map( + (_stateGroup) => + issueFilterStore?.projectStates && + issueFilterStore?.projectStates[_stateGroup] && + issueFilterStore?.projectStates[_stateGroup].length > 0 && + issueFilterStore?.projectStates[_stateGroup].map( + (_state: any) => + issueFilterStore?.userFilters?.filters?.state != null && + issueFilterStore?.userFilters?.filters?.state.includes(_state?.id) && ( + } + title={_state?.name} + style={stateStyles(_state?.group, _state?.color)} + onClick={() => handleFilter("state", _state?.id)} + /> + ) + ) + )} +
+ +
+
+
+ )} + + ); +}); diff --git a/web/components/issue-layouts/filters-preview/target-date.tsx b/web/components/issue-layouts/filters-preview/target-date.tsx new file mode 100644 index 000000000..866f550b5 --- /dev/null +++ b/web/components/issue-layouts/filters-preview/target-date.tsx @@ -0,0 +1,56 @@ +import React from "react"; +// components +import { FilterPreviewHeader } from "./helpers/header"; +import { FilterPreviewContent } from "./helpers/content"; +import { FilterPreviewClear } from "./helpers/clear"; +// mobx react lite +import { observer } from "mobx-react-lite"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +import { RootStore } from "store/root"; + +export const FilterTargetDate = observer(() => { + const store: RootStore = useMobxStore(); + const { issueFilters: issueFilterStore } = store; + + const handleFilter = (key: string, value: string) => { + let _value = + issueFilterStore?.userFilters?.filters?.[key] != null && + issueFilterStore?.userFilters?.filters?.[key].filter((p: string) => p != value); + _value = _value && _value.length > 0 ? _value : null; + + issueFilterStore.handleUserFilter("filters", key, _value); + }; + + const clearFilter = () => { + issueFilterStore.handleUserFilter("filters", "target_date", null); + }; + + return ( + <> + {issueFilterStore?.userFilters?.filters?.target_date != null && ( +
+
+ +
+ +
+ {issueFilterStore?.issueRenderFilters?.due_date && + issueFilterStore?.issueRenderFilters?.due_date.length > 0 && + issueFilterStore?.issueRenderFilters?.due_date.map((_targetDate) => ( + handleFilter("target_date", _targetDate?.key)} + /> + ))} +
+
+ +
+
+ )} + + ); +}); diff --git a/web/components/issue-layouts/filters/index.tsx b/web/components/issue-layouts/filters/index.tsx index 2ad446eb3..d83f3f725 100644 --- a/web/components/issue-layouts/filters/index.tsx +++ b/web/components/issue-layouts/filters/index.tsx @@ -22,61 +22,60 @@ export const FilterSelection = observer(() => { const handleFilterSectionVisibility = (section_key: string) => issueFilterStore?.issueView && + issueFilterStore?.issueLayout && issueFilterVisibilityData[ issueFilterStore?.issueView === "my_issues" ? "my_issues" : "others" - ].filters.includes(section_key); + ]?.filters?.[issueFilterStore?.issueLayout]?.includes(section_key); return ( -
-
- Search container -
-
+
+
Search container
+
{/* priority */} {handleFilterSectionVisibility("priority") && ( -
+
)} {/* state group */} {handleFilterSectionVisibility("state_group") && ( -
+
)} {/* state */} {handleFilterSectionVisibility("state") && ( -
+
)} {/* assignees */} {handleFilterSectionVisibility("assignees") && ( -
+
)} {/* created_by */} {handleFilterSectionVisibility("created_by") && ( -
+
)} {/* labels */} {handleFilterSectionVisibility("labels") && ( -
+
)} {/* start_date */} {handleFilterSectionVisibility("start_date") && ( -
+
)} diff --git a/web/components/issue-layouts/gantt/index.tsx b/web/components/issue-layouts/gantt/index.tsx new file mode 100644 index 000000000..b00e20632 --- /dev/null +++ b/web/components/issue-layouts/gantt/index.tsx @@ -0,0 +1,6 @@ +import React from "react"; + +export const IssueGanttViewRoot = () => { + console.log(); + return
IssueGanttViewRoot
; +}; diff --git a/web/components/issue-layouts/helpers/dropdown.tsx b/web/components/issue-layouts/helpers/dropdown.tsx index 66b7537e9..988f1050e 100644 --- a/web/components/issue-layouts/helpers/dropdown.tsx +++ b/web/components/issue-layouts/helpers/dropdown.tsx @@ -37,7 +37,7 @@ export const IssueDropdown = ({ children, title = "Dropdown" }: IIssueDropdown) leaveFrom="opacity-100 translate-y-0" leaveTo="opacity-0 translate-y-1" > - +
{children}
diff --git a/web/components/issue-layouts/layout-selection.tsx b/web/components/issue-layouts/layout-selection.tsx index f5f3d77fd..87960824c 100644 --- a/web/components/issue-layouts/layout-selection.tsx +++ b/web/components/issue-layouts/layout-selection.tsx @@ -52,22 +52,21 @@ export const LayoutSelection = observer(() => { issueFilterStore.handleUserFilter("display_filters", "layout", _layoutKey); }; - console.log("----"); - console.log("my_user_id", issueFilterStore.myUserId); - console.log("workspace_id", issueFilterStore.workspaceId); - console.log("project_id", issueFilterStore.projectId); - console.log("module_id", issueFilterStore.moduleId); - console.log("cycle_id", issueFilterStore.cycleId); - console.log("view_id", issueFilterStore.viewId); + // console.log("----"); + // console.log("my_user_id", issueFilterStore.myUserId); + // console.log("workspace_id", issueFilterStore.workspaceId); + // console.log("project_id", issueFilterStore.projectId); + // console.log("module_id", issueFilterStore.moduleId); + // console.log("cycle_id", issueFilterStore.cycleId); + // console.log("view_id", issueFilterStore.viewId); - console.log("issue_view", issueFilterStore.issueView); - console.log("issue_layout", issueFilterStore.issueLayout); + // console.log("issue_view", issueFilterStore.issueView); + // console.log("issue_layout", issueFilterStore.issueLayout); - console.log("user_filters", issueFilterStore.userFilters); - console.log("issues", issueStore.issues); - console.log("issues", issueStore.getIssues); - - console.log("----"); + // console.log("user_filters", issueFilterStore.userFilters); + // console.log("issues", issueStore.issues); + // console.log("issues", issueStore.getIssues); + // console.log("----"); return (
diff --git a/web/components/issue-layouts/list/index.tsx b/web/components/issue-layouts/list/index.tsx new file mode 100644 index 000000000..826f1b884 --- /dev/null +++ b/web/components/issue-layouts/list/index.tsx @@ -0,0 +1,6 @@ +import React from "react"; + +export const IssueListViewRoot = () => { + console.log(); + return
IssueListViewRoot
; +}; diff --git a/web/components/issue-layouts/root.tsx b/web/components/issue-layouts/root.tsx index 660f398f2..897c47121 100644 --- a/web/components/issue-layouts/root.tsx +++ b/web/components/issue-layouts/root.tsx @@ -1,17 +1,30 @@ import React from "react"; // components -import { IssueKanBanViewRoot } from "./kanban"; import { LayoutSelection } from "./layout-selection"; import { IssueDropdown } from "./helpers/dropdown"; import { FilterSelection } from "./filters"; import { DisplayFiltersSelection } from "./display-filters"; -export const IssuesRoot = () => { - console.log("issue root"); +import { FilterPreview } from "./filters-preview"; + +import { IssueListViewRoot } from "./list"; +import { IssueKanBanViewRoot } from "./kanban"; +import { IssueCalendarViewRoot } from "./calendar"; +import { IssueSpreadsheetViewRoot } from "./spreadsheet"; +import { IssueGanttViewRoot } from "./gantt"; +// mobx +import { observer } from "mobx-react-lite"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +import { RootStore } from "store/root"; + +export const IssuesRoot = observer(() => { + const store: RootStore = useMobxStore(); + const { issueFilters: issueFilterStore } = store; return (
-
+
Filter Header
@@ -27,10 +40,16 @@ export const IssuesRoot = () => {
-
Hello
+
+ +
- + {issueFilterStore?.issueLayout === "list" && } + {issueFilterStore?.issueLayout === "kanban" && } + {issueFilterStore?.issueLayout === "calendar" && } + {issueFilterStore?.issueLayout === "spreadsheet" && } + {issueFilterStore?.issueLayout === "gantt_chart" && }
); -}; +}); diff --git a/web/components/issue-layouts/spreadsheet/index.tsx b/web/components/issue-layouts/spreadsheet/index.tsx new file mode 100644 index 000000000..02313458d --- /dev/null +++ b/web/components/issue-layouts/spreadsheet/index.tsx @@ -0,0 +1,6 @@ +import React from "react"; + +export const IssueSpreadsheetViewRoot = () => { + console.log(); + return
IssueSpreadsheetViewRoot
; +}; diff --git a/web/pages/m-store/[workspace_slug]/my-issues.tsx b/web/pages/m-store/[workspace_slug]/my-issues.tsx index 766381fd2..a5b244446 100644 --- a/web/pages/m-store/[workspace_slug]/my-issues.tsx +++ b/web/pages/m-store/[workspace_slug]/my-issues.tsx @@ -1,6 +1,8 @@ import React from "react"; // next imports import { useRouter } from "next/router"; +// swr +// import useSWR from "swr"; // components import { IssuesRoot } from "components/issue-layouts/root"; // mobx store @@ -14,6 +16,10 @@ const KanBanViewRoot = () => { const store: RootStore = useMobxStore(); const { issueView: issueViewStore } = store; + // useSWR(`REVALIDATE_MY_ISSUES`, async () => { + // if (workspace_slug) await issueViewStore.getMyIssuesAsync(workspace_slug); + // }); + React.useEffect(() => { console.log("request init--->"); const init = async () => await issueViewStore.getMyIssuesAsync(workspace_slug); diff --git a/web/store/issue-views/Issues.ts b/web/store/issue-views/Issues.ts index f3b021f00..1eb29188d 100644 --- a/web/store/issue-views/Issues.ts +++ b/web/store/issue-views/Issues.ts @@ -48,22 +48,29 @@ export interface IIssueViewStore { // computed getIssues: IIssues | null | undefined; // actions - getMyIssuesAsync: (workspaceId: string) => null | Promise; - getProjectIssuesAsync: (workspaceId: string, projectId: string) => null | Promise; + getMyIssuesAsync: (workspaceId: string, fetchFilterToggle: boolean) => null | Promise; + getProjectIssuesAsync: ( + workspaceId: string, + projectId: string, + fetchFilterToggle: boolean + ) => null | Promise; getIssuesForModulesAsync: ( workspaceId: string, projectId: string, - moduleId: string + moduleId: string, + fetchFilterToggle: boolean ) => null | Promise; getIssuesForCyclesAsync: ( workspaceId: string, projectId: string, - cycleId: string + cycleId: string, + fetchFilterToggle: boolean ) => null | Promise; getIssuesForViewsAsync: ( workspaceId: string, projectId: string, - viewId: string + viewId: string, + fetchFilterToggle: boolean ) => null | Promise; } @@ -144,12 +151,13 @@ class IssueViewStore implements IIssueViewStore { } // fetching my issues - getMyIssuesAsync = async (workspaceId: string) => { + getMyIssuesAsync = async (workspaceId: string, fetchFilterToggle: boolean = true) => { try { this.loader = true; this.error = null; - await this.rootStore.issueFilters.getWorkspaceMyIssuesFilters(workspaceId); + if (fetchFilterToggle) + await this.rootStore.issueFilters.getWorkspaceMyIssuesFilters(workspaceId); const filteredParams = this.rootStore.issueFilters.getComputedFilters( workspaceId, null, @@ -190,12 +198,17 @@ class IssueViewStore implements IIssueViewStore { }; // fetching project issues - getProjectIssuesAsync = async (workspaceId: string, projectId: string) => { + getProjectIssuesAsync = async ( + workspaceId: string, + projectId: string, + fetchFilterToggle: boolean = true + ) => { try { this.loader = true; this.error = null; - await this.rootStore.issueFilters.getProjectIssueFilters(workspaceId, projectId); + if (fetchFilterToggle) + await this.rootStore.issueFilters.getProjectIssueFilters(workspaceId, projectId); const filteredParams = this.rootStore.issueFilters.getComputedFilters( workspaceId, projectId, @@ -246,16 +259,22 @@ class IssueViewStore implements IIssueViewStore { }; // fetching project issues for modules - getIssuesForModulesAsync = async (workspaceId: string, projectId: string, moduleId: string) => { + getIssuesForModulesAsync = async ( + workspaceId: string, + projectId: string, + moduleId: string, + fetchFilterToggle: boolean = true + ) => { try { this.loader = true; this.error = null; - await this.rootStore.issueFilters.getProjectIssueModuleFilters( - workspaceId, - projectId, - moduleId - ); + if (fetchFilterToggle) + await this.rootStore.issueFilters.getProjectIssueModuleFilters( + workspaceId, + projectId, + moduleId + ); const filteredParams = this.rootStore.issueFilters.getComputedFilters( workspaceId, projectId, @@ -310,16 +329,22 @@ class IssueViewStore implements IIssueViewStore { }; // fetching project issues for cycles - getIssuesForCyclesAsync = async (workspaceId: string, projectId: string, cycleId: string) => { + getIssuesForCyclesAsync = async ( + workspaceId: string, + projectId: string, + cycleId: string, + fetchFilterToggle: boolean = true + ) => { try { this.loader = true; this.error = null; - await this.rootStore.issueFilters.getProjectIssueCyclesFilters( - workspaceId, - projectId, - cycleId - ); + if (fetchFilterToggle) + await this.rootStore.issueFilters.getProjectIssueCyclesFilters( + workspaceId, + projectId, + cycleId + ); const filteredParams = this.rootStore.issueFilters.getComputedFilters( workspaceId, projectId, @@ -374,12 +399,22 @@ class IssueViewStore implements IIssueViewStore { }; // fetching project issues for views - getIssuesForViewsAsync = async (workspaceId: string, projectId: string, viewId: string) => { + getIssuesForViewsAsync = async ( + workspaceId: string, + projectId: string, + viewId: string, + fetchFilterToggle: boolean = true + ) => { try { this.loader = true; this.error = null; - await this.rootStore.issueFilters.getProjectIssueViewsFilters(workspaceId, projectId, viewId); + if (fetchFilterToggle) + await this.rootStore.issueFilters.getProjectIssueViewsFilters( + workspaceId, + projectId, + viewId + ); const filteredParams = this.rootStore.issueFilters.getComputedFilters( workspaceId, projectId, diff --git a/web/store/issue-views/issue_data.ts b/web/store/issue-views/issue_data.ts index f4de061e7..7c870e5b5 100644 --- a/web/store/issue-views/issue_data.ts +++ b/web/store/issue-views/issue_data.ts @@ -1,3 +1,5 @@ +import { renderDateFormat } from "helpers/date-time.helper"; + export type TStateGroup = "backlog" | "unstarted" | "started" | "completed" | "cancelled"; export const issueStateGroupKeys: TStateGroup[] = [ "backlog", @@ -87,7 +89,10 @@ export const extraProperties: { key: string; title: string }[] = [ export const issueFilterVisibilityData: any = { my_issues: { layout: ["list", "kanban"], - filters: ["priority", "state_group", "labels", "start_date", "due_date"], + filters: { + list: ["priority", "state_group", "labels", "start_date", "due_date"], + kanban: ["priority", "state_group", "labels", "start_date", "due_date"], + }, display_properties: { list: true, kanban: true, @@ -109,7 +114,29 @@ export const issueFilterVisibilityData: any = { }, others: { layout: ["list", "kanban", "calendar", "spreadsheet", "gantt_chart"], - filters: ["priority", "state", "assignees", "created_by", "labels", "start_date", "due_date"], + filters: { + list: ["priority", "state", "assignees", "created_by", "labels", "start_date", "due_date"], + kanban: ["priority", "state", "assignees", "created_by", "labels", "start_date", "due_date"], + calendar: ["priority", "state", "assignees", "created_by", "labels"], + spreadsheet: [ + "priority", + "state", + "assignees", + "created_by", + "labels", + "start_date", + "due_date", + ], + gantt_chart: [ + "priority", + "state", + "assignees", + "created_by", + "labels", + "start_date", + "due_date", + ], + }, display_properties: { list: true, kanban: true, @@ -148,3 +175,31 @@ export const issueFilterVisibilityData: any = { }, }, }; + +export const handleIssueParamsDateFormat = ( + key: string, + start_date: any | null, + target_date: any | null +) => { + if (key === "last_week") + return `${renderDateFormat( + new Date(new Date().getTime() - 7 * 24 * 60 * 60 * 1000) + )};after,${renderDateFormat(new Date())};before`; + + if (key === "2_weeks_from_now") + return `${renderDateFormat(new Date())};after, + ${renderDateFormat(new Date(new Date().getTime() + 14 * 24 * 60 * 60 * 1000))};before`; + + if (key === "1_month_from_now") + return `${renderDateFormat(new Date())};after,${renderDateFormat( + new Date(new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()) + )};before`; + + if (key === "2_months_from_now") + return `${renderDateFormat(new Date())};after,${renderDateFormat( + new Date(new Date().getFullYear(), new Date().getMonth() + 2, new Date().getDate()) + )};before`; + + if (key === "custom" && start_date && target_date) + return `${renderDateFormat(start_date)};after,${renderDateFormat(target_date)};before`; +}; diff --git a/web/store/issue-views/issue_filters.ts b/web/store/issue-views/issue_filters.ts index 5dc063711..dda2f0dac 100644 --- a/web/store/issue-views/issue_filters.ts +++ b/web/store/issue-views/issue_filters.ts @@ -1,4 +1,4 @@ -import { observable, action, computed, makeObservable, runInAction } from "mobx"; +import { observable, action, computed, makeObservable, runInAction, autorun } from "mobx"; // types import { RootStore } from "../root"; // services @@ -24,6 +24,22 @@ import { export type TIssueViews = "my_issues" | "issues" | "modules" | "views" | "cycles"; export type TIssueLayouts = "list" | "kanban" | "calendar" | "spreadsheet" | "gantt_chart"; +export type TIssueParams = + | "priority" + | "state_group" + | "state" + | "assignees" + | "created_by" + | "labels" + | "start_date" + | "target_date" + | "group_by" + | "order_by" + | "type" + | "sub_issue" + | "show_empty_groups" + | "calendar_date_range" + | "start_target_date"; export interface IIssueFilter { priority: string[] | undefined; @@ -649,6 +665,32 @@ class IssueFilterStore implements IIssueFilterStore { ); } } + + if (this.issueView === "my_issues") + this.rootStore?.issueView?.getProjectIssuesAsync(this.workspaceId, this.projectId, false); + if (this.issueView === "issues") + this.rootStore?.issueView?.getProjectIssuesAsync(this.workspaceId, this.projectId, false); + if (this.issueView === "modules" && this.moduleId) + this.rootStore?.issueView?.getIssuesForModulesAsync( + this.workspaceId, + this.projectId, + this.moduleId, + false + ); + if (this.issueView === "cycles" && this.cycleId) + this.rootStore?.issueView?.getIssuesForCyclesAsync( + this.workspaceId, + this.projectId, + this.cycleId, + false + ); + if (this.issueView === "views" && this.viewId) + this.rootStore?.issueView?.getIssuesForViewsAsync( + this.workspaceId, + this.projectId, + this.viewId, + false + ); }; computedFilter = (filters: any, filteredParams: any) => { @@ -699,8 +741,11 @@ class IssueFilterStore implements IIssueFilterStore { }; // start date and target date we have to construct the format here + // in calendar view calendar_date_range send as target_date + // in spreadsheet sub issue is false for sure + // in gantt start_target_date is true for sure - let filteredParams: any = {}; + let filteredParams: TIssueParams[] = []; if (_layout === "list") filteredParams = [ "priority", @@ -727,10 +772,11 @@ class IssueFilterStore implements IIssueFilterStore { "labels", "start_date", "target_date", - "type", "group_by", "order_by", + "type", "sub_issue", + "show_empty_groups", ]; if (_layout === "calendar") filteredParams = [ @@ -756,7 +802,7 @@ class IssueFilterStore implements IIssueFilterStore { "start_date", "target_date", "type", - "sub_issues", + "sub_issue", ]; if (_layout === "gantt_chart") filteredParams = [ @@ -769,14 +815,12 @@ class IssueFilterStore implements IIssueFilterStore { "target_date", "order_by", "type", - "sub_issue_id", + "sub_issue", "start_target_date", ]; filteredRouteParams = this.computedFilter(filteredRouteParams, filteredParams); - // remove few attributes from the object when we are in workspace issues - return filteredRouteParams; };