chore: optimised issue activity and updated the popover component in issue detail and peek overview (#5208)

This commit is contained in:
guru_sainath 2024-07-23 17:54:26 +05:30 committed by GitHub
parent 31fe9a1a02
commit f5027f4268
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 94 additions and 81 deletions

View file

@ -0,0 +1,28 @@
"use client";
import { FC } from "react";
// components
import { ActivityFilter } from "@/components/issues";
// plane web constants
import { TActivityFilters, ACTIVITY_FILTER_TYPE_OPTIONS, TActivityFilterOption } from "@/plane-web/constants/issues";
export type TActivityFilterRoot = {
selectedFilters: TActivityFilters[];
toggleFilter: (filter: TActivityFilters) => void;
};
export const ActivityFilterRoot: FC<TActivityFilterRoot> = (props) => {
const { selectedFilters, toggleFilter } = props;
const filters: TActivityFilterOption[] = Object.entries(ACTIVITY_FILTER_TYPE_OPTIONS).map(([key, value]) => {
const filterKey = key as TActivityFilters;
return {
key: filterKey,
label: value.label,
isSelected: selectedFilters.includes(filterKey),
onClick: () => toggleFilter(filterKey),
};
});
return <ActivityFilter selectedFilters={selectedFilters} filterOptions={filters} />;
};

View file

@ -1,2 +1,4 @@
export * from "./root"; export * from "./root";
export * from "./worklog-create-button"; export * from "./worklog-create-button";
export * from "./filter-root";

View file

@ -18,6 +18,13 @@ export const ACTIVITY_FILTER_TYPE_OPTIONS: Record<EActivityFilterType, { label:
export const defaultActivityFilters: TActivityFilters[] = [EActivityFilterType.ACTIVITY, EActivityFilterType.COMMENT]; export const defaultActivityFilters: TActivityFilters[] = [EActivityFilterType.ACTIVITY, EActivityFilterType.COMMENT];
export type TActivityFilterOption = {
key: EActivityFilterType;
label: string;
isSelected: boolean;
onClick: () => void;
};
export const filterActivityOnSelectedFilters = ( export const filterActivityOnSelectedFilters = (
activity: TIssueActivityComment[], activity: TIssueActivityComment[],
filter: TActivityFilters[] filter: TActivityFilters[]

View file

@ -1,85 +1,59 @@
import React, { FC, Fragment } from "react"; import React, { FC } from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { Check, ListFilter } from "lucide-react"; import { Check, ListFilter } from "lucide-react";
import { Popover, Transition } from "@headlessui/react"; import { Button, PopoverMenu } from "@plane/ui";
// ui
import { Button } from "@plane/ui";
// helper // helper
import { cn } from "@/helpers/common.helper"; import { cn } from "@/helpers/common.helper";
// constants // constants
import { TActivityFilters, ACTIVITY_FILTER_TYPE_OPTIONS } from "@/plane-web/constants/issues"; import { TActivityFilterOption, TActivityFilters } from "@/plane-web/constants/issues";
type Props = { type TActivityFilter = {
selectedFilters: TActivityFilters[]; selectedFilters: TActivityFilters[];
toggleFilter: (filter: TActivityFilters) => void; filterOptions: TActivityFilterOption[];
}; };
export const ActivityFilter: FC<Props> = observer((props) => { export const ActivityFilter: FC<TActivityFilter> = observer((props) => {
const { selectedFilters, toggleFilter } = props; const { selectedFilters = [], filterOptions } = props;
return ( return (
<Popover as="div" className="relative"> <PopoverMenu
{({ open }) => ( buttonClassName="outline-none"
<> button={
<Popover.Button as={React.Fragment}>
<Button <Button
variant="neutral-primary" variant="neutral-primary"
size="sm" size="sm"
prependIcon={<ListFilter className="h-3 w-3" />} prependIcon={<ListFilter className="h-3 w-3" />}
className="relative" className="relative"
> >
<span className={`${open ? "text-custom-text-100" : "text-custom-text-200"}`}>Filters</span> <span className="text-custom-text-200">Filters</span>
</Button> </Button>
</Popover.Button> }
panelClassName="p-2 rounded-md border border-custom-border-200 bg-custom-background-100"
<Transition data={filterOptions}
as={Fragment} keyExtractor={(item) => item.key}
enter="transition ease-out duration-200" render={(item) => (
enterFrom="opacity-0 translate-y-1"
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
>
<Popover.Panel className="absolute mt-2 right-0 z-10 min-w-40">
<div className="p-2 rounded-md border border-custom-border-200 bg-custom-background-100">
{Object.keys(ACTIVITY_FILTER_TYPE_OPTIONS).map((key) => {
const filterKey = key as TActivityFilters;
const filter = ACTIVITY_FILTER_TYPE_OPTIONS[filterKey];
const isSelected = selectedFilters.includes(filterKey);
return (
<div <div
key={filterKey} key={item.key}
className="flex items-center gap-2 text-sm cursor-pointer px-2 p-1 transition-all hover:bg-custom-background-80 rounded-sm" className="flex items-center gap-2 text-sm cursor-pointer px-2 p-1 transition-all hover:bg-custom-background-80 rounded-sm"
onClick={() => toggleFilter(filterKey)} onClick={item.onClick}
> >
<div <div
className={cn( className={cn(
"flex-shrink-0 w-3 h-3 flex justify-center items-center rounded-sm transition-all bg-custom-background-90", "flex-shrink-0 w-3 h-3 flex justify-center items-center rounded-sm transition-all bg-custom-background-90",
{ {
"bg-custom-primary text-white": isSelected, "bg-custom-primary text-white": item.isSelected,
"bg-custom-background-80 text-custom-text-400": isSelected && selectedFilters.length === 1, "bg-custom-background-80 text-custom-text-400": item.isSelected && selectedFilters.length === 1,
"bg-custom-background-90": !isSelected, "bg-custom-background-90": !item.isSelected,
} }
)} )}
> >
{isSelected && <Check className="h-2.5 w-2.5" />} {item.isSelected && <Check className="h-2.5 w-2.5" />}
</div>
<div className={cn("whitespace-nowrap", item.isSelected ? "text-custom-text-100" : "text-custom-text-200")}>
{item.label}
</div>
</div> </div>
<div
className={cn(
"whitespace-nowrap",
isSelected ? "text-custom-text-100" : "text-custom-text-200"
)} )}
> />
{filter.label}
</div>
</div>
);
})}
</div>
</Popover.Panel>
</Transition>
</>
)}
</Popover>
); );
}); });

View file

@ -7,12 +7,12 @@ import { TIssueComment } from "@plane/types";
// ui // ui
import { TOAST_TYPE, setToast } from "@plane/ui"; import { TOAST_TYPE, setToast } from "@plane/ui";
// components // components
import { ActivityFilter, IssueCommentCreate } from "@/components/issues"; import { IssueCommentCreate } from "@/components/issues";
import { IssueActivityCommentRoot } from "@/components/issues/issue-detail"; import { IssueActivityCommentRoot } from "@/components/issues/issue-detail";
// hooks // hooks
import { useIssueDetail, useProject } from "@/hooks/store"; import { useIssueDetail, useProject } from "@/hooks/store";
// plane web components // plane web components
import { IssueActivityWorklogCreateButton } from "@/plane-web/components/issues/worklog"; import { ActivityFilterRoot, IssueActivityWorklogCreateButton } from "@/plane-web/components/issues/worklog";
// plane web constants // plane web constants
import { TActivityFilters, defaultActivityFilters } from "@/plane-web/constants/issues"; import { TActivityFilters, defaultActivityFilters } from "@/plane-web/constants/issues";
@ -120,7 +120,7 @@ export const IssueActivity: FC<TIssueActivity> = observer((props) => {
issueId={issueId} issueId={issueId}
disabled={disabled} disabled={disabled}
/> />
<ActivityFilter selectedFilters={selectedFilters} toggleFilter={toggleFilter} /> <ActivityFilterRoot selectedFilters={selectedFilters} toggleFilter={toggleFilter} />
</div> </div>
</div> </div>

View file

@ -0,0 +1 @@
export * from "ce/components/issues/worklog/activity/filter-root";

View file

@ -1,2 +1,4 @@
export * from "./root"; export * from "./root";
export * from "./worklog-create-button"; export * from "./worklog-create-button";
export * from "./filter-root";

View file

@ -323,14 +323,13 @@ export const convertMinutesToHoursAndMinutes = (mins: number): { hours: number;
}; };
/** /**
* @description converts minutes to days, hours and minutes * @description converts minutes to hours and minutes string
* @param { number } totalMinutes * @param { number } totalMinutes
* @returns { string } days, hours and minutes * @returns { string } 0h 0m
* @example convertMinutesToHoursAndMinutes(150) // Output: 2h 10m
*/ */
export const convertMinutesToDaysHoursMinutes = (totalMinutes: number): string => { export const convertMinutesToHoursMinutesString = (totalMinutes: number): string => {
const days = Math.floor(totalMinutes / (60 * 24)); const { hours, minutes } = convertMinutesToHoursAndMinutes(totalMinutes);
const hours = Math.floor((totalMinutes % (60 * 24)) / 60);
const minutes = totalMinutes % 60;
return `${days ? `${days}d ` : ``}${hours ? `${hours}h ` : ``}${minutes ? `${minutes}m ` : ``} `; return `${hours ? `${hours}h ` : ``}${minutes ? `${minutes}m ` : ``}`;
}; };