chore: filters dropdown (#2260)

* chore: project issues topbar

* style: theming and minor UI fixes

* refactor: file structure

* chore: layout wise authorization added

* style: filter dropdowns

* chore: add fetch keys

* feat: search option for filters

* fix: sticky headers

* chore: sub_group_by section added
This commit is contained in:
Aaryan Khandelwal 2023-09-25 19:17:40 +05:30 committed by GitHub
parent 9a8dcc349f
commit 9831418a11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 786 additions and 341 deletions

View File

@ -10,58 +10,65 @@ import {
FilterGroupBy, FilterGroupBy,
FilterIssueType, FilterIssueType,
FilterOrderBy, FilterOrderBy,
FilterSubGroupBy,
} from "components/issue-layouts"; } from "components/issue-layouts";
// helpers // helpers
import { issueFilterVisibilityData } from "helpers/issue.helper"; import { ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "constants/issue";
export const DisplayFiltersSelection = observer(() => { export const DisplayFiltersSelection = observer(() => {
const { issueFilter: issueFilterStore } = useMobxStore(); const { issueFilter: issueFilterStore } = useMobxStore();
const isDisplayFilterEnabled = (displayFilter: string) => const isDisplayFilterEnabled = (displayFilter: string) =>
issueFilterVisibilityData.issues.display_filters[issueFilterStore.userDisplayFilters.layout ?? "list"].includes( ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues.display_filters[
displayFilter issueFilterStore.userDisplayFilters.layout ?? "list"
); ].includes(displayFilter);
return ( return (
<div className="w-full h-full overflow-hidden select-none relative flex flex-col divide-y divide-custom-border-200 px-0.5"> <div className="w-full h-full overflow-hidden overflow-y-auto relative px-2.5 divide-y divide-custom-border-200">
{/* <div className="flex-shrink-0 p-2 text-sm">Search container</div> */} {/* display properties */}
<div className="w-full h-full overflow-hidden overflow-y-auto relative pb-2 divide-y divide-custom-border-200"> {ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues.display_properties[
{/* display properties */} issueFilterStore.userDisplayFilters.layout ?? "list"
{issueFilterVisibilityData.issues.display_properties[issueFilterStore.userDisplayFilters.layout ?? "list"] && ( ] && (
<div className="pb-2 px-2"> <div className="py-2">
<FilterDisplayProperties /> <FilterDisplayProperties />
</div> </div>
)} )}
{/* group by */} {/* group by */}
{isDisplayFilterEnabled("group_by") && ( {isDisplayFilterEnabled("group_by") && (
<div className="py-1 px-2"> <div className="py-2">
<FilterGroupBy /> <FilterGroupBy />
</div> </div>
)} )}
{/* order by */} {/* sub-group by */}
{isDisplayFilterEnabled("order_by") && ( {isDisplayFilterEnabled("sub_group_by") && (
<div className="py-1 px-2"> <div className="py-2">
<FilterOrderBy /> <FilterSubGroupBy />
</div> </div>
)} )}
{/* issue type */} {/* order by */}
{isDisplayFilterEnabled("issue_type") && ( {isDisplayFilterEnabled("order_by") && (
<div className="py-1 px-2"> <div className="py-2">
<FilterIssueType /> <FilterOrderBy />
</div> </div>
)} )}
{/* Options */} {/* issue type */}
{issueFilterVisibilityData.issues.extra_options[issueFilterStore.userDisplayFilters.layout ?? "list"] {isDisplayFilterEnabled("issue_type") && (
.access && ( <div className="py-2">
<div className="pt-1 px-2"> <FilterIssueType />
<FilterExtraOptions /> </div>
</div> )}
)}
</div> {/* Options */}
{ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues.extra_options[issueFilterStore.userDisplayFilters.layout ?? "list"]
.access && (
<div className="py-2">
<FilterExtraOptions />
</div>
)}
</div> </div>
); );
}); });

View File

@ -28,14 +28,14 @@ export const FilterDisplayProperties = observer(() => {
}; };
return ( return (
<div> <>
<FilterHeader <FilterHeader
title="Display Properties" title="Display Properties"
isPreviewEnabled={previewEnabled} isPreviewEnabled={previewEnabled}
handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)}
/> />
{previewEnabled && ( {previewEnabled && (
<div className="flex items-center gap-2 flex-wrap mx-1 mt-1"> <div className="flex items-center gap-2 flex-wrap mt-1">
{ISSUE_DISPLAY_PROPERTIES.map((displayProperty) => ( {ISSUE_DISPLAY_PROPERTIES.map((displayProperty) => (
<button <button
key={displayProperty.key} key={displayProperty.key}
@ -56,6 +56,6 @@ export const FilterDisplayProperties = observer(() => {
))} ))}
</div> </div>
)} )}
</div> </>
); );
}); });

View File

@ -6,10 +6,8 @@ import { useMobxStore } from "lib/mobx/store-provider";
// components // components
import { FilterHeader, FilterOption } from "components/issue-layouts"; import { FilterHeader, FilterOption } from "components/issue-layouts";
// helpers
import { issueFilterVisibilityData } from "helpers/issue.helper";
// constants // constants
import { ISSUE_EXTRA_OPTIONS } from "constants/issue"; import { ISSUE_EXTRA_OPTIONS, ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "constants/issue";
export const FilterExtraOptions = observer(() => { export const FilterExtraOptions = observer(() => {
const [previewEnabled, setPreviewEnabled] = useState(true); const [previewEnabled, setPreviewEnabled] = useState(true);
@ -18,19 +16,19 @@ export const FilterExtraOptions = observer(() => {
const { issueFilter: issueFilterStore } = store; const { issueFilter: issueFilterStore } = store;
const isExtraOptionEnabled = (option: string) => const isExtraOptionEnabled = (option: string) =>
issueFilterVisibilityData.issues.extra_options[ ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues.extra_options[
issueFilterStore.userDisplayFilters.layout ?? "list" issueFilterStore.userDisplayFilters.layout ?? "list"
].values.includes(option); ].values.includes(option);
return ( return (
<div> <>
<FilterHeader <FilterHeader
title="Extra Options" title="Extra Options"
isPreviewEnabled={previewEnabled} isPreviewEnabled={previewEnabled}
handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)}
/> />
{previewEnabled && ( {previewEnabled && (
<div className="space-y-[2px] pt-1"> <div>
{ISSUE_EXTRA_OPTIONS.map((option) => { {ISSUE_EXTRA_OPTIONS.map((option) => {
if (!isExtraOptionEnabled(option.key)) return null; if (!isExtraOptionEnabled(option.key)) return null;
@ -44,6 +42,6 @@ export const FilterExtraOptions = observer(() => {
})} })}
</div> </div>
)} )}
</div> </>
); );
}); });

View File

@ -6,8 +6,7 @@ import { useRouter } from "next/router";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { useMobxStore } from "lib/mobx/store-provider"; import { useMobxStore } from "lib/mobx/store-provider";
// components // components
import { FilterHeader } from "../helpers/filter-header"; import { FilterHeader, FilterOption } from "components/issue-layouts";
import { FilterOption } from "../helpers/filter-option";
// types // types
import { TIssueGroupByOptions } from "types"; import { TIssueGroupByOptions } from "types";
// constants // constants
@ -33,14 +32,14 @@ export const FilterGroupBy = observer(() => {
}; };
return ( return (
<div> <>
<FilterHeader <FilterHeader
title="Group By" title="Group by"
isPreviewEnabled={previewEnabled} isPreviewEnabled={previewEnabled}
handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)}
/> />
{previewEnabled && ( {previewEnabled && (
<div className="space-y-1 pt-1"> <div>
{ISSUE_GROUP_BY_OPTIONS.map((groupBy) => ( {ISSUE_GROUP_BY_OPTIONS.map((groupBy) => (
<FilterOption <FilterOption
key={groupBy?.key} key={groupBy?.key}
@ -52,6 +51,6 @@ export const FilterGroupBy = observer(() => {
))} ))}
</div> </div>
)} )}
</div> </>
); );
}); });

View File

@ -4,3 +4,4 @@ export * from "./extra-options";
export * from "./group-by"; export * from "./group-by";
export * from "./issue-type"; export * from "./issue-type";
export * from "./order-by"; export * from "./order-by";
export * from "./sub-group-by";

View File

@ -6,8 +6,7 @@ import { useRouter } from "next/router";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { useMobxStore } from "lib/mobx/store-provider"; import { useMobxStore } from "lib/mobx/store-provider";
// components // components
import { FilterHeader } from "../helpers/filter-header"; import { FilterHeader, FilterOption } from "components/issue-layouts";
import { FilterOption } from "../helpers/filter-option";
// types // types
import { TIssueTypeFilters } from "types"; import { TIssueTypeFilters } from "types";
// constants // constants
@ -33,14 +32,14 @@ export const FilterIssueType = observer(() => {
}; };
return ( return (
<div> <>
<FilterHeader <FilterHeader
title="Issue Type" title="Issue Type"
isPreviewEnabled={previewEnabled} isPreviewEnabled={previewEnabled}
handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)}
/> />
{previewEnabled && ( {previewEnabled && (
<div className="space-y-1 pt-1"> <div>
{ISSUE_FILTER_OPTIONS.map((issueType) => ( {ISSUE_FILTER_OPTIONS.map((issueType) => (
<FilterOption <FilterOption
key={issueType?.key} key={issueType?.key}
@ -52,6 +51,6 @@ export const FilterIssueType = observer(() => {
))} ))}
</div> </div>
)} )}
</div> </>
); );
}); });

View File

@ -6,8 +6,7 @@ import { useRouter } from "next/router";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { useMobxStore } from "lib/mobx/store-provider"; import { useMobxStore } from "lib/mobx/store-provider";
// components // components
import { FilterHeader } from "../helpers/filter-header"; import { FilterHeader, FilterOption } from "components/issue-layouts";
import { FilterOption } from "../helpers/filter-option";
// types // types
import { TIssueOrderByOptions } from "types"; import { TIssueOrderByOptions } from "types";
// constants // constants
@ -33,14 +32,14 @@ export const FilterOrderBy = observer(() => {
}; };
return ( return (
<div> <>
<FilterHeader <FilterHeader
title={"Order By"} title="Order by"
isPreviewEnabled={previewEnabled} isPreviewEnabled={previewEnabled}
handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)}
/> />
{previewEnabled && ( {previewEnabled && (
<div className="space-y-[2px] pt-1"> <div>
{ISSUE_ORDER_BY_OPTIONS.map((orderBy) => ( {ISSUE_ORDER_BY_OPTIONS.map((orderBy) => (
<FilterOption <FilterOption
key={orderBy?.key} key={orderBy?.key}
@ -52,6 +51,6 @@ export const FilterOrderBy = observer(() => {
))} ))}
</div> </div>
)} )}
</div> </>
); );
}); });

View File

@ -0,0 +1,64 @@
import React, { useState } from "react";
import { useRouter } from "next/router";
// mobx
import { observer } from "mobx-react-lite";
import { useMobxStore } from "lib/mobx/store-provider";
// components
import { FilterHeader, FilterOption } from "components/issue-layouts";
// types
import { TIssueGroupByOptions } from "types";
// constants
import { ISSUE_GROUP_BY_OPTIONS } from "constants/issue";
export const FilterSubGroupBy = observer(() => {
const [previewEnabled, setPreviewEnabled] = useState(true);
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const store = useMobxStore();
const { issueFilter: issueFilterStore } = store;
const handleSubGroupBy = (value: TIssueGroupByOptions) => {
if (!workspaceSlug || !projectId) return;
issueFilterStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), {
display_filters: {
sub_group_by: value,
},
});
};
return (
<>
<FilterHeader
title="Sub-group by"
isPreviewEnabled={previewEnabled}
handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)}
/>
{previewEnabled && (
<div>
{ISSUE_GROUP_BY_OPTIONS.map((subGroupBy) => {
if (
issueFilterStore.userDisplayFilters.group_by !== null &&
issueFilterStore.userDisplayFilters.sub_group_by === issueFilterStore.userDisplayFilters.group_by
)
return null;
return (
<FilterOption
key={subGroupBy?.key}
isChecked={issueFilterStore?.userDisplayFilters?.sub_group_by === subGroupBy?.key ? true : false}
onClick={() => handleSubGroupBy(subGroupBy.key)}
title={subGroupBy.title}
multiple={false}
/>
);
})}
</div>
)}
</>
);
});

View File

@ -6,15 +6,12 @@ import { useMobxStore } from "lib/mobx/store-provider";
// components // components
import { FilterHeader, FilterOption } from "components/issue-layouts"; import { FilterHeader, FilterOption } from "components/issue-layouts";
// ui // ui
import { Avatar } from "components/ui"; import { Avatar, Loader } from "components/ui";
type Props = { type Props = { workspaceSlug: string; projectId: string; itemsToRender: number };
workspaceSlug: string;
projectId: string;
};
export const FilterAssignees: React.FC<Props> = observer((props) => { export const FilterAssignees: React.FC<Props> = observer((props) => {
const { workspaceSlug, projectId } = props; const { workspaceSlug, projectId, itemsToRender } = props;
const [previewEnabled, setPreviewEnabled] = useState(true); const [previewEnabled, setPreviewEnabled] = useState(true);
@ -34,31 +31,51 @@ export const FilterAssignees: React.FC<Props> = observer((props) => {
}); });
}; };
const appliedFiltersCount = issueFilterStore.userFilters?.assignees?.length ?? 0;
const filteredOptions = projectStore.members?.[projectId?.toString() ?? ""]?.filter((member) =>
member.member.display_name.toLowerCase().includes(issueFilterStore.filtersSearchQuery.toLowerCase())
);
return ( return (
<div> <>
<FilterHeader <FilterHeader
title={`Assignees (${issueFilterStore?.userFilters.assignees?.length ?? 0})`} title={`Assignee${appliedFiltersCount > 0 ? ` (${appliedFiltersCount})` : ""}`}
isPreviewEnabled={previewEnabled} isPreviewEnabled={previewEnabled}
handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)}
/> />
{previewEnabled && ( {previewEnabled && (
<div className="space-y-[2px] pt-1"> <div>
{projectStore.members?.[projectId?.toString() ?? ""]?.map((member) => ( {filteredOptions ? (
<FilterOption filteredOptions.length > 0 ? (
key={`assignees-${member?.member?.id}`} filteredOptions
isChecked={ .slice(0, itemsToRender)
issueFilterStore?.userFilters?.assignees != null && .map((member) => (
issueFilterStore?.userFilters?.assignees.includes(member.member?.id) <FilterOption
? true key={`assignees-${member?.member?.id}`}
: false isChecked={
} issueFilterStore?.userFilters?.assignees != null &&
onClick={() => handleUpdateAssignees(member.member?.id)} issueFilterStore?.userFilters?.assignees.includes(member.member?.id)
icon={<Avatar user={member.member} height="18px" width="18px" />} ? true
title={member.member?.display_name} : false
/> }
))} onClick={() => handleUpdateAssignees(member.member?.id)}
icon={<Avatar user={member.member} height="18px" width="18px" />}
title={member.member?.display_name}
/>
))
) : (
<p className="text-xs text-custom-text-400 italic">No matches found</p>
)
) : (
<Loader className="space-y-2">
<Loader.Item height="20px" />
<Loader.Item height="20px" />
<Loader.Item height="20px" />
</Loader>
)}
</div> </div>
)} )}
</div> </>
); );
}); });

View File

@ -6,15 +6,12 @@ import { useMobxStore } from "lib/mobx/store-provider";
// components // components
import { FilterHeader, FilterOption } from "components/issue-layouts"; import { FilterHeader, FilterOption } from "components/issue-layouts";
// ui // ui
import { Avatar } from "components/ui"; import { Avatar, Loader } from "components/ui";
type Props = { type Props = { workspaceSlug: string; projectId: string; itemsToRender: number };
workspaceSlug: string;
projectId: string;
};
export const FilterCreatedBy: React.FC<Props> = observer((props) => { export const FilterCreatedBy: React.FC<Props> = observer((props) => {
const { workspaceSlug, projectId } = props; const { workspaceSlug, projectId, itemsToRender } = props;
const [previewEnabled, setPreviewEnabled] = useState(true); const [previewEnabled, setPreviewEnabled] = useState(true);
@ -34,26 +31,46 @@ export const FilterCreatedBy: React.FC<Props> = observer((props) => {
}); });
}; };
const appliedFiltersCount = issueFilterStore.userFilters?.created_by?.length ?? 0;
const filteredOptions = projectStore.members?.[projectId?.toString() ?? ""]?.filter((member) =>
member.member.display_name.toLowerCase().includes(issueFilterStore.filtersSearchQuery.toLowerCase())
);
return ( return (
<div> <>
<FilterHeader <FilterHeader
title={`Created By (${issueFilterStore?.userFilters.created_by?.length ?? 0})`} title={`Created by${appliedFiltersCount > 0 ? ` (${appliedFiltersCount})` : ""}`}
isPreviewEnabled={previewEnabled} isPreviewEnabled={previewEnabled}
handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)}
/> />
{previewEnabled && ( {previewEnabled && (
<div className="space-y-1 pt-1"> <div>
{projectStore.members?.[projectId?.toString() ?? ""]?.map((member) => ( {filteredOptions ? (
<FilterOption filteredOptions.length > 0 ? (
key={`created-by-${member.member?.id}`} filteredOptions
isChecked={issueFilterStore?.userFilters?.created_by?.includes(member.member?.id) ? true : false} .slice(0, itemsToRender)
onClick={() => handleUpdateCreatedBy(member.member?.id)} .map((member) => (
icon={<Avatar user={member.member} height="18px" width="18px" />} <FilterOption
title={member.member?.display_name} key={`created-by-${member.member?.id}`}
/> isChecked={issueFilterStore?.userFilters?.created_by?.includes(member.member?.id) ? true : false}
))} onClick={() => handleUpdateCreatedBy(member.member?.id)}
icon={<Avatar user={member.member} height="18px" width="18px" />}
title={member.member?.display_name}
/>
))
) : (
<p className="text-xs text-custom-text-400 italic">No matches found</p>
)
) : (
<Loader className="space-y-2">
<Loader.Item height="20px" />
<Loader.Item height="20px" />
<Loader.Item height="20px" />
</Loader>
)}
</div> </div>
)} )}
</div> </>
); );
}); });

View File

@ -0,0 +1,230 @@
import React, { useState } from "react";
// mobx
import { observer } from "mobx-react-lite";
import { useMobxStore } from "lib/mobx/store-provider";
// components
import {
FilterAssignees,
FilterCreatedBy,
FilterLabels,
FilterPriority,
FilterState,
FilterStateGroup,
} from "components/issue-layouts";
// icons
import { Search, X } from "lucide-react";
// helpers
import { getStatesList } from "helpers/state.helper";
// types
import { IIssueFilterOptions } from "types";
// constants
import { ISSUE_PRIORITIES, ISSUE_STATE_GROUPS } from "constants/issue";
type Props = {
workspaceSlug: string;
projectId: string;
};
export const FilterSelection: React.FC<Props> = observer((props) => {
const { workspaceSlug, projectId } = props;
const { issueFilter: issueFilterStore, project: projectStore } = useMobxStore();
const statesList = getStatesList(projectStore.states?.[projectId?.toString() ?? ""]);
const [filtersToRender, setFiltersToRender] = useState<{
[key in keyof IIssueFilterOptions]: {
currentLength: number;
totalLength: number;
};
}>({
assignees: {
currentLength: 5,
totalLength: projectStore.members?.[projectId]?.length ?? 0,
},
created_by: {
currentLength: 5,
totalLength: projectStore.members?.[projectId]?.length ?? 0,
},
labels: {
currentLength: 5,
totalLength: projectStore.labels?.[projectId]?.length ?? 0,
},
priority: {
currentLength: 5,
totalLength: ISSUE_PRIORITIES.length,
},
state_group: {
currentLength: 5,
totalLength: ISSUE_STATE_GROUPS.length,
},
state: {
currentLength: 5,
totalLength: statesList?.length ?? 0,
},
});
const handleViewMore = (filterName: keyof IIssueFilterOptions) => {
const filterDetails = filtersToRender[filterName];
if (!filterDetails) return;
if (filterDetails.currentLength <= filterDetails.totalLength)
setFiltersToRender((prev) => ({
...prev,
[filterName]: {
...prev[filterName],
currentLength: filterDetails.currentLength + 5,
},
}));
};
const isViewMoreVisible = (filterName: keyof IIssueFilterOptions): boolean => {
const filterDetails = filtersToRender[filterName];
if (!filterDetails) return false;
return filterDetails.currentLength < filterDetails.totalLength;
};
return (
<div className="w-full h-full flex flex-col overflow-hidden">
<div className="p-2.5 bg-custom-background-100 sticky top-0 z-[1]">
<div className="bg-custom-background-90 border-[0.5px] border-custom-border-200 text-xs rounded flex items-center gap-1.5 px-1.5 py-1">
<Search className="text-custom-text-400" size={12} strokeWidth={2} />
<input
type="text"
className="bg-custom-background-90 placeholder:text-custom-text-400 w-full outline-none"
placeholder="Search"
value={issueFilterStore.filtersSearchQuery}
onChange={(e) => issueFilterStore.updateFiltersSearchQuery(e.target.value)}
autoFocus
/>
{issueFilterStore.filtersSearchQuery !== "" && (
<button
type="button"
className="grid place-items-center"
onClick={() => issueFilterStore.updateFiltersSearchQuery("")}
>
<X className="text-custom-text-300" size={12} strokeWidth={2} />
</button>
)}
</div>
</div>
<div className="w-full h-full divide-y divide-custom-border-20 px-2.5 overflow-y-auto">
{/* priority */}
<div className="py-2">
<FilterPriority
workspaceSlug={workspaceSlug}
projectId={projectId}
itemsToRender={filtersToRender.priority?.currentLength ?? 0}
/>
{isViewMoreVisible("priority") && (
<button
className="text-custom-primary-100 text-xs font-medium ml-7"
onClick={() => handleViewMore("priority")}
>
View more
</button>
)}
</div>
{/* state group */}
<div className="py-2">
<FilterStateGroup
workspaceSlug={workspaceSlug}
projectId={projectId}
itemsToRender={filtersToRender.state_group?.currentLength ?? 0}
/>
{isViewMoreVisible("state_group") && (
<button
className="text-custom-primary-100 text-xs font-medium ml-7"
onClick={() => handleViewMore("state_group")}
>
View more
</button>
)}
</div>
{/* state */}
<div className="py-2">
<FilterState
workspaceSlug={workspaceSlug}
projectId={projectId}
itemsToRender={filtersToRender.state?.currentLength ?? 0}
/>
{isViewMoreVisible("state") && (
<button
className="text-custom-primary-100 text-xs font-medium ml-7"
onClick={() => handleViewMore("state")}
>
View more
</button>
)}
</div>
{/* assignees */}
<div className="py-2">
<FilterAssignees
workspaceSlug={workspaceSlug}
projectId={projectId}
itemsToRender={filtersToRender.assignees?.currentLength ?? 0}
/>
{isViewMoreVisible("assignees") && (
<button
className="text-custom-primary-100 text-xs font-medium ml-7"
onClick={() => handleViewMore("assignees")}
>
View more
</button>
)}
</div>
{/* created_by */}
<div className="py-2">
<FilterCreatedBy
workspaceSlug={workspaceSlug}
projectId={projectId}
itemsToRender={filtersToRender.created_by?.currentLength ?? 0}
/>
{isViewMoreVisible("created_by") && (
<button
className="text-custom-primary-100 text-xs font-medium ml-7"
onClick={() => handleViewMore("created_by")}
>
View more
</button>
)}
</div>
{/* labels */}
<div className="py-2">
<FilterLabels
workspaceSlug={workspaceSlug}
projectId={projectId}
itemsToRender={filtersToRender.labels?.currentLength ?? 0}
/>
{isViewMoreVisible("labels") && (
<button
className="text-custom-primary-100 text-xs font-medium ml-7"
onClick={() => handleViewMore("labels")}
>
View more
</button>
)}
</div>
{/* start_date */}
{/* <div>
<FilterStartDate />
</div> */}
{/* due_date */}
{/* <div>
<FilterTargetDate />
</div> */}
</div>
</div>
);
});

View File

@ -1,6 +1,6 @@
export * from "./assignees"; export * from "./assignees";
export * from "./created-by"; export * from "./created-by";
export * from "./filter-selection"; export * from "./filters-selection";
export * from "./labels"; export * from "./labels";
export * from "./priority"; export * from "./priority";
export * from "./start-date"; export * from "./start-date";

View File

@ -1,23 +1,21 @@
import React, { useState } from "react"; import React, { useState } from "react";
// mobx
import { observer } from "mobx-react-lite";
import { useMobxStore } from "lib/mobx/store-provider";
// components // components
import { FilterHeader, FilterOption } from "components/issue-layouts"; import { FilterHeader, FilterOption } from "components/issue-layouts";
// mobx react lite // ui
import { observer } from "mobx-react-lite"; import { Loader } from "components/ui";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
const LabelIcons = ({ color }: { color: string }) => ( const LabelIcons = ({ color }: { color: string }) => (
<span className="w-2.5 h-2.5 rounded-full" style={{ backgroundColor: color }} /> <span className="w-2.5 h-2.5 rounded-full" style={{ backgroundColor: color }} />
); );
type Props = { type Props = { workspaceSlug: string; projectId: string; itemsToRender: number };
workspaceSlug: string;
projectId: string;
};
export const FilterLabels: React.FC<Props> = observer((props) => { export const FilterLabels: React.FC<Props> = observer((props) => {
const { workspaceSlug, projectId } = props; const { workspaceSlug, projectId, itemsToRender } = props;
const [previewEnabled, setPreviewEnabled] = useState(true); const [previewEnabled, setPreviewEnabled] = useState(true);
@ -37,26 +35,46 @@ export const FilterLabels: React.FC<Props> = observer((props) => {
}); });
}; };
const appliedFiltersCount = issueFilterStore.userFilters?.labels?.length ?? 0;
const filteredOptions = projectStore.labels?.[projectId?.toString() ?? ""]?.filter((label) =>
label.name.toLowerCase().includes(issueFilterStore.filtersSearchQuery.toLowerCase())
);
return ( return (
<div> <>
<FilterHeader <FilterHeader
title={`Labels (${issueFilterStore.userFilters?.labels?.length ?? 0})`} title={`Label${appliedFiltersCount > 0 ? ` (${appliedFiltersCount})` : ""}`}
isPreviewEnabled={previewEnabled} isPreviewEnabled={previewEnabled}
handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)}
/> />
{previewEnabled && ( {previewEnabled && (
<div className="space-y-[2px] pt-1"> <div>
{projectStore.labels?.[projectId?.toString() ?? ""]?.map((label) => ( {filteredOptions ? (
<FilterOption filteredOptions.length > 0 ? (
key={label?.id} filteredOptions
isChecked={issueFilterStore?.userFilters?.labels?.includes(label?.id) ? true : false} .slice(0, itemsToRender)
onClick={() => handleUpdateLabels(label?.id)} .map((label) => (
icon={<LabelIcons color={label.color} />} <FilterOption
title={label.name} key={label?.id}
/> isChecked={issueFilterStore?.userFilters?.labels?.includes(label?.id) ? true : false}
))} onClick={() => handleUpdateLabels(label?.id)}
icon={<LabelIcons color={label.color} />}
title={label.name}
/>
))
) : (
<p className="text-xs text-custom-text-400 italic">No matches found</p>
)
) : (
<Loader className="space-y-2">
<Loader.Item height="20px" />
<Loader.Item height="20px" />
<Loader.Item height="20px" />
</Loader>
)}
</div> </div>
)} )}
</div> </>
); );
}); });

View File

@ -50,10 +50,10 @@ const PriorityIcons = ({
); );
}; };
type Props = { workspaceSlug: string; projectId: string }; type Props = { workspaceSlug: string; projectId: string; itemsToRender: number };
export const FilterPriority: React.FC<Props> = observer((props) => { export const FilterPriority: React.FC<Props> = observer((props) => {
const { workspaceSlug, projectId } = props; const { workspaceSlug, projectId, itemsToRender } = props;
const [previewEnabled, setPreviewEnabled] = useState(true); const [previewEnabled, setPreviewEnabled] = useState(true);
@ -73,24 +73,36 @@ export const FilterPriority: React.FC<Props> = observer((props) => {
}); });
}; };
const appliedFiltersCount = issueFilterStore.userFilters?.priority?.length ?? 0;
const filteredOptions = ISSUE_PRIORITIES.filter((p) =>
p.key.includes(issueFilterStore.filtersSearchQuery.toLowerCase())
);
return ( return (
<> <>
<FilterHeader <FilterHeader
title={`Priority (${issueFilterStore.userFilters?.priority?.length ?? 0})`} title={`Priority${appliedFiltersCount > 0 ? ` (${appliedFiltersCount})` : ""}`}
isPreviewEnabled={previewEnabled} isPreviewEnabled={previewEnabled}
handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)}
/> />
{previewEnabled && ( {previewEnabled && (
<div className="space-y-1 pt-1"> <div>
{ISSUE_PRIORITIES.map((priority) => ( {filteredOptions.length > 0 ? (
<FilterOption filteredOptions
key={priority.key} .slice(0, itemsToRender)
isChecked={issueFilterStore.userFilters?.priority?.includes(priority.key) ? true : false} .map((priority) => (
onClick={() => handleUpdatePriority(priority.key)} <FilterOption
icon={<PriorityIcons priority={priority.key} />} key={priority.key}
title={priority.title} isChecked={issueFilterStore.userFilters?.priority?.includes(priority.key) ? true : false}
/> onClick={() => handleUpdatePriority(priority.key)}
))} icon={<PriorityIcons priority={priority.key} />}
title={priority.title}
/>
))
) : (
<p className="text-xs text-custom-text-400 italic">No matches found</p>
)}
</div> </div>
)} )}
</> </>

View File

@ -1,4 +1,4 @@
import React from "react"; import React, { useState } from "react";
// mobx // mobx
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
@ -7,20 +7,22 @@ import { useMobxStore } from "lib/mobx/store-provider";
import { FilterHeader, FilterOption } from "components/issue-layouts"; import { FilterHeader, FilterOption } from "components/issue-layouts";
export const FilterStartDate = observer(() => { export const FilterStartDate = observer(() => {
const [previewEnabled, setPreviewEnabled] = useState(true);
const store = useMobxStore(); const store = useMobxStore();
const { issueFilter: issueFilterStore } = store; const { issueFilter: issueFilterStore } = store;
const [previewEnabled, setPreviewEnabled] = React.useState(true); const appliedFiltersCount = issueFilterStore.userFilters?.start_date?.length ?? 0;
return ( return (
<div> <>
<FilterHeader <FilterHeader
title={`Start Date (${issueFilterStore?.userFilters?.start_date?.length})`} title={`Start date${appliedFiltersCount > 0 ? ` (${appliedFiltersCount})` : ""}`}
isPreviewEnabled={previewEnabled} isPreviewEnabled={previewEnabled}
handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)}
/> />
{previewEnabled && ( {previewEnabled && (
<div className="space-y-1 pt-1"> <div>
{issueFilterStore?.userFilters?.start_date && {issueFilterStore?.userFilters?.start_date &&
issueFilterStore?.userFilters?.start_date.length > 0 && issueFilterStore?.userFilters?.start_date.length > 0 &&
issueFilterStore?.userFilters?.start_date.map((_startDate) => ( issueFilterStore?.userFilters?.start_date.map((_startDate) => (
@ -33,6 +35,6 @@ export const FilterStartDate = observer(() => {
))} ))}
</div> </div>
)} )}
</div> </>
); );
}); });

View File

@ -10,13 +10,10 @@ import { StateGroupIcon } from "components/icons";
// constants // constants
import { ISSUE_STATE_GROUPS } from "constants/issue"; import { ISSUE_STATE_GROUPS } from "constants/issue";
type Props = { type Props = { workspaceSlug: string; projectId: string; itemsToRender: number };
workspaceSlug: string;
projectId: string;
};
export const FilterStateGroup: React.FC<Props> = observer((props) => { export const FilterStateGroup: React.FC<Props> = observer((props) => {
const { workspaceSlug, projectId } = props; const { workspaceSlug, projectId, itemsToRender } = props;
const [previewEnabled, setPreviewEnabled] = useState(true); const [previewEnabled, setPreviewEnabled] = useState(true);
@ -36,26 +33,38 @@ export const FilterStateGroup: React.FC<Props> = observer((props) => {
}); });
}; };
const appliedFiltersCount = issueFilterStore.userFilters?.state_group?.length ?? 0;
const filteredOptions = ISSUE_STATE_GROUPS.filter((s) =>
s.key.includes(issueFilterStore.filtersSearchQuery.toLowerCase())
);
return ( return (
<div> <>
<FilterHeader <FilterHeader
title={`State Group (${issueFilterStore.userFilters?.state_group?.length ?? 0})`} title={`State group${appliedFiltersCount > 0 ? ` (${appliedFiltersCount})` : ""}`}
isPreviewEnabled={previewEnabled} isPreviewEnabled={previewEnabled}
handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)}
/> />
{previewEnabled && ( {previewEnabled && (
<div className="space-y-1 pt-1"> <div>
{ISSUE_STATE_GROUPS.map((stateGroup) => ( {filteredOptions.length > 0 ? (
<FilterOption filteredOptions
key={stateGroup.key} .slice(0, itemsToRender)
isChecked={issueFilterStore.userFilters?.state_group?.includes(stateGroup.key) ? true : false} .map((stateGroup) => (
onClick={() => handleUpdateStateGroup(stateGroup.key)} <FilterOption
icon={<StateGroupIcon stateGroup={stateGroup.key} />} key={stateGroup.key}
title={stateGroup.title} isChecked={issueFilterStore.userFilters?.state_group?.includes(stateGroup.key) ? true : false}
/> onClick={() => handleUpdateStateGroup(stateGroup.key)}
))} icon={<StateGroupIcon stateGroup={stateGroup.key} />}
title={stateGroup.title}
/>
))
) : (
<p className="text-xs text-custom-text-400 italic">No matches found</p>
)}
</div> </div>
)} )}
</div> </>
); );
}); });

View File

@ -1,28 +1,27 @@
import React from "react"; import React, { useState } from "react";
// mobx // mobx
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { useMobxStore } from "lib/mobx/store-provider"; import { useMobxStore } from "lib/mobx/store-provider";
// components // components
import { FilterHeader, FilterOption } from "components/issue-layouts"; import { FilterHeader, FilterOption } from "components/issue-layouts";
// ui
import { Loader } from "components/ui";
// icons // icons
import { StateGroupIcon } from "components/icons"; import { StateGroupIcon } from "components/icons";
// helpers // helpers
import { getStatesList } from "helpers/state.helper"; import { getStatesList } from "helpers/state.helper";
type Props = { type Props = { workspaceSlug: string; projectId: string; itemsToRender: number };
workspaceSlug: string;
projectId: string;
};
export const FilterState: React.FC<Props> = observer((props) => { export const FilterState: React.FC<Props> = observer((props) => {
const { workspaceSlug, projectId } = props; const { workspaceSlug, projectId, itemsToRender } = props;
const [previewEnabled, setPreviewEnabled] = useState(true);
const store = useMobxStore(); const store = useMobxStore();
const { issueFilter: issueFilterStore, project: projectStore } = store; const { issueFilter: issueFilterStore, project: projectStore } = store;
const [previewEnabled, setPreviewEnabled] = React.useState(true);
const statesByGroups = projectStore.states?.[projectId?.toString() ?? ""]; const statesByGroups = projectStore.states?.[projectId?.toString() ?? ""];
const statesList = getStatesList(statesByGroups); const statesList = getStatesList(statesByGroups);
@ -39,26 +38,46 @@ export const FilterState: React.FC<Props> = observer((props) => {
}); });
}; };
const appliedFiltersCount = issueFilterStore.userFilters?.state?.length ?? 0;
const filteredOptions = statesList?.filter((s) =>
s.name.toLowerCase().includes(issueFilterStore.filtersSearchQuery.toLowerCase())
);
return ( return (
<div> <>
<FilterHeader <FilterHeader
title={`State (${issueFilterStore.userFilters?.state?.length ?? 0})`} title={`State${appliedFiltersCount > 0 ? ` (${appliedFiltersCount})` : ""}`}
isPreviewEnabled={previewEnabled} isPreviewEnabled={previewEnabled}
handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)}
/> />
{previewEnabled && ( {previewEnabled && (
<div className="space-y-1 pt-1"> <div>
{statesList?.map((state) => ( {filteredOptions ? (
<FilterOption filteredOptions.length > 0 ? (
key={state.id} <>
isChecked={issueFilterStore?.userFilters?.state?.includes(state?.id) ? true : false} {filteredOptions.slice(0, itemsToRender).map((state) => (
onClick={() => handleUpdateState(state?.id)} <FilterOption
icon={<StateGroupIcon stateGroup={state?.group} color={state?.color} />} key={state.id}
title={state?.name} isChecked={issueFilterStore?.userFilters?.state?.includes(state?.id) ? true : false}
/> onClick={() => handleUpdateState(state?.id)}
))} icon={<StateGroupIcon stateGroup={state?.group} color={state?.color} />}
title={state?.name}
/>
))}
</>
) : (
<p className="text-xs text-custom-text-400 italic">No matches found</p>
)
) : (
<Loader className="space-y-2">
<Loader.Item height="20px" />
<Loader.Item height="20px" />
<Loader.Item height="20px" />
</Loader>
)}
</div> </div>
)} )}
</div> </>
); );
}); });

View File

@ -1,4 +1,4 @@
import React from "react"; import React, { useState } from "react";
// mobx // mobx
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
@ -7,15 +7,17 @@ import { useMobxStore } from "lib/mobx/store-provider";
import { FilterHeader, FilterOption } from "components/issue-layouts"; import { FilterHeader, FilterOption } from "components/issue-layouts";
export const FilterTargetDate = observer(() => { export const FilterTargetDate = observer(() => {
const [previewEnabled, setPreviewEnabled] = useState(true);
const store = useMobxStore(); const store = useMobxStore();
const { issueFilter: issueFilterStore } = store; const { issueFilter: issueFilterStore } = store;
const [previewEnabled, setPreviewEnabled] = React.useState(true); const appliedFiltersCount = issueFilterStore.userFilters?.target_date?.length ?? 0;
return ( return (
<div> <>
<FilterHeader <FilterHeader
title={`Target Date (${issueFilterStore?.userFilters?.target_date?.length})`} title={`Target date${appliedFiltersCount > 0 ? ` (${appliedFiltersCount})` : ""}`}
isPreviewEnabled={previewEnabled} isPreviewEnabled={previewEnabled}
handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)}
/> />
@ -33,6 +35,6 @@ export const FilterTargetDate = observer(() => {
))} ))}
</div> </div>
)} )}
</div> </>
); );
}); });

View File

@ -36,8 +36,8 @@ export const IssueDropdown = ({ children, title = "Dropdown" }: IIssueDropdown)
leaveFrom="opacity-100 translate-y-0" leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1" leaveTo="opacity-0 translate-y-1"
> >
<Popover.Panel className="absolute right-0 z-10 mt-1 w-[18.75rem] h-auto max-h-[37.5rem] bg-custom-background-100 border border-custom-border-200 shadow-custom-shadow-rg rounded overflow-y-auto"> <Popover.Panel className="absolute right-0 z-10 mt-1 bg-custom-background-100 border border-custom-border-200 shadow-custom-shadow-rg rounded overflow-hidden">
{children} <div className="w-[18.75rem] max-h-[37.5rem] flex flex-col overflow-hidden">{children}</div>
</Popover.Panel> </Popover.Panel>
</Transition> </Transition>
</> </>

View File

@ -9,7 +9,7 @@ interface IFilterHeader {
} }
export const FilterHeader = ({ title, isPreviewEnabled, handleIsPreviewEnabled }: IFilterHeader) => ( export const FilterHeader = ({ title, isPreviewEnabled, handleIsPreviewEnabled }: IFilterHeader) => (
<div className="flex items-center justify-between gap-2 p-1.5 pb-1 bg-custom-background-100 sticky top-0"> <div className="flex items-center justify-between gap-2 pb-1 bg-custom-background-100 sticky top-0">
<div className="text-custom-text-300 text-xs font-medium flex-grow truncate">{title}</div> <div className="text-custom-text-300 text-xs font-medium flex-grow truncate">{title}</div>
<button <button
type="button" type="button"

View File

@ -16,7 +16,7 @@ export const FilterOption: React.FC<Props> = (props) => {
return ( return (
<button <button
type="button" type="button"
className="flex items-center gap-2 rounded p-1.5 hover:bg-custom-background-80 w-full" className="flex items-center gap-2 rounded hover:bg-custom-background-80 w-full p-1.5"
onClick={onClick} onClick={onClick}
> >
<div <div

View File

@ -1,5 +1,7 @@
import React from "react"; import React from "react";
// ui
import { Tooltip } from "components/ui";
// types // types
import { TIssueLayouts } from "types"; import { TIssueLayouts } from "types";
// constants // constants
@ -17,20 +19,22 @@ export const LayoutSelection: React.FC<Props> = (props) => {
return ( return (
<div className="flex items-center gap-1 p-1 rounded bg-custom-background-80"> <div className="flex items-center gap-1 p-1 rounded bg-custom-background-80">
{ISSUE_LAYOUTS.filter((l) => layouts.includes(l.key)).map((layout) => ( {ISSUE_LAYOUTS.filter((l) => layouts.includes(l.key)).map((layout) => (
<button <Tooltip tooltipContent={layout.title}>
key={layout.key} <button
type="button" key={layout.key}
className={`w-7 h-[22px] rounded grid place-items-center transition-all hover:bg-custom-background-100 overflow-hidden group ${ type="button"
selectedLayout == layout.key ? "bg-custom-background-100 shadow-custom-shadow-2xs" : "" className={`w-7 h-[22px] rounded grid place-items-center transition-all hover:bg-custom-background-100 overflow-hidden group ${
}`} selectedLayout == layout.key ? "bg-custom-background-100 shadow-custom-shadow-2xs" : ""
onClick={() => onChange(layout.key)} }`}
> onClick={() => onChange(layout.key)}
<layout.icon >
size={14} <layout.icon
strokeWidth={2} size={14}
className={`${selectedLayout == layout.key ? "text-custom-text-100" : "text-custom-text-200"}`} strokeWidth={2}
/> className={`${selectedLayout == layout.key ? "text-custom-text-100" : "text-custom-text-200"}`}
</button> />
</button>
</Tooltip>
))} ))}
</div> </div>
); );

View File

@ -2,7 +2,7 @@ import React from "react";
// components // components
import { LayoutSelection } from "./layout-selection"; import { LayoutSelection } from "./layout-selection";
import { IssueDropdown } from "./helpers/dropdown"; import { IssueDropdown } from "./helpers/dropdown";
import { FilterSelection } from "./filters/filter-selection"; import { FilterSelection } from "./filters/filters-selection";
import { DisplayFiltersSelection } from "./display-filters"; import { DisplayFiltersSelection } from "./display-filters";
import { FilterPreview } from "./filters-preview"; import { FilterPreview } from "./filters-preview";
@ -64,13 +64,13 @@ export const IssuesRoot = observer(() => {
<div>Filter Header</div> <div>Filter Header</div>
</div> </div>
<div className="relative flex items-center gap-2"> <div className="relative flex items-center gap-2">
<IssueDropdown title={"Filters"}> {/* <IssueDropdown title={"Filters"}>
<FilterSelection /> <FilterSelection />
</IssueDropdown> </IssueDropdown>
<IssueDropdown title={"View"}> <IssueDropdown title={"View"}>
<DisplayFiltersSelection /> <DisplayFiltersSelection />
</IssueDropdown> </IssueDropdown>
<LayoutSelection /> <LayoutSelection /> */}
</div> </div>
</div> </div>
</div> </div>
@ -78,11 +78,11 @@ export const IssuesRoot = observer(() => {
<FilterPreview /> <FilterPreview />
</div> </div>
<div className="w-full h-full relative overflow-hidden"> <div className="w-full h-full relative overflow-hidden">
{issueFilterStore?.issueLayout === "list" && <IssueListViewRoot />} {issueFilterStore?.userDisplayFilters.layout === "list" && <IssueListViewRoot />}
{issueFilterStore?.issueLayout === "kanban" && <IssueKanBanViewRoot />} {issueFilterStore?.userDisplayFilters.layout === "kanban" && <IssueKanBanViewRoot />}
{issueFilterStore?.issueLayout === "calendar" && <IssueCalendarViewRoot />} {issueFilterStore?.userDisplayFilters.layout === "calendar" && <IssueCalendarViewRoot />}
{issueFilterStore?.issueLayout === "spreadsheet" && <IssueSpreadsheetViewRoot />} {issueFilterStore?.userDisplayFilters.layout === "spreadsheet" && <IssueSpreadsheetViewRoot />}
{issueFilterStore?.issueLayout === "gantt_chart" && <IssueGanttViewRoot />} {issueFilterStore?.userDisplayFilters.layout === "gantt_chart" && <IssueGanttViewRoot />}
</div> </div>
</div> </div>
); );

View File

@ -2,17 +2,8 @@ import { objToQueryParams } from "helpers/string.helper";
import { IAnalyticsParams, IJiraMetadata, INotificationParams } from "types"; import { IAnalyticsParams, IJiraMetadata, INotificationParams } from "types";
const paramsToKey = (params: any) => { const paramsToKey = (params: any) => {
const { const { state, priority, assignees, created_by, labels, start_date, target_date, sub_issue, start_target_date } =
state, params;
priority,
assignees,
created_by,
labels,
start_date,
target_date,
sub_issue,
start_target_date,
} = params;
let stateKey = state ? state.split(",") : []; let stateKey = state ? state.split(",") : [];
let priorityKey = priority ? priority.split(",") : []; let priorityKey = priority ? priority.split(",") : [];
@ -50,16 +41,7 @@ const inboxParamsToKey = (params: any) => {
}; };
const myIssuesParamsToKey = (params: any) => { const myIssuesParamsToKey = (params: any) => {
const { const { assignees, created_by, labels, priority, state_group, subscriber, start_date, target_date } = params;
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(",") : [];
@ -88,15 +70,12 @@ export const CURRENT_USER = "CURRENT_USER";
export const USER_WORKSPACE_INVITATIONS = "USER_WORKSPACE_INVITATIONS"; export const USER_WORKSPACE_INVITATIONS = "USER_WORKSPACE_INVITATIONS";
export const USER_WORKSPACES = "USER_WORKSPACES"; export const USER_WORKSPACES = "USER_WORKSPACES";
export const WORKSPACE_DETAILS = (workspaceSlug: string) => export const WORKSPACE_DETAILS = (workspaceSlug: string) => `WORKSPACE_DETAILS_${workspaceSlug.toUpperCase()}`;
`WORKSPACE_DETAILS_${workspaceSlug.toUpperCase()}`;
export const WORKSPACE_MEMBERS = (workspaceSlug: string) => export const WORKSPACE_MEMBERS = (workspaceSlug: string) => `WORKSPACE_MEMBERS_${workspaceSlug.toUpperCase()}`;
`WORKSPACE_MEMBERS_${workspaceSlug.toUpperCase()}`;
export const WORKSPACE_MEMBERS_WITH_EMAIL = (workspaceSlug: string) => export const WORKSPACE_MEMBERS_WITH_EMAIL = (workspaceSlug: string) =>
`WORKSPACE_MEMBERS_WITH_EMAIL_${workspaceSlug.toUpperCase()}`; `WORKSPACE_MEMBERS_WITH_EMAIL_${workspaceSlug.toUpperCase()}`;
export const WORKSPACE_MEMBERS_ME = (workspaceSlug: string) => export const WORKSPACE_MEMBERS_ME = (workspaceSlug: string) => `WORKSPACE_MEMBERS_ME${workspaceSlug.toUpperCase()}`;
`WORKSPACE_MEMBERS_ME${workspaceSlug.toUpperCase()}`;
export const WORKSPACE_INVITATIONS = "WORKSPACE_INVITATIONS"; export const WORKSPACE_INVITATIONS = "WORKSPACE_INVITATIONS";
export const WORKSPACE_INVITATION_WITH_EMAIL = (workspaceSlug: string) => export const WORKSPACE_INVITATION_WITH_EMAIL = (workspaceSlug: string) =>
`WORKSPACE_INVITATION_WITH_EMAIL_${workspaceSlug.toUpperCase()}`; `WORKSPACE_INVITATION_WITH_EMAIL_${workspaceSlug.toUpperCase()}`;
@ -111,9 +90,7 @@ export const PROJECTS_LIST = (
) => { ) => {
if (!params) return `PROJECTS_LIST_${workspaceSlug.toUpperCase()}`; if (!params) return `PROJECTS_LIST_${workspaceSlug.toUpperCase()}`;
return `PROJECTS_LIST_${workspaceSlug.toUpperCase()}_${params.is_favorite return `PROJECTS_LIST_${workspaceSlug.toUpperCase()}_${params.is_favorite.toString().toUpperCase()}`;
.toString()
.toUpperCase()}`;
}; };
export const PROJECT_DETAILS = (projectId: string) => `PROJECT_DETAILS_${projectId.toUpperCase()}`; export const PROJECT_DETAILS = (projectId: string) => `PROJECT_DETAILS_${projectId.toUpperCase()}`;
@ -149,35 +126,22 @@ export const PROJECT_DRAFT_ISSUES_LIST_WITH_PARAMS = (projectId: string, params?
return `PROJECT_DRAFT_ISSUES_LIST_WITH_PARAMS${projectId.toUpperCase()}_${paramsKey}`; return `PROJECT_DRAFT_ISSUES_LIST_WITH_PARAMS${projectId.toUpperCase()}_${paramsKey}`;
}; };
export const PROJECT_ISSUES_DETAILS = (issueId: string) => export const PROJECT_ISSUES_DETAILS = (issueId: string) => `PROJECT_ISSUES_DETAILS_${issueId.toUpperCase()}`;
`PROJECT_ISSUES_DETAILS_${issueId.toUpperCase()}`; export const PROJECT_ISSUES_PROPERTIES = (projectId: string) => `PROJECT_ISSUES_PROPERTIES_${projectId.toUpperCase()}`;
export const PROJECT_ISSUES_PROPERTIES = (projectId: string) => export const PROJECT_ISSUES_COMMENTS = (issueId: string) => `PROJECT_ISSUES_COMMENTS_${issueId.toUpperCase()}`;
`PROJECT_ISSUES_PROPERTIES_${projectId.toUpperCase()}`; export const PROJECT_ISSUES_ACTIVITY = (issueId: string) => `PROJECT_ISSUES_ACTIVITY_${issueId.toUpperCase()}`;
export const PROJECT_ISSUES_COMMENTS = (issueId: string) => export const PROJECT_ISSUE_BY_STATE = (projectId: string) => `PROJECT_ISSUE_BY_STATE_${projectId.toUpperCase()}`;
`PROJECT_ISSUES_COMMENTS_${issueId.toUpperCase()}`; export const PROJECT_ISSUE_LABELS = (projectId: string) => `PROJECT_ISSUE_LABELS_${projectId.toUpperCase()}`;
export const PROJECT_ISSUES_ACTIVITY = (issueId: string) => export const WORKSPACE_LABELS = (workspaceSlug: string) => `WORKSPACE_LABELS_${workspaceSlug.toUpperCase()}`;
`PROJECT_ISSUES_ACTIVITY_${issueId.toUpperCase()}`; export const PROJECT_GITHUB_REPOSITORY = (projectId: string) => `PROJECT_GITHUB_REPOSITORY_${projectId.toUpperCase()}`;
export const PROJECT_ISSUE_BY_STATE = (projectId: string) =>
`PROJECT_ISSUE_BY_STATE_${projectId.toUpperCase()}`;
export const PROJECT_ISSUE_LABELS = (projectId: string) =>
`PROJECT_ISSUE_LABELS_${projectId.toUpperCase()}`;
export const WORKSPACE_LABELS = (workspaceSlug: string) =>
`WORKSPACE_LABELS_${workspaceSlug.toUpperCase()}`;
export const PROJECT_GITHUB_REPOSITORY = (projectId: string) =>
`PROJECT_GITHUB_REPOSITORY_${projectId.toUpperCase()}`;
// cycles // cycles
export const CYCLES_LIST = (projectId: string) => `CYCLE_LIST_${projectId.toUpperCase()}`; export const CYCLES_LIST = (projectId: string) => `CYCLE_LIST_${projectId.toUpperCase()}`;
export const INCOMPLETE_CYCLES_LIST = (projectId: string) => export const INCOMPLETE_CYCLES_LIST = (projectId: string) => `INCOMPLETE_CYCLES_LIST_${projectId.toUpperCase()}`;
`INCOMPLETE_CYCLES_LIST_${projectId.toUpperCase()}`; export const CURRENT_CYCLE_LIST = (projectId: string) => `CURRENT_CYCLE_LIST_${projectId.toUpperCase()}`;
export const CURRENT_CYCLE_LIST = (projectId: string) => export const UPCOMING_CYCLES_LIST = (projectId: string) => `UPCOMING_CYCLES_LIST_${projectId.toUpperCase()}`;
`CURRENT_CYCLE_LIST_${projectId.toUpperCase()}`; export const DRAFT_CYCLES_LIST = (projectId: string) => `DRAFT_CYCLES_LIST_${projectId.toUpperCase()}`;
export const UPCOMING_CYCLES_LIST = (projectId: string) => export const COMPLETED_CYCLES_LIST = (projectId: string) => `COMPLETED_CYCLES_LIST_${projectId.toUpperCase()}`;
`UPCOMING_CYCLES_LIST_${projectId.toUpperCase()}`;
export const DRAFT_CYCLES_LIST = (projectId: string) =>
`DRAFT_CYCLES_LIST_${projectId.toUpperCase()}`;
export const COMPLETED_CYCLES_LIST = (projectId: string) =>
`COMPLETED_CYCLES_LIST_${projectId.toUpperCase()}`;
export const CYCLE_ISSUES = (cycleId: string) => `CYCLE_ISSUES_${cycleId.toUpperCase()}`; export const CYCLE_ISSUES = (cycleId: string) => `CYCLE_ISSUES_${cycleId.toUpperCase()}`;
export const CYCLE_ISSUES_WITH_PARAMS = (cycleId: string, params?: any) => { export const CYCLE_ISSUES_WITH_PARAMS = (cycleId: string, params?: any) => {
if (!params) return `CYCLE_ISSUES_WITH_PARAMS_${cycleId.toUpperCase()}`; if (!params) return `CYCLE_ISSUES_WITH_PARAMS_${cycleId.toUpperCase()}`;
@ -199,8 +163,7 @@ export const USER_ISSUES = (workspaceSlug: string, params: any) => {
export const USER_ACTIVITY = "USER_ACTIVITY"; export const USER_ACTIVITY = "USER_ACTIVITY";
export const USER_WORKSPACE_DASHBOARD = (workspaceSlug: string) => export const USER_WORKSPACE_DASHBOARD = (workspaceSlug: string) =>
`USER_WORKSPACE_DASHBOARD_${workspaceSlug.toUpperCase()}`; `USER_WORKSPACE_DASHBOARD_${workspaceSlug.toUpperCase()}`;
export const USER_PROJECT_VIEW = (projectId: string) => export const USER_PROJECT_VIEW = (projectId: string) => `USER_PROJECT_VIEW_${projectId.toUpperCase()}`;
`USER_PROJECT_VIEW_${projectId.toUpperCase()}`;
export const MODULE_LIST = (projectId: string) => `MODULE_LIST_${projectId.toUpperCase()}`; export const MODULE_LIST = (projectId: string) => `MODULE_LIST_${projectId.toUpperCase()}`;
export const MODULE_ISSUES = (moduleId: string) => `MODULE_ISSUES_${moduleId.toUpperCase()}`; export const MODULE_ISSUES = (moduleId: string) => `MODULE_ISSUES_${moduleId.toUpperCase()}`;
@ -240,8 +203,7 @@ export const INBOX_ISSUE_DETAILS = (inboxId: string, issueId: string) =>
export const ISSUE_DETAILS = (issueId: string) => `ISSUE_DETAILS_${issueId.toUpperCase()}`; export const ISSUE_DETAILS = (issueId: string) => `ISSUE_DETAILS_${issueId.toUpperCase()}`;
export const SUB_ISSUES = (issueId: string) => `SUB_ISSUES_${issueId.toUpperCase()}`; export const SUB_ISSUES = (issueId: string) => `SUB_ISSUES_${issueId.toUpperCase()}`;
export const ISSUE_ATTACHMENTS = (issueId: string) => `ISSUE_ATTACHMENTS_${issueId.toUpperCase()}`; export const ISSUE_ATTACHMENTS = (issueId: string) => `ISSUE_ATTACHMENTS_${issueId.toUpperCase()}`;
export const ARCHIVED_ISSUE_DETAILS = (issueId: string) => export const ARCHIVED_ISSUE_DETAILS = (issueId: string) => `ARCHIVED_ISSUE_DETAILS_${issueId.toUpperCase()}`;
`ARCHIVED_ISSUE_DETAILS_${issueId.toUpperCase()}`;
// integrations // integrations
export const APP_INTEGRATIONS = "APP_INTEGRATIONS"; export const APP_INTEGRATIONS = "APP_INTEGRATIONS";
@ -271,22 +233,18 @@ export const SLACK_CHANNEL_INFO = (workspaceSlug: string, projectId: string) =>
`SLACK_CHANNEL_INFO_${workspaceSlug.toString().toUpperCase()}_${projectId.toUpperCase()}`; `SLACK_CHANNEL_INFO_${workspaceSlug.toString().toUpperCase()}_${projectId.toUpperCase()}`;
// Pages // Pages
export const RECENT_PAGES_LIST = (projectId: string) => export const RECENT_PAGES_LIST = (projectId: string) => `RECENT_PAGES_LIST_${projectId.toUpperCase()}`;
`RECENT_PAGES_LIST_${projectId.toUpperCase()}`;
export const ALL_PAGES_LIST = (projectId: string) => `ALL_PAGES_LIST_${projectId.toUpperCase()}`; export const ALL_PAGES_LIST = (projectId: string) => `ALL_PAGES_LIST_${projectId.toUpperCase()}`;
export const FAVORITE_PAGES_LIST = (projectId: string) => export const FAVORITE_PAGES_LIST = (projectId: string) => `FAVORITE_PAGES_LIST_${projectId.toUpperCase()}`;
`FAVORITE_PAGES_LIST_${projectId.toUpperCase()}`;
export const MY_PAGES_LIST = (projectId: string) => `MY_PAGES_LIST_${projectId.toUpperCase()}`; export const MY_PAGES_LIST = (projectId: string) => `MY_PAGES_LIST_${projectId.toUpperCase()}`;
export const OTHER_PAGES_LIST = (projectId: string) => export const OTHER_PAGES_LIST = (projectId: string) => `OTHER_PAGES_LIST_${projectId.toUpperCase()}`;
`OTHER_PAGES_LIST_${projectId.toUpperCase()}`;
export const PAGE_DETAILS = (pageId: string) => `PAGE_DETAILS_${pageId.toUpperCase()}`; export const PAGE_DETAILS = (pageId: string) => `PAGE_DETAILS_${pageId.toUpperCase()}`;
export const PAGE_BLOCKS_LIST = (pageId: string) => `PAGE_BLOCK_LIST_${pageId.toUpperCase()}`; export const PAGE_BLOCKS_LIST = (pageId: string) => `PAGE_BLOCK_LIST_${pageId.toUpperCase()}`;
export const PAGE_BLOCK_DETAILS = (pageId: string) => `PAGE_BLOCK_DETAILS_${pageId.toUpperCase()}`; export const PAGE_BLOCK_DETAILS = (pageId: string) => `PAGE_BLOCK_DETAILS_${pageId.toUpperCase()}`;
// estimates // estimates
export const ESTIMATES_LIST = (projectId: string) => `ESTIMATES_LIST_${projectId.toUpperCase()}`; export const ESTIMATES_LIST = (projectId: string) => `ESTIMATES_LIST_${projectId.toUpperCase()}`;
export const ESTIMATE_DETAILS = (estimateId: string) => export const ESTIMATE_DETAILS = (estimateId: string) => `ESTIMATE_DETAILS_${estimateId.toUpperCase()}`;
`ESTIMATE_DETAILS_${estimateId.toUpperCase()}`;
// analytics // analytics
export const ANALYTICS = (workspaceSlug: string, params: IAnalyticsParams) => export const ANALYTICS = (workspaceSlug: string, params: IAnalyticsParams) =>
@ -294,15 +252,10 @@ export const ANALYTICS = (workspaceSlug: string, params: IAnalyticsParams) =>
params.segment params.segment
}_${params.project?.toString()}`; }_${params.project?.toString()}`;
export const DEFAULT_ANALYTICS = (workspaceSlug: string, params?: Partial<IAnalyticsParams>) => export const DEFAULT_ANALYTICS = (workspaceSlug: string, params?: Partial<IAnalyticsParams>) =>
`DEFAULT_ANALYTICS_${workspaceSlug.toUpperCase()}_${params?.project?.toString()}_${ `DEFAULT_ANALYTICS_${workspaceSlug.toUpperCase()}_${params?.project?.toString()}_${params?.cycle}_${params?.module}`;
params?.cycle
}_${params?.module}`;
// notifications // notifications
export const USER_WORKSPACE_NOTIFICATIONS = ( export const USER_WORKSPACE_NOTIFICATIONS = (workspaceSlug: string, params: INotificationParams) => {
workspaceSlug: string,
params: INotificationParams
) => {
const { type, snoozed, archived, read } = params; const { type, snoozed, archived, read } = params;
return `USER_WORKSPACE_NOTIFICATIONS_${workspaceSlug?.toUpperCase()}_TYPE_${( return `USER_WORKSPACE_NOTIFICATIONS_${workspaceSlug?.toUpperCase()}_TYPE_${(
@ -310,21 +263,13 @@ export const USER_WORKSPACE_NOTIFICATIONS = (
)?.toUpperCase()}_SNOOZED_${snoozed}_ARCHIVED_${archived}_READ_${read}`; )?.toUpperCase()}_SNOOZED_${snoozed}_ARCHIVED_${archived}_READ_${read}`;
}; };
export const USER_WORKSPACE_NOTIFICATIONS_DETAILS = ( export const USER_WORKSPACE_NOTIFICATIONS_DETAILS = (workspaceSlug: string, notificationId: string) =>
workspaceSlug: string,
notificationId: string
) =>
`USER_WORKSPACE_NOTIFICATIONS_DETAILS_${workspaceSlug?.toUpperCase()}_${notificationId?.toUpperCase()}`; `USER_WORKSPACE_NOTIFICATIONS_DETAILS_${workspaceSlug?.toUpperCase()}_${notificationId?.toUpperCase()}`;
export const UNREAD_NOTIFICATIONS_COUNT = (workspaceSlug: string) => export const UNREAD_NOTIFICATIONS_COUNT = (workspaceSlug: string) =>
`UNREAD_NOTIFICATIONS_COUNT_${workspaceSlug?.toUpperCase()}`; `UNREAD_NOTIFICATIONS_COUNT_${workspaceSlug?.toUpperCase()}`;
export const getPaginatedNotificationKey = ( export const getPaginatedNotificationKey = (index: number, prevData: any, workspaceSlug: string, params: any) => {
index: number,
prevData: any,
workspaceSlug: string,
params: any
) => {
if (prevData && !prevData?.results?.length) return null; if (prevData && !prevData?.results?.length) return null;
if (index === 0) if (index === 0)
@ -360,9 +305,5 @@ export const USER_PROFILE_ISSUES = (workspaceSlug: string, userId: string, param
// reactions // reactions
export const ISSUE_REACTION_LIST = (workspaceSlug: string, projectId: string, issueId: string) => export const ISSUE_REACTION_LIST = (workspaceSlug: string, projectId: string, issueId: string) =>
`ISSUE_REACTION_LIST_${workspaceSlug.toUpperCase()}_${projectId.toUpperCase()}_${issueId.toUpperCase()}`; `ISSUE_REACTION_LIST_${workspaceSlug.toUpperCase()}_${projectId.toUpperCase()}_${issueId.toUpperCase()}`;
export const COMMENT_REACTION_LIST = ( export const COMMENT_REACTION_LIST = (workspaceSlug: string, projectId: string, commendId: string) =>
workspaceSlug: string,
projectId: string,
commendId: string
) =>
`COMMENT_REACTION_LIST_${workspaceSlug.toUpperCase()}_${projectId.toUpperCase()}_${commendId.toUpperCase()}`; `COMMENT_REACTION_LIST_${workspaceSlug.toUpperCase()}_${projectId.toUpperCase()}_${commendId.toUpperCase()}`;

View File

@ -117,11 +117,11 @@ export const ISSUE_LAYOUTS: {
title: string; title: string;
icon: any; icon: any;
}[] = [ }[] = [
{ key: "list", title: "List View", icon: List }, { key: "list", title: "List Layout", icon: List },
{ key: "kanban", title: "Kanban View", icon: Kanban }, { key: "kanban", title: "Kanban Layout", icon: Kanban },
{ key: "calendar", title: "Calendar View", icon: Calendar }, { key: "calendar", title: "Calendar Layout", icon: Calendar },
{ key: "spreadsheet", title: "Spreadsheet View", icon: Sheet }, { key: "spreadsheet", title: "Spreadsheet Layout", icon: Sheet },
{ key: "gantt_chart", title: "Gantt Chart View", icon: GanttChart }, { key: "gantt_chart", title: "Gantt Chart Layout", icon: GanttChart },
]; ];
export const ISSUE_LIST_FILTERS = [ export const ISSUE_LIST_FILTERS = [
@ -198,26 +198,116 @@ export const ISSUE_GANTT_DISPLAY_FILTERS = [
{ key: "sub_issue", title: "Sub Issue" }, { key: "sub_issue", title: "Sub Issue" },
]; ];
// TODO: update this later export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: {
export const ISSUE_EXTRA_DISPLAY_PROPERTIES = { [key: string]: {
list: { layout: TIssueLayouts[];
access: true, filters: {
values: ["show_empty_groups", "sub_issue"], [key in TIssueLayouts]: string[];
};
display_properties: {
[key in TIssueLayouts]: boolean;
};
display_filters: {
[key in TIssueLayouts]: string[];
};
extra_options: {
[key in TIssueLayouts]: {
access: boolean;
values: string[];
};
};
};
} = {
my_issues: {
layout: ["list", "kanban"],
filters: {
list: ["priority", "state_group", "labels", "start_date", "due_date"],
kanban: ["priority", "state_group", "labels", "start_date", "due_date"],
calendar: [],
spreadsheet: [],
gantt_chart: [],
},
display_properties: {
list: true,
kanban: true,
calendar: true,
spreadsheet: true,
gantt_chart: false,
},
display_filters: {
list: ["group_by", "sub_group_by", "order_by", "issue_type"],
kanban: ["group_by", "sub_group_by", "order_by", "issue_type"],
calendar: ["issue_type"],
spreadsheet: ["issue_type"],
gantt_chart: ["order_by", "issue_type"],
},
extra_options: {
list: {
access: true,
values: ["show_empty_groups", "sub_issue"],
},
kanban: {
access: true,
values: ["show_empty_groups", "sub_issue"],
},
calendar: {
access: false,
values: [],
},
spreadsheet: {
access: false,
values: [],
},
gantt_chart: {
access: false,
values: [],
},
},
}, },
kanban: { issues: {
access: true, layout: ["list", "kanban", "calendar", "spreadsheet", "gantt_chart"],
values: ["show_empty_groups", "sub_issue"], filters: {
}, list: ["priority", "state", "assignees", "created_by", "labels", "start_date", "due_date"],
calendar: { kanban: ["priority", "state", "assignees", "created_by", "labels", "start_date", "due_date"],
access: false, calendar: ["priority", "state", "assignees", "created_by", "labels"],
values: [], spreadsheet: ["priority", "state", "assignees", "created_by", "labels", "start_date", "due_date"],
}, gantt_chart: ["priority", "state", "assignees", "created_by", "labels", "start_date", "due_date"],
spreadsheet: { },
access: false, display_properties: {
values: [], list: true,
}, kanban: true,
gantt_chart: { calendar: true,
access: true, spreadsheet: true,
values: ["sub_issue"], gantt_chart: false,
},
display_filters: {
list: ["group_by", "sub_group_by", "order_by", "issue_type"],
kanban: ["group_by", "sub_group_by", "order_by", "issue_type"],
calendar: ["issue_type"],
spreadsheet: ["issue_type"],
gantt_chart: ["order_by", "issue_type"],
},
extra_options: {
list: {
access: true,
values: ["show_empty_groups", "sub_issue"],
},
kanban: {
access: true,
values: ["show_empty_groups", "sub_issue"],
},
calendar: {
access: false,
values: [],
},
spreadsheet: {
access: false,
values: [],
},
gantt_chart: {
access: true,
values: ["sub_issue"],
},
},
}, },
}; };

View File

@ -91,6 +91,7 @@ export const handleIssueQueryParamsByLayout = (_layout: TIssueLayouts | undefine
"start_date", "start_date",
"target_date", "target_date",
"group_by", "group_by",
"sub_group_by",
"order_by", "order_by",
"type", "type",
"sub_issue", "sub_issue",
@ -107,6 +108,7 @@ export const handleIssueQueryParamsByLayout = (_layout: TIssueLayouts | undefine
"start_date", "start_date",
"target_date", "target_date",
"group_by", "group_by",
"sub_group_by",
"order_by", "order_by",
"type", "type",
"sub_issue", "sub_issue",

View File

@ -54,29 +54,30 @@ const ProjectIssues: NextPage = () => {
workspaceSlug && projectId ? () => inboxService.getInboxes(workspaceSlug as string, projectId as string) : null workspaceSlug && projectId ? () => inboxService.getInboxes(workspaceSlug as string, projectId as string) : null
); );
// TODO: update the fetch keys
useSWR( useSWR(
workspaceSlug && projectId ? USER_PROJECT_VIEW(projectId.toString()) : null, workspaceSlug && projectId ? "REVALIDATE_USER_PROJECT_FILTERS" : null,
workspaceSlug && projectId workspaceSlug && projectId
? () => issueFilterStore.fetchUserProjectFilters(workspaceSlug.toString(), projectId.toString()) ? () => issueFilterStore.fetchUserProjectFilters(workspaceSlug.toString(), projectId.toString())
: null : null
); );
useSWR( useSWR(
workspaceSlug && projectId ? STATES_LIST(projectId.toString()) : null, workspaceSlug && projectId ? "REVALIDATE_PROJECT_STATES_LIST" : null,
workspaceSlug && projectId workspaceSlug && projectId
? () => projectStore.fetchProjectStates(workspaceSlug.toString(), projectId.toString()) ? () => projectStore.fetchProjectStates(workspaceSlug.toString(), projectId.toString())
: null : null
); );
useSWR( useSWR(
workspaceSlug && projectId ? PROJECT_ISSUE_LABELS(projectId.toString()) : null, workspaceSlug && projectId ? "REVALIDATE_PROJECT_LABELS_LIST" : null,
workspaceSlug && projectId workspaceSlug && projectId
? () => projectStore.fetchProjectLabels(workspaceSlug.toString(), projectId.toString()) ? () => projectStore.fetchProjectLabels(workspaceSlug.toString(), projectId.toString())
: null : null
); );
useSWR( useSWR(
workspaceSlug && projectId ? PROJECT_MEMBERS(projectId.toString()) : null, workspaceSlug && projectId ? "REVALIDATE_PROJECT_MEMBERS_LIST" : null,
workspaceSlug && projectId workspaceSlug && projectId
? () => projectStore.fetchProjectMembers(workspaceSlug.toString(), projectId.toString()) ? () => projectStore.fetchProjectMembers(workspaceSlug.toString(), projectId.toString())
: null : null

View File

@ -2,6 +2,8 @@ import { observable, action, computed, makeObservable, runInAction } from "mobx"
// services // services
import { ProjectService } from "services/project.service"; import { ProjectService } from "services/project.service";
import { IssueService } from "services/issue.service"; import { IssueService } from "services/issue.service";
// helpers
import { handleIssueQueryParamsByLayout } from "helpers/issue.helper";
// types // types
import { RootStore } from "./root"; import { RootStore } from "./root";
import { import {
@ -11,7 +13,6 @@ import {
IProjectViewProps, IProjectViewProps,
TIssueParams, TIssueParams,
} from "types"; } from "types";
import { handleIssueQueryParamsByLayout } from "helpers/issue.helper";
export interface IIssueFilterStore { export interface IIssueFilterStore {
loader: boolean; loader: boolean;
@ -21,6 +22,7 @@ export interface IIssueFilterStore {
userFilters: IIssueFilterOptions; userFilters: IIssueFilterOptions;
defaultDisplayFilters: IIssueDisplayFilterOptions; defaultDisplayFilters: IIssueDisplayFilterOptions;
defaultFilters: IIssueFilterOptions; defaultFilters: IIssueFilterOptions;
filtersSearchQuery: string;
// action // action
fetchUserProjectFilters: (workspaceSlug: string, projectSlug: string) => Promise<void>; fetchUserProjectFilters: (workspaceSlug: string, projectSlug: string) => Promise<void>;
@ -34,6 +36,7 @@ export interface IIssueFilterStore {
projectSlug: string, projectSlug: string,
properties: Partial<IIssueDisplayProperties> properties: Partial<IIssueDisplayProperties>
) => Promise<void>; ) => Promise<void>;
updateFiltersSearchQuery: (query: string) => void;
// computed // computed
appliedFilters: TIssueParams[] | null; appliedFilters: TIssueParams[] | null;
@ -64,6 +67,7 @@ class IssueFilterStore implements IIssueFilterStore {
created_on: true, created_on: true,
updated_on: true, updated_on: true,
}; };
filtersSearchQuery: string = "";
// root store // root store
rootStore; rootStore;
@ -83,11 +87,13 @@ class IssueFilterStore implements IIssueFilterStore {
userDisplayProperties: observable.ref, userDisplayProperties: observable.ref,
userDisplayFilters: observable.ref, userDisplayFilters: observable.ref,
userFilters: observable.ref, userFilters: observable.ref,
filtersSearchQuery: observable.ref,
// actions // actions
fetchUserProjectFilters: action, fetchUserProjectFilters: action,
updateUserFilters: action, updateUserFilters: action,
updateDisplayProperties: action, updateDisplayProperties: action,
updateFiltersSearchQuery: action,
// computed // computed
appliedFilters: computed, appliedFilters: computed,
@ -183,6 +189,12 @@ class IssueFilterStore implements IIssueFilterStore {
console.log("Failed to update user filters in issue filter store", error); console.log("Failed to update user filters in issue filter store", error);
} }
}; };
updateFiltersSearchQuery: (query: string) => void = (query) => {
runInAction(() => {
this.filtersSearchQuery = query;
});
};
} }
export default IssueFilterStore; export default IssueFilterStore;

View File

@ -40,6 +40,7 @@ export type TIssueParams =
| "start_date" | "start_date"
| "target_date" | "target_date"
| "group_by" | "group_by"
| "sub_group_by"
| "order_by" | "order_by"
| "type" | "type"
| "sub_issue" | "sub_issue"
@ -62,6 +63,7 @@ export interface IIssueFilterOptions {
export interface IIssueDisplayFilterOptions { export interface IIssueDisplayFilterOptions {
calendar_date_range?: string; calendar_date_range?: string;
group_by?: TIssueGroupByOptions; group_by?: TIssueGroupByOptions;
sub_group_by?: TIssueGroupByOptions;
layout?: TIssueLayouts; layout?: TIssueLayouts;
order_by?: TIssueOrderByOptions; order_by?: TIssueOrderByOptions;
show_empty_groups?: boolean; show_empty_groups?: boolean;