mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
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:
parent
9a8dcc349f
commit
9831418a11
@ -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>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -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>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -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>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -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>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -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";
|
||||||
|
@ -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>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -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>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -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>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
@ -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>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -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>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
230
web/components/issue-layouts/filters/filters-selection.tsx
Normal file
230
web/components/issue-layouts/filters/filters-selection.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
});
|
@ -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";
|
||||||
|
@ -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>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -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>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -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>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -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>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -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>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -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>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -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>
|
||||||
</>
|
</>
|
||||||
|
@ -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"
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
|
@ -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()}`;
|
||||||
|
@ -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"],
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -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",
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
2
web/types/view-props.d.ts
vendored
2
web/types/view-props.d.ts
vendored
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user