mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
chore: rendered applied filters in the project views
This commit is contained in:
parent
73c91654eb
commit
0f92473eba
57
web/components/views/applied-filters/filter-item-map.tsx
Normal file
57
web/components/views/applied-filters/filter-item-map.tsx
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { FC, useMemo } from "react";
|
||||||
|
// components
|
||||||
|
import { ViewAppliedFiltersItem } from "./";
|
||||||
|
// hooks
|
||||||
|
import { useViewFilter } from "hooks/user-view-filters";
|
||||||
|
// types
|
||||||
|
import { IIssueFilterOptions } from "@plane/types";
|
||||||
|
|
||||||
|
type TViewAppliedFiltersItemMap = {
|
||||||
|
workspaceSlug: string;
|
||||||
|
projectId: string;
|
||||||
|
filterKey: keyof IIssueFilterOptions;
|
||||||
|
filterValue: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ViewAppliedFiltersItemMap: FC<TViewAppliedFiltersItemMap> = (props) => {
|
||||||
|
const { workspaceSlug, projectId, filterKey, filterValue } = props;
|
||||||
|
// hooks
|
||||||
|
const viewFilterStore = useViewFilter(workspaceSlug, projectId);
|
||||||
|
|
||||||
|
const currentDefaultFilterDetails = useMemo(
|
||||||
|
() => viewFilterStore?.propertyDefaultDetails(filterKey),
|
||||||
|
[viewFilterStore, filterKey]
|
||||||
|
);
|
||||||
|
|
||||||
|
const propertyVisibleCount = 5;
|
||||||
|
|
||||||
|
if (!filterValue) return <></>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative flex items-center gap-2 border border-custom-border-200 rounded p-1 px-1.5">
|
||||||
|
<div className="flex-shrink-0 text-xs text-custom-text-200 capitalize">{filterKey.replaceAll("_", " ")}</div>
|
||||||
|
<div className="relative flex items-center gap-1.5 flex-wrap">
|
||||||
|
{propertyVisibleCount && filterValue.length >= propertyVisibleCount ? (
|
||||||
|
<div className="text-xs bg-custom-primary-100/20 rounded relative flex items-center gap-1 p-1 px-2">
|
||||||
|
<div className="flex-shrink-0 w-4-h-4">{currentDefaultFilterDetails?.icon}</div>
|
||||||
|
<div className="whitespace-nowrap">
|
||||||
|
{filterValue.length} {currentDefaultFilterDetails?.label}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{filterValue.map((propertyId) => (
|
||||||
|
<ViewAppliedFiltersItem
|
||||||
|
key={`filter_value_${filterKey}_${propertyId}`}
|
||||||
|
workspaceSlug={workspaceSlug}
|
||||||
|
projectId={projectId}
|
||||||
|
filterKey={filterKey}
|
||||||
|
propertyId={propertyId}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
32
web/components/views/applied-filters/filter-item.tsx
Normal file
32
web/components/views/applied-filters/filter-item.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
import { ImagePlus } from "lucide-react";
|
||||||
|
// types
|
||||||
|
import { IIssueFilterOptions } from "@plane/types";
|
||||||
|
import { useViewFilter } from "hooks/user-view-filters";
|
||||||
|
|
||||||
|
type TViewAppliedFiltersItem = {
|
||||||
|
workspaceSlug: string;
|
||||||
|
projectId: string;
|
||||||
|
filterKey: keyof IIssueFilterOptions;
|
||||||
|
propertyId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ViewAppliedFiltersItem: FC<TViewAppliedFiltersItem> = (props) => {
|
||||||
|
const { workspaceSlug, projectId, filterKey, propertyId } = props;
|
||||||
|
// hooks
|
||||||
|
const viewFilterHelper = useViewFilter(workspaceSlug, projectId);
|
||||||
|
const propertyDetail = viewFilterHelper?.propertyDetails(filterKey, propertyId) || undefined;
|
||||||
|
|
||||||
|
if (!filterKey || !propertyId) return <></>;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={`filter_value_${filterKey}_${propertyId}`}
|
||||||
|
className="bg-custom-background-80 rounded relative flex items-center gap-1.5 p-1"
|
||||||
|
>
|
||||||
|
<div className="flex-shrink-0 w-4 h-4 relative flex justify-center items-center overflow-hidden">
|
||||||
|
{propertyDetail?.icon || <ImagePlus size={14} />}
|
||||||
|
</div>
|
||||||
|
<div className="text-xs">{propertyDetail?.label || propertyId}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
36
web/components/views/applied-filters/filters-root.tsx
Normal file
36
web/components/views/applied-filters/filters-root.tsx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { FC } from "react";
|
||||||
|
// components
|
||||||
|
import { ViewAppliedFiltersItemMap } from "./";
|
||||||
|
// types
|
||||||
|
import { IIssueFilterOptions } from "@plane/types";
|
||||||
|
|
||||||
|
type TViewAppliedFilters = {
|
||||||
|
workspaceSlug: string;
|
||||||
|
projectId: string;
|
||||||
|
filters: Partial<Record<keyof IIssueFilterOptions, string[] | undefined | null>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ViewAppliedFilters: FC<TViewAppliedFilters> = (props) => {
|
||||||
|
const { workspaceSlug, projectId, filters } = props;
|
||||||
|
|
||||||
|
if (filters && Object.keys(filters).length <= 0)
|
||||||
|
return (
|
||||||
|
<div className="text-xs bg-custom-primary-100/20 rounded relative flex items-center gap-1 p-1 px-2">
|
||||||
|
<div className="whitespace-nowrap">0 Filters</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative flex items-center gap-2 flex-wrap">
|
||||||
|
{Object.entries(filters || {}).map(([key, value]) => (
|
||||||
|
<ViewAppliedFiltersItemMap
|
||||||
|
key={key}
|
||||||
|
workspaceSlug={workspaceSlug}
|
||||||
|
projectId={projectId}
|
||||||
|
filterKey={key as keyof IIssueFilterOptions}
|
||||||
|
filterValue={value || []}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
3
web/components/views/applied-filters/index.ts
Normal file
3
web/components/views/applied-filters/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export * from "./filters-root";
|
||||||
|
export * from "./filter-item-map";
|
||||||
|
export * from "./filter-item";
|
@ -3,3 +3,4 @@ export * from "./form";
|
|||||||
export * from "./modal";
|
export * from "./modal";
|
||||||
export * from "./view-list-item";
|
export * from "./view-list-item";
|
||||||
export * from "./views-list";
|
export * from "./views-list";
|
||||||
|
export * from "./applied-filters";
|
||||||
|
@ -6,11 +6,11 @@ import { LinkIcon, PencilIcon, StarIcon, TrashIcon } from "lucide-react";
|
|||||||
// ui
|
// ui
|
||||||
import { CustomMenu, TOAST_TYPE, setToast } from "@plane/ui";
|
import { CustomMenu, TOAST_TYPE, setToast } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { CreateUpdateProjectViewModal, DeleteProjectViewModal } from "components/views";
|
import { CreateUpdateProjectViewModal, DeleteProjectViewModal, ViewAppliedFilters } from "components/views";
|
||||||
// constants
|
// constants
|
||||||
import { EUserProjectRoles } from "constants/project";
|
import { EUserProjectRoles } from "constants/project";
|
||||||
// helpers
|
// helpers
|
||||||
import { calculateTotalFilters } from "helpers/filter.helper";
|
// import { calculateTotalFilters } from "helpers/filter.helper";
|
||||||
import { copyUrlToClipboard } from "helpers/string.helper";
|
import { copyUrlToClipboard } from "helpers/string.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useProjectView, useUser } from "hooks/store";
|
import { useProjectView, useUser } from "hooks/store";
|
||||||
@ -59,7 +59,7 @@ export const ProjectViewListItem: React.FC<Props> = observer((props) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const totalFilters = calculateTotalFilters(view.filters ?? {});
|
// const totalFilters = calculateTotalFilters(view.filters ?? {});
|
||||||
|
|
||||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||||
|
|
||||||
@ -77,84 +77,85 @@ export const ProjectViewListItem: React.FC<Props> = observer((props) => {
|
|||||||
<DeleteProjectViewModal data={view} isOpen={deleteViewModal} onClose={() => setDeleteViewModal(false)} />
|
<DeleteProjectViewModal data={view} isOpen={deleteViewModal} onClose={() => setDeleteViewModal(false)} />
|
||||||
<div className="group border-b border-custom-border-200 hover:bg-custom-background-90">
|
<div className="group border-b border-custom-border-200 hover:bg-custom-background-90">
|
||||||
<Link href={`/${workspaceSlug}/projects/${projectId}/views/${view.id}`}>
|
<Link href={`/${workspaceSlug}/projects/${projectId}/views/${view.id}`}>
|
||||||
<div className="relative flex h-[52px] w-full items-center justify-between rounded p-4">
|
<div className="relative rounded p-4 min-h-[52px] flex w-full items-center justify-between overflow-hidden gap-2">
|
||||||
<div className="flex w-full items-center justify-between">
|
<div className="flex-shrink-0 flex items-center gap-4 overflow-hidden">
|
||||||
<div className="flex items-center gap-4 overflow-hidden">
|
<div className="flex flex-col overflow-hidden ">
|
||||||
<div className="flex flex-col overflow-hidden ">
|
<p className="truncate break-all text-sm font-medium leading-4">{view.name}</p>
|
||||||
<p className="truncate break-all text-sm font-medium leading-4">{view.name}</p>
|
{view?.description && <p className="break-all text-xs text-custom-text-200">{view.description}</p>}
|
||||||
{view?.description && <p className="break-all text-xs text-custom-text-200">{view.description}</p>}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-2 flex flex-shrink-0">
|
</div>
|
||||||
<div className="flex items-center gap-4">
|
|
||||||
<p className="hidden rounded bg-custom-background-80 px-2 py-1 text-xs text-custom-text-200 group-hover:block">
|
|
||||||
{totalFilters} {totalFilters === 1 ? "filter" : "filters"}
|
|
||||||
</p>
|
|
||||||
{isEditingAllowed &&
|
|
||||||
(view.is_favorite ? (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
handleRemoveFromFavorites();
|
|
||||||
}}
|
|
||||||
className="grid place-items-center"
|
|
||||||
>
|
|
||||||
<StarIcon className="h-3.5 w-3.5 fill-orange-400 text-orange-400" strokeWidth={2} />
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
handleAddToFavorites();
|
|
||||||
}}
|
|
||||||
className="grid place-items-center"
|
|
||||||
>
|
|
||||||
<StarIcon size={14} strokeWidth={2} />
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
|
|
||||||
<CustomMenu ellipsis>
|
<div className="relative flex items-center gap-4 overflow-hidden">
|
||||||
{isEditingAllowed && (
|
{workspaceSlug && projectId && (
|
||||||
<>
|
<ViewAppliedFilters
|
||||||
<CustomMenu.MenuItem
|
workspaceSlug={workspaceSlug?.toString()}
|
||||||
onClick={(e) => {
|
projectId={projectId?.toString()}
|
||||||
e.preventDefault();
|
filters={view.filters || {}}
|
||||||
e.stopPropagation();
|
/>
|
||||||
setCreateUpdateViewModal(true);
|
)}
|
||||||
}}
|
|
||||||
>
|
{isEditingAllowed &&
|
||||||
<span className="flex items-center justify-start gap-2">
|
(view.is_favorite ? (
|
||||||
<PencilIcon size={14} strokeWidth={2} />
|
<button
|
||||||
<span>Edit View</span>
|
type="button"
|
||||||
</span>
|
onClick={(e) => {
|
||||||
</CustomMenu.MenuItem>
|
e.preventDefault();
|
||||||
<CustomMenu.MenuItem
|
e.stopPropagation();
|
||||||
onClick={(e) => {
|
handleRemoveFromFavorites();
|
||||||
e.preventDefault();
|
}}
|
||||||
e.stopPropagation();
|
className="grid place-items-center"
|
||||||
setDeleteViewModal(true);
|
>
|
||||||
}}
|
<StarIcon className="h-3.5 w-3.5 fill-orange-400 text-orange-400" strokeWidth={2} />
|
||||||
>
|
</button>
|
||||||
<span className="flex items-center justify-start gap-2">
|
) : (
|
||||||
<TrashIcon size={14} strokeWidth={2} />
|
<button
|
||||||
<span>Delete View</span>
|
type="button"
|
||||||
</span>
|
onClick={(e) => {
|
||||||
</CustomMenu.MenuItem>
|
e.preventDefault();
|
||||||
</>
|
e.stopPropagation();
|
||||||
)}
|
handleAddToFavorites();
|
||||||
<CustomMenu.MenuItem onClick={handleCopyText}>
|
}}
|
||||||
|
className="grid place-items-center"
|
||||||
|
>
|
||||||
|
<StarIcon size={14} strokeWidth={2} />
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
<CustomMenu ellipsis>
|
||||||
|
{isEditingAllowed && (
|
||||||
|
<>
|
||||||
|
<CustomMenu.MenuItem
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
setCreateUpdateViewModal(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
<span className="flex items-center justify-start gap-2">
|
<span className="flex items-center justify-start gap-2">
|
||||||
<LinkIcon className="h-3 w-3" />
|
<PencilIcon size={14} strokeWidth={2} />
|
||||||
<span>Copy view link</span>
|
<span>Edit View</span>
|
||||||
</span>
|
</span>
|
||||||
</CustomMenu.MenuItem>
|
</CustomMenu.MenuItem>
|
||||||
</CustomMenu>
|
<CustomMenu.MenuItem
|
||||||
</div>
|
onClick={(e) => {
|
||||||
</div>
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
setDeleteViewModal(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="flex items-center justify-start gap-2">
|
||||||
|
<TrashIcon size={14} strokeWidth={2} />
|
||||||
|
<span>Delete View</span>
|
||||||
|
</span>
|
||||||
|
</CustomMenu.MenuItem>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<CustomMenu.MenuItem onClick={handleCopyText}>
|
||||||
|
<span className="flex items-center justify-start gap-2">
|
||||||
|
<LinkIcon className="h-3 w-3" />
|
||||||
|
<span>Copy view link</span>
|
||||||
|
</span>
|
||||||
|
</CustomMenu.MenuItem>
|
||||||
|
</CustomMenu>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
26
web/constants/views/filters.ts
Normal file
26
web/constants/views/filters.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { TIssuePriorities, TStateGroups } from "@plane/types";
|
||||||
|
|
||||||
|
// filters constants
|
||||||
|
export const STATE_GROUP_PROPERTY: Record<TStateGroups, { label: string; color: string }> = {
|
||||||
|
backlog: { label: "Backlog", color: "#d9d9d9" },
|
||||||
|
unstarted: { label: "Unstarted", color: "#3f76ff" },
|
||||||
|
started: { label: "Started", color: "#f59e0b" },
|
||||||
|
completed: { label: "Completed", color: "#16a34a" },
|
||||||
|
cancelled: { label: "Canceled", color: "#dc2626" },
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PRIORITIES_PROPERTY: Record<TIssuePriorities, { label: string }> = {
|
||||||
|
urgent: { label: "Urgent" },
|
||||||
|
high: { label: "High" },
|
||||||
|
medium: { label: "Medium" },
|
||||||
|
low: { label: "Low" },
|
||||||
|
none: { label: "None" },
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DATE_PROPERTY: Record<string, { label: string }> = {
|
||||||
|
"1_weeks;after;fromnow": { label: "1 week from now" },
|
||||||
|
"2_weeks;after;fromnow": { label: "2 weeks from now" },
|
||||||
|
"1_months;after;fromnow": { label: "1 month from now" },
|
||||||
|
"2_months;after;fromnow": { label: "2 months from now" },
|
||||||
|
custom: { label: "Custom" },
|
||||||
|
};
|
302
web/hooks/user-view-filters.tsx
Normal file
302
web/hooks/user-view-filters.tsx
Normal file
@ -0,0 +1,302 @@
|
|||||||
|
import { ReactNode } from "react";
|
||||||
|
import { Briefcase, CalendarDays, CircleUser, Tag } from "lucide-react";
|
||||||
|
// hooks
|
||||||
|
import { useProject, useModule, useCycle, useProjectState, useMember, useLabel } from "hooks/store";
|
||||||
|
// ui
|
||||||
|
import {
|
||||||
|
Avatar,
|
||||||
|
ContrastIcon,
|
||||||
|
CycleGroupIcon,
|
||||||
|
DiceIcon,
|
||||||
|
DoubleCircleIcon,
|
||||||
|
PriorityIcon,
|
||||||
|
StateGroupIcon,
|
||||||
|
} from "@plane/ui";
|
||||||
|
// types
|
||||||
|
import { TIssuePriorities, TStateGroups, IIssueFilterOptions } from "@plane/types";
|
||||||
|
// constants
|
||||||
|
import { STATE_GROUP_PROPERTY, PRIORITIES_PROPERTY, DATE_PROPERTY } from "constants/views/filters";
|
||||||
|
// helpers
|
||||||
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
|
import { renderFormattedDate } from "helpers/date-time.helper";
|
||||||
|
|
||||||
|
type TFilterPropertyDetails = {
|
||||||
|
icon: ReactNode;
|
||||||
|
label: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type TFilterPropertyDefaultDetails = {
|
||||||
|
icon: ReactNode;
|
||||||
|
label: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useViewFilter = (workspaceSlug: string, projectId: string | undefined) => {
|
||||||
|
const { getProjectById } = useProject();
|
||||||
|
const { getModuleById } = useModule();
|
||||||
|
const { getCycleById } = useCycle();
|
||||||
|
const { getStateById } = useProjectState();
|
||||||
|
const { getUserDetails } = useMember();
|
||||||
|
const { getLabelById } = useLabel();
|
||||||
|
|
||||||
|
if (!workspaceSlug || !projectId) return undefined;
|
||||||
|
|
||||||
|
const propertyDefaultDetails = (filterKey: keyof IIssueFilterOptions): TFilterPropertyDefaultDetails | undefined => {
|
||||||
|
if (!filterKey) return undefined;
|
||||||
|
|
||||||
|
switch (filterKey) {
|
||||||
|
case "project":
|
||||||
|
return {
|
||||||
|
icon: <Briefcase size={12} />,
|
||||||
|
label: "Projects",
|
||||||
|
};
|
||||||
|
case "module":
|
||||||
|
return {
|
||||||
|
icon: <DiceIcon className="w-3 h-3" />,
|
||||||
|
label: "Modules",
|
||||||
|
};
|
||||||
|
case "cycle":
|
||||||
|
return {
|
||||||
|
icon: <ContrastIcon className="w-3 h-3" />,
|
||||||
|
label: "Cycles",
|
||||||
|
};
|
||||||
|
case "priority":
|
||||||
|
return {
|
||||||
|
icon: <PriorityIcon priority="high" withContainer size={10} />,
|
||||||
|
label: "Priorities",
|
||||||
|
};
|
||||||
|
case "state":
|
||||||
|
return {
|
||||||
|
icon: <DoubleCircleIcon className="w-3 h-3" />,
|
||||||
|
label: "States",
|
||||||
|
};
|
||||||
|
case "state_group":
|
||||||
|
return {
|
||||||
|
icon: <DoubleCircleIcon className="w-3 h-3" />,
|
||||||
|
label: "State Groups",
|
||||||
|
};
|
||||||
|
case "assignees":
|
||||||
|
return {
|
||||||
|
icon: <CircleUser size={12} />,
|
||||||
|
label: "Assignees",
|
||||||
|
};
|
||||||
|
case "mentions":
|
||||||
|
return {
|
||||||
|
icon: <CircleUser size={12} />,
|
||||||
|
label: "Mentions",
|
||||||
|
};
|
||||||
|
case "subscriber":
|
||||||
|
return {
|
||||||
|
icon: <CircleUser size={12} />,
|
||||||
|
label: "Subscribers",
|
||||||
|
};
|
||||||
|
case "created_by":
|
||||||
|
return {
|
||||||
|
icon: <CircleUser size={12} />,
|
||||||
|
label: "Creators",
|
||||||
|
};
|
||||||
|
case "labels":
|
||||||
|
return {
|
||||||
|
icon: <Tag size={12} />,
|
||||||
|
label: "Labels",
|
||||||
|
};
|
||||||
|
case "start_date":
|
||||||
|
return {
|
||||||
|
icon: <CalendarDays size={12} />,
|
||||||
|
label: "Start Dates",
|
||||||
|
};
|
||||||
|
case "target_date":
|
||||||
|
return {
|
||||||
|
icon: <CalendarDays size={12} />,
|
||||||
|
label: "Target Dates",
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const propertyDetails = (
|
||||||
|
filterKey: keyof IIssueFilterOptions,
|
||||||
|
propertyId: string
|
||||||
|
): TFilterPropertyDetails | undefined => {
|
||||||
|
if (!filterKey || !propertyId) return undefined;
|
||||||
|
|
||||||
|
switch (filterKey) {
|
||||||
|
case "project":
|
||||||
|
const projectPropertyDetail = getProjectById(propertyId);
|
||||||
|
if (!projectPropertyDetail) return undefined;
|
||||||
|
return {
|
||||||
|
icon: (
|
||||||
|
<>
|
||||||
|
{projectPropertyDetail?.logo_props?.in_use === "emoji" &&
|
||||||
|
projectPropertyDetail?.logo_props?.emoji?.value ? (
|
||||||
|
<div className="text-xs">
|
||||||
|
{projectPropertyDetail?.logo_props?.emoji.value
|
||||||
|
?.split("-")
|
||||||
|
.map((emoji) => String.fromCodePoint(parseInt(emoji, 10)))}
|
||||||
|
</div>
|
||||||
|
) : projectPropertyDetail?.logo_props?.in_use === "icon" &&
|
||||||
|
projectPropertyDetail?.logo_props?.icon?.name ? (
|
||||||
|
<div
|
||||||
|
className="material-symbols-rounded text-xs"
|
||||||
|
style={{
|
||||||
|
color: projectPropertyDetail?.logo_props?.icon?.color,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{renderEmoji(projectPropertyDetail?.logo_props?.icon?.name)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Briefcase size={12} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
label: projectPropertyDetail.name,
|
||||||
|
};
|
||||||
|
case "module":
|
||||||
|
const modulePropertyDetail = getModuleById(propertyId);
|
||||||
|
if (!modulePropertyDetail) return undefined;
|
||||||
|
return {
|
||||||
|
icon: <DiceIcon className="w-3 h-3" />,
|
||||||
|
label: modulePropertyDetail.name,
|
||||||
|
};
|
||||||
|
case "cycle":
|
||||||
|
const cyclePropertyDetail = getCycleById(propertyId);
|
||||||
|
if (!cyclePropertyDetail) return undefined;
|
||||||
|
return {
|
||||||
|
icon: <CycleGroupIcon cycleGroup={cyclePropertyDetail.status} height="14px" width="14px" />,
|
||||||
|
label: cyclePropertyDetail.name,
|
||||||
|
};
|
||||||
|
case "priority":
|
||||||
|
const priorityPropertyDetail = PRIORITIES_PROPERTY?.[propertyId as TIssuePriorities];
|
||||||
|
if (!priorityPropertyDetail) return undefined;
|
||||||
|
return {
|
||||||
|
icon: <PriorityIcon priority={propertyId as TIssuePriorities} size={10} withContainer />,
|
||||||
|
label: priorityPropertyDetail.label,
|
||||||
|
};
|
||||||
|
case "state":
|
||||||
|
const statePropertyDetail = getStateById(propertyId);
|
||||||
|
if (!statePropertyDetail) return undefined;
|
||||||
|
return {
|
||||||
|
icon: <StateGroupIcon stateGroup={statePropertyDetail.group} />,
|
||||||
|
label: statePropertyDetail.name,
|
||||||
|
};
|
||||||
|
case "state_group":
|
||||||
|
const stateGroupPropertyDetail = STATE_GROUP_PROPERTY?.[propertyId as TStateGroups];
|
||||||
|
if (!stateGroupPropertyDetail) return undefined;
|
||||||
|
return {
|
||||||
|
icon: <StateGroupIcon stateGroup={propertyId as TStateGroups} />,
|
||||||
|
label: stateGroupPropertyDetail.label,
|
||||||
|
};
|
||||||
|
case "assignees":
|
||||||
|
const assigneePropertyDetail = getUserDetails(propertyId);
|
||||||
|
if (!assigneePropertyDetail) return undefined;
|
||||||
|
return {
|
||||||
|
icon: (
|
||||||
|
<Avatar
|
||||||
|
name={assigneePropertyDetail.display_name}
|
||||||
|
src={assigneePropertyDetail.avatar}
|
||||||
|
size={"sm"}
|
||||||
|
showTooltip={false}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
label: assigneePropertyDetail.display_name,
|
||||||
|
};
|
||||||
|
case "mentions":
|
||||||
|
const mentionPropertyDetail = getUserDetails(propertyId);
|
||||||
|
if (!mentionPropertyDetail) return undefined;
|
||||||
|
return {
|
||||||
|
icon: (
|
||||||
|
<Avatar
|
||||||
|
name={mentionPropertyDetail.display_name}
|
||||||
|
src={mentionPropertyDetail.avatar}
|
||||||
|
size={"sm"}
|
||||||
|
showTooltip={false}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
label: mentionPropertyDetail.display_name,
|
||||||
|
};
|
||||||
|
case "subscriber":
|
||||||
|
const subscribedPropertyDetail = getUserDetails(propertyId);
|
||||||
|
if (!subscribedPropertyDetail) return undefined;
|
||||||
|
return {
|
||||||
|
icon: (
|
||||||
|
<Avatar
|
||||||
|
name={subscribedPropertyDetail.display_name}
|
||||||
|
src={subscribedPropertyDetail.avatar}
|
||||||
|
size={"sm"}
|
||||||
|
showTooltip={false}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
label: subscribedPropertyDetail.display_name,
|
||||||
|
};
|
||||||
|
case "created_by":
|
||||||
|
const createdByPropertyDetail = getUserDetails(propertyId);
|
||||||
|
if (!createdByPropertyDetail) return undefined;
|
||||||
|
return {
|
||||||
|
icon: (
|
||||||
|
<Avatar
|
||||||
|
name={createdByPropertyDetail.display_name}
|
||||||
|
src={createdByPropertyDetail.avatar}
|
||||||
|
size={"sm"}
|
||||||
|
showTooltip={false}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
label: createdByPropertyDetail.display_name,
|
||||||
|
};
|
||||||
|
case "labels":
|
||||||
|
const labelPropertyDetail = getLabelById(propertyId);
|
||||||
|
if (!labelPropertyDetail) return undefined;
|
||||||
|
return {
|
||||||
|
icon: (
|
||||||
|
<div
|
||||||
|
className="w-2.5 h-2.5 rounded-full"
|
||||||
|
style={{
|
||||||
|
backgroundColor: labelPropertyDetail.color,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
label: labelPropertyDetail.name,
|
||||||
|
};
|
||||||
|
case "start_date":
|
||||||
|
if (propertyId.includes("-")) {
|
||||||
|
const customDateString = propertyId.split(";");
|
||||||
|
return {
|
||||||
|
icon: <CalendarDays size={12} />,
|
||||||
|
label: `${customDateString[1].charAt(0).toUpperCase()}${customDateString[1].slice(1)} ${renderFormattedDate(
|
||||||
|
customDateString[0]
|
||||||
|
)}`,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const startDatePropertyDetail = DATE_PROPERTY?.[propertyId];
|
||||||
|
if (!startDatePropertyDetail) return undefined;
|
||||||
|
return {
|
||||||
|
icon: <CalendarDays size={12} />,
|
||||||
|
label: startDatePropertyDetail.label,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case "target_date":
|
||||||
|
if (propertyId.includes("-")) {
|
||||||
|
const customDateString = propertyId.split(";");
|
||||||
|
return {
|
||||||
|
icon: <CalendarDays size={12} />,
|
||||||
|
label: `${customDateString[1].charAt(0).toUpperCase()}${customDateString[1].slice(1)} ${renderFormattedDate(
|
||||||
|
customDateString[0]
|
||||||
|
)}`,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const targetDatePropertyDetail = DATE_PROPERTY?.[propertyId];
|
||||||
|
if (!targetDatePropertyDetail) return undefined;
|
||||||
|
return {
|
||||||
|
icon: <CalendarDays size={12} />,
|
||||||
|
label: targetDatePropertyDetail.label,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
propertyDefaultDetails,
|
||||||
|
propertyDetails,
|
||||||
|
};
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user