chore: peek overview improvement and bug fixes (#2627)

* chore: peekoverview issue properties text size fix

* chore: peekoverview icon updated and active view indicator added

* chore: peekoverview and issue sidebar improvement
This commit is contained in:
Anmol Singh Bhatia 2023-11-03 18:01:34 +05:30 committed by GitHub
parent f639e467f8
commit 7eeac188d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 172 additions and 243 deletions

View File

@ -2,7 +2,7 @@ import * as React from "react";
import { ISvgIcons } from "./type";
export const FullScreenPeekIcon: React.FC<ISvgIcons> = ({
export const CenterPanelIcon: React.FC<ISvgIcons> = ({
className = "text-current",
...rest
}) => (
@ -16,14 +16,18 @@ export const FullScreenPeekIcon: React.FC<ISvgIcons> = ({
>
<path
d="M19 3H5C3.89543 3 3 3.89543 3 5V19C3 20.1046 3.89543 21 5 21H19C20.1046 21 21 20.1046 21 19V5C21 3.89543 20.1046 3 19 3Z"
strokeLinecap="round"
strokeLinejoin="round"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M20 3H12V5V19V21H20C20.5523 21 21 20.1046 21 19V5C21 3.89543 20.5523 3 20 3Z"
d="M15.1111 8.00009H8.8001C8.33334 8.00007 8.00003 8.0001 8.00012 8.88897V15.1111C8.00012 16 8.00012 16 8.8001 16H15.1111C16 16 16 16 16 15.1111V8.88897C16 8.00009 16 8.00009 15.1111 8.00009H15.1111Z"
fill="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
);

View File

@ -2,7 +2,7 @@ import * as React from "react";
import { ISvgIcons } from "./type";
export const ModalPeekIcon: React.FC<ISvgIcons> = ({
export const FullScreenPanelIcon: React.FC<ISvgIcons> = ({
className = "text-current",
...rest
}) => (

View File

@ -9,9 +9,9 @@ export * from "./subscribe-icon";
export * from "./external-link-icon";
export * from "./copy-icon";
export * from "./layer-stack";
export * from "./side-peek-icon";
export * from "./modal-peek-icon";
export * from "./panel-center-icon";
export * from "./side-panel-icon";
export * from "./center-panel-icon";
export * from "./full-screen-panel-icon";
export * from "./priority-icon";
export * from "./state";
export * from "./blocked-icon";

View File

@ -2,7 +2,7 @@ import * as React from "react";
import { ISvgIcons } from "./type";
export const SidePeekIcon: React.FC<ISvgIcons> = ({
export const SidePanelIcon: React.FC<ISvgIcons> = ({
className = "text-current",
...rest
}) => (

View File

@ -24,7 +24,7 @@ import useToast from "hooks/use-toast";
import { CustomDatePicker } from "components/ui";
import { LinkModal, LinksList } from "components/core";
// types
import { ICycle, IIssue, IIssueLink, IModule, TIssuePriorities, linkDetails } from "types";
import { IIssue, IIssueLink, TIssuePriorities, linkDetails } from "types";
import { ISSUE_DETAILS } from "constants/fetch-keys";
// services
import { IssueService } from "services/issue";
@ -42,7 +42,7 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
const [linkModal, setLinkModal] = useState(false);
const [selectedLinkToUpdate, setSelectedLinkToUpdate] = useState<linkDetails | null>(null);
const { user: userStore } = useMobxStore();
const { user: userStore, cycleIssue: cycleIssueStore, moduleIssue: moduleIssueStore } = useMobxStore();
const userRole = userStore.currentProjectRole;
const router = useRouter();
@ -71,11 +71,15 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
const handleParent = (_parent: string) => {
issueUpdate({ ...issue, parent: _parent });
};
const handleCycle = (_cycle: ICycle) => {
issueUpdate({ ...issue, cycle: _cycle.id });
const addIssueToCycle = async (cycleId: string) => {
if (!workspaceSlug || !issue || !cycleId) return;
cycleIssueStore.addIssueToCycle(workspaceSlug.toString(), issue.project_detail.id, cycleId, issue.id);
};
const handleModule = (_module: IModule) => {
issueUpdate({ ...issue, module: _module.id });
const addIssueToModule = async (moduleId: string) => {
if (!workspaceSlug || !issue || !moduleId) return;
moduleIssueStore.addIssueToModule(workspaceSlug.toString(), issue.project_detail.id, moduleId, issue.id);
};
const handleLabels = (formData: Partial<IIssue>) => {
issueUpdate({ ...issue, ...formData });
@ -187,7 +191,7 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
<div className="flex flex-col gap-5 py-5 w-full">
{/* state */}
<div className="flex items-center gap-2 w-full">
<div className="flex items-center gap-2 w-40">
<div className="flex items-center gap-2 w-40 text-sm">
<DoubleCircleIcon className="h-4 w-4 flex-shrink-0" />
<p>State</p>
</div>
@ -198,7 +202,7 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
{/* assignee */}
<div className="flex items-center gap-2 w-full">
<div className="flex items-center gap-2 w-40">
<div className="flex items-center gap-2 w-40 text-sm">
<UserGroupIcon className="h-4 w-4 flex-shrink-0" />
<p>Assignees</p>
</div>
@ -209,7 +213,7 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
{/* priority */}
<div className="flex items-center gap-2 w-full">
<div className="flex items-center gap-2 w-40">
<div className="flex items-center gap-2 w-40 text-sm">
<Signal className="h-4 w-4 flex-shrink-0" />
<p>Priority</p>
</div>
@ -220,7 +224,7 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
{/* estimate */}
<div className="flex items-center gap-2 w-full">
<div className="flex items-center gap-2 w-40">
<div className="flex items-center gap-2 w-40 text-sm">
<Triangle className="h-4 w-4 flex-shrink-0 " />
<p>Estimate</p>
</div>
@ -231,7 +235,7 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
{/* start date */}
<div className="flex items-center gap-2 w-full">
<div className="flex items-center gap-2 w-40">
<div className="flex items-center gap-2 w-40 text-sm">
<CalendarDays className="h-4 w-4 flex-shrink-0" />
<p>Start date</p>
</div>
@ -249,7 +253,7 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
{/* due date */}
<div className="flex items-center gap-2 w-full">
<div className="flex items-center gap-2 w-40">
<div className="flex items-center gap-2 w-40 text-sm">
<CalendarDays className="h-4 w-4 flex-shrink-0" />
<p>Due date</p>
</div>
@ -267,7 +271,7 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
{/* parent */}
<div className="flex items-center gap-2 w-full">
<div className="flex items-center gap-2 w-40">
<div className="flex items-center gap-2 w-40 text-sm">
<User2 className="h-4 w-4 flex-shrink-0" />
<p>Parent</p>
</div>
@ -281,26 +285,26 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
<div className="flex flex-col gap-5 py-5 w-full">
<div className="flex items-center gap-2 w-80">
<div className="flex items-center gap-2 w-40">
<div className="flex items-center gap-2 w-40 text-sm">
<ContrastIcon className="h-4 w-4 flex-shrink-0" />
<p>Cycle</p>
</div>
<div>
<SidebarCycleSelect issueDetail={issue} handleCycleChange={handleCycle} disabled={isNotAllowed} />
<SidebarCycleSelect issueDetail={issue} handleCycleChange={addIssueToCycle} disabled={isNotAllowed} />
</div>
</div>
<div className="flex items-center gap-2 w-80">
<div className="flex items-center gap-2 w-40">
<div className="flex items-center gap-2 w-40 text-sm">
<DiceIcon className="h-4 w-4 flex-shrink-0" />
<p>Module</p>
</div>
<div>
<SidebarModuleSelect issueDetail={issue} handleModuleChange={handleModule} disabled={isNotAllowed} />
<SidebarModuleSelect issueDetail={issue} handleModuleChange={addIssueToModule} disabled={isNotAllowed} />
</div>
</div>
<div className="flex items-start gap-2 w-full">
<div className="flex items-center gap-2 w-40 flex-shrink-0">
<div className="flex items-center gap-2 w-40 text-sm flex-shrink-0">
<Tag className="h-4 w-4 flex-shrink-0" />
<p>Label</p>
</div>
@ -321,8 +325,8 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
<div className="flex flex-col gap-5 pt-5 w-full">
<div className="flex flex-col gap-2 w-full">
<div className="flex items-center gap-2 w-80">
<div className="flex items-center gap-2 w-40">
<Link2 className="h-4 w-4 rotate-45 flex-shrink-0" />
<div className="flex items-center gap-2 w-40 text-sm">
<Link2 className="h-4 w-4 flex-shrink-0" />
<p>Links</p>
</div>
<div>

View File

@ -7,7 +7,7 @@ import useSWR from "swr";
import { PeekOverviewIssueDetails } from "./issue-detail";
import { PeekOverviewProperties } from "./properties";
import { IssueComment } from "./activity";
import { Button, CustomSelect, FullScreenPeekIcon, ModalPeekIcon, SidePeekIcon } from "@plane/ui";
import { Button, CenterPanelIcon, CustomSelect, FullScreenPanelIcon, SidePanelIcon } from "@plane/ui";
import { DeleteIssueModal } from "../delete-issue-modal";
// types
import { IIssue } from "types";
@ -41,17 +41,17 @@ type TPeekModes = "side-peek" | "modal" | "full-screen";
const peekOptions: { key: TPeekModes; icon: any; title: string }[] = [
{
key: "side-peek",
icon: SidePeekIcon,
icon: SidePanelIcon,
title: "Side Peek",
},
{
key: "modal",
icon: ModalPeekIcon,
icon: CenterPanelIcon,
title: "Modal",
},
{
key: "full-screen",
icon: FullScreenPeekIcon,
icon: FullScreenPanelIcon,
title: "Full Screen",
},
];
@ -206,7 +206,13 @@ export const IssueView: FC<IIssueView> = observer((props) => {
>
{peekOptions.map((mode) => (
<CustomSelect.Option key={mode.key} value={mode.key}>
<div className="flex items-center gap-1.5">
<div
className={`flex items-center gap-1.5 ${
currentMode.key === mode.key
? "text-custom-text-200"
: "text-custom-text-400 hover:text-custom-text-200"
}`}
>
<mode.icon className={`h-4 w-4 flex-shrink-0 -my-1 `} />
{mode.title}
</div>

View File

@ -3,9 +3,9 @@ import Link from "next/link";
// hooks
import useToast from "hooks/use-toast";
// ui
import { CustomSelect, FullScreenPeekIcon, ModalPeekIcon, SidePeekIcon } from "@plane/ui";
import { CenterPanelIcon, CustomSelect, FullScreenPanelIcon, SidePanelIcon } from "@plane/ui";
// icons
import { LinkIcon, MoveRight, Trash2 } from "lucide-react";
import { LinkIcon, MoveDiagonal, MoveRight, Trash2 } from "lucide-react";
// helpers
import { copyTextToClipboard } from "helpers/string.helper";
// types
@ -26,15 +26,15 @@ const peekModes: {
icon: any;
label: string;
}[] = [
{ key: "side", icon: SidePeekIcon, label: "Side Peek" },
{ key: "side", icon: SidePanelIcon, label: "Side Peek" },
{
key: "modal",
icon: ModalPeekIcon,
icon: CenterPanelIcon,
label: "Modal Peek",
},
{
key: "full",
icon: FullScreenPeekIcon,
icon: FullScreenPanelIcon,
label: "Full Screen Peek",
},
];
@ -73,7 +73,7 @@ export const PeekOverviewHeader: React.FC<Props> = ({
)}
<Link href={`/${workspaceSlug}/projects/${issue?.project}/issues/${issue?.id}`}>
<a>
<FullScreenPeekIcon className="h-3.5 w-3.5" />
<MoveDiagonal className="h-3.5 w-3.5" />
</a>
</Link>
<CustomSelect

View File

@ -19,11 +19,12 @@ type Props = {
value: string[];
onChange: (value: string[]) => void;
projectId: string;
label?: JSX.Element;
};
const issueLabelService = new IssueLabelService();
export const IssueLabelSelect: React.FC<Props> = ({ setIsOpen, value, onChange, projectId }) => {
export const IssueLabelSelect: React.FC<Props> = ({ setIsOpen, value, onChange, projectId, label }) => {
// states
const [query, setQuery] = useState("");
@ -56,7 +57,9 @@ export const IssueLabelSelect: React.FC<Props> = ({ setIsOpen, value, onChange,
ref={setReferenceElement}
className="flex items-center gap-2 cursor-pointer text-xs text-custom-text-200"
>
{value && value.length > 0 ? (
{label ? (
label
) : value && value.length > 0 ? (
<span className="flex items-center justify-center gap-2 text-xs">
<IssueLabelsList
labels={value.map((v) => issueLabels?.find((l) => l.id === v)) ?? []}

View File

@ -8,17 +8,15 @@ import useSWR, { mutate } from "swr";
import { IssueService } from "services/issue";
import { CycleService } from "services/cycle.service";
// ui
import { CustomSelect, Spinner, Tooltip } from "@plane/ui";
// helper
import { truncateText } from "helpers/string.helper";
import { ContrastIcon, CustomSearchSelect, Tooltip } from "@plane/ui";
// types
import { ICycle, IIssue } from "types";
import { IIssue } from "types";
// fetch-keys
import { CYCLE_ISSUES, INCOMPLETE_CYCLES_LIST, ISSUE_DETAILS } from "constants/fetch-keys";
type Props = {
issueDetail: IIssue | undefined;
handleCycleChange: (cycle: ICycle) => void;
handleCycleChange: (cycleId: string) => void;
disabled?: boolean;
};
@ -52,10 +50,30 @@ export const SidebarCycleSelect: React.FC<Props> = ({ issueDetail, handleCycleCh
});
};
const options = incompleteCycles?.map((cycle) => ({
value: cycle.id,
query: cycle.name,
content: (
<div className="flex items-center gap-1.5 truncate">
<span className="flex justify-center items-center flex-shrink-0 w-3.5 h-3.5">
<ContrastIcon />
</span>
<span className="truncate flex-grow">{cycle.name}</span>
</div>
),
}));
const issueCycle = issueDetail?.issue_cycle;
return (
<CustomSelect
<CustomSearchSelect
value={issueCycle?.cycle_detail.id}
onChange={(value: any) => {
value === issueCycle?.cycle_detail.id
? removeIssueFromCycle(issueCycle?.id ?? "", issueCycle?.cycle ?? "")
: handleCycleChange(value);
}}
options={options}
customButton={
<div>
<Tooltip position="left" tooltipContent={`${issueCycle ? issueCycle.cycle_detail.name : "No cycle"}`}>
@ -65,41 +83,21 @@ export const SidebarCycleSelect: React.FC<Props> = ({ issueDetail, handleCycleCh
disabled ? "cursor-not-allowed" : ""
}`}
>
<span className={`truncate ${issueCycle ? "text-custom-text-100" : "text-custom-text-200"}`}>
<span
className={`flex items-center gap-1.5 truncate ${
issueCycle ? "text-custom-text-100" : "text-custom-text-200"
}`}
>
{issueCycle && <ContrastIcon className="h-3.5 w-3.5" />}
{issueCycle ? issueCycle.cycle_detail.name : "No cycle"}
</span>
</button>
</Tooltip>
</div>
}
value={issueCycle ? issueCycle.cycle_detail.id : null}
onChange={(value: any) => {
!value
? removeIssueFromCycle(issueCycle?.id ?? "", issueCycle?.cycle ?? "")
: handleCycleChange(incompleteCycles?.find((c) => c.id === value) as ICycle);
}}
width="w-full"
maxHeight="rg"
width="max-w-[10rem]"
noChevron
disabled={disabled}
>
{incompleteCycles ? (
incompleteCycles.length > 0 ? (
<>
{incompleteCycles.map((option) => (
<CustomSelect.Option key={option.id} value={option.id}>
<Tooltip position="left-bottom" tooltipContent={option.name}>
<span className="w-full truncate">{truncateText(option.name, 25)}</span>
</Tooltip>
</CustomSelect.Option>
))}
<CustomSelect.Option value={null}>None</CustomSelect.Option>
</>
) : (
<div className="text-center">No cycles found</div>
)
) : (
<Spinner />
)}
</CustomSelect>
/>
);
};

View File

@ -4,15 +4,16 @@ import useSWR from "swr";
import { Controller, useForm } from "react-hook-form";
import { TwitterPicker } from "react-color";
// headless ui
import { Listbox, Popover, Transition } from "@headlessui/react";
import { Popover, Transition } from "@headlessui/react";
// services
import { IssueLabelService } from "services/issue";
// hooks
import useUser from "hooks/use-user";
// ui
import { Input, Spinner } from "@plane/ui";
import { Input } from "@plane/ui";
import { IssueLabelSelect } from "../select";
// icons
import { Component, Plus, X } from "lucide-react";
import { Plus, X } from "lucide-react";
// types
import { IIssue, IIssueLabels } from "types";
// fetch-keys
@ -117,102 +118,21 @@ export const SidebarLabelSelect: React.FC<Props> = ({
</span>
);
})}
<Listbox
as="div"
<IssueLabelSelect
setIsOpen={setCreateLabelForm}
value={issueDetails?.labels ?? []}
onChange={(val: any) => submitChanges({ labels: val })}
className="flex-shrink-0"
multiple
disabled={isNotAllowed || uneditable}
>
{({ open }) => (
<div className="relative">
<Listbox.Button
projectId={issueDetails?.project_detail.id ?? ""}
label={
<span
className={`flex ${
isNotAllowed || uneditable ? "cursor-not-allowed" : "cursor-pointer hover:bg-custom-background-90"
} items-center gap-2 rounded-2xl border border-custom-border-100 px-2 py-0.5 text-xs hover:text-custom-text-200 text-custom-text-300`}
>
Select Label
</Listbox.Button>
<Transition
show={open}
as={React.Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options className="absolute right-0 z-10 mt-1 max-h-28 w-40 overflow-auto rounded-md bg-custom-background-80 py-1 text-xs shadow-lg border border-custom-border-100 focus:outline-none">
<div className="py-1">
{issueLabels ? (
issueLabels.length > 0 ? (
issueLabels.map((label: IIssueLabels) => {
const children = issueLabels?.filter((l) => l.parent === label.id);
if (children.length === 0) {
if (!label.parent)
return (
<Listbox.Option
key={label.id}
className={({ active, selected }) =>
`${active || selected ? "bg-custom-background-90" : ""} ${
selected ? "" : "text-custom-text-200"
} flex cursor-pointer select-none items-center gap-2 truncate p-2`
</span>
}
value={label.id}
>
<span
className="h-2 w-2 flex-shrink-0 rounded-full"
style={{
backgroundColor: label.color && label.color !== "" ? label.color : "#000",
}}
/>
{label.name}
</Listbox.Option>
);
} else
return (
<div className="border-y border-custom-border-100 bg-custom-background-90">
<div className="flex select-none items-center gap-2 truncate p-2 font-medium text-custom-text-100">
<Component className="h-3 w-3" />
{label.name}
</div>
<div>
{children.map((child) => (
<Listbox.Option
key={child.id}
className={({ active, selected }) =>
`${active || selected ? "bg-custom-background-100" : ""} ${
selected ? "" : "text-custom-text-200"
} flex cursor-pointer select-none items-center gap-2 truncate p-2`
}
value={child.id}
>
<span
className="h-2 w-2 flex-shrink-0 rounded-full"
style={{
backgroundColor: child?.color ?? "black",
}}
/>
{child.name}
</Listbox.Option>
))}
</div>
</div>
);
})
) : (
<div className="text-center">No labels found</div>
)
) : (
<Spinner />
)}
</div>
</Listbox.Options>
</Transition>
</div>
)}
</Listbox>
{!isNotAllowed && (
<button
type="button"

View File

@ -4,17 +4,15 @@ import useSWR, { mutate } from "swr";
// services
import { ModuleService } from "services/module.service";
// ui
import { CustomSelect, Spinner, Tooltip } from "@plane/ui";
// helper
import { truncateText } from "helpers/string.helper";
import { CustomSearchSelect, DiceIcon, Tooltip } from "@plane/ui";
// types
import { IIssue, IModule } from "types";
import { IIssue } from "types";
// fetch-keys
import { ISSUE_DETAILS, MODULE_ISSUES, MODULE_LIST } from "constants/fetch-keys";
type Props = {
issueDetail: IIssue | undefined;
handleModuleChange: (module: IModule) => void;
handleModuleChange: (moduleId: string) => void;
disabled?: boolean;
};
@ -44,10 +42,30 @@ export const SidebarModuleSelect: React.FC<Props> = ({ issueDetail, handleModule
});
};
const options = modules?.map((module) => ({
value: module.id,
query: module.name,
content: (
<div className="flex items-center gap-1.5 truncate">
<span className="flex justify-center items-center flex-shrink-0 w-3.5 h-3.5">
<DiceIcon />
</span>
<span className="truncate flex-grow">{module.name}</span>
</div>
),
}));
const issueModule = issueDetail?.issue_module;
return (
<CustomSelect
<CustomSearchSelect
value={issueModule?.module_detail.id}
onChange={(value: any) => {
value === issueModule?.module_detail.id
? removeIssueFromModule(issueModule?.id ?? "", issueModule?.module ?? "")
: handleModuleChange(value);
}}
options={options}
customButton={
<div>
<Tooltip
@ -60,41 +78,21 @@ export const SidebarModuleSelect: React.FC<Props> = ({ issueDetail, handleModule
disabled ? "cursor-not-allowed" : ""
}`}
>
<span className={`truncate ${issueModule ? "text-custom-text-100" : "text-custom-text-200"}`}>
<span
className={`flex items-center gap-1.5 truncate ${
issueModule ? "text-custom-text-100" : "text-custom-text-200"
}`}
>
{issueModule && <DiceIcon className="h-3.5 w-3.5" />}
{modules?.find((m) => m.id === issueModule?.module)?.name ?? "No module"}
</span>
</button>
</Tooltip>
</div>
}
value={issueModule ? issueModule.module_detail?.id : null}
onChange={(value: any) => {
!value
? removeIssueFromModule(issueModule?.id ?? "", issueModule?.module ?? "")
: handleModuleChange(modules?.find((m) => m.id === value) as IModule);
}}
width="w-full"
maxHeight="rg"
width="max-w-[10rem]"
noChevron
disabled={disabled}
>
{modules ? (
modules.length > 0 ? (
<>
{modules.map((option) => (
<CustomSelect.Option key={option.id} value={option.id}>
<Tooltip position="left-bottom" tooltipContent={option.name}>
<span className="w-full truncate">{truncateText(option.name, 25)}</span>
</Tooltip>
</CustomSelect.Option>
))}
<CustomSelect.Option value={null}>None</CustomSelect.Option>
</>
) : (
<div className="text-center">No modules found</div>
)
) : (
<Spinner />
)}
</CustomSelect>
/>
);
};

View File

@ -7,7 +7,7 @@ import useSWR from "swr";
// services
import { ProjectStateService } from "services/project";
// ui
import { CustomSelect, Spinner, StateGroupIcon } from "@plane/ui";
import { CustomSearchSelect, StateGroupIcon } from "@plane/ui";
// helpers
import { getStatesList } from "helpers/state.helper";
import { addSpaceIfCamelCase } from "helpers/string.helper";
@ -33,16 +33,30 @@ export const SidebarStateSelect: React.FC<Props> = ({ value, onChange, disabled
);
const states = getStatesList(stateGroups);
const selectedState = states?.find((s) => s.id === value);
const selectedOption = states?.find((s) => s.id === value);
const options = states?.map((state) => ({
value: state.id,
query: state.name,
content: (
<div className="flex items-center gap-2">
<StateGroupIcon stateGroup={state.group} color={state.color} />
{state.name}
</div>
),
}));
return (
<CustomSelect
<CustomSearchSelect
value={value}
onChange={onChange}
options={options}
customButton={
<div className="bg-custom-background-80 text-xs rounded px-2.5 py-0.5">
{selectedState ? (
{selectedOption ? (
<div className="flex items-center gap-1.5 text-left text-custom-text-100">
<StateGroupIcon stateGroup={selectedState.group} color={selectedState.color} />
{addSpaceIfCamelCase(selectedState?.name ?? "")}
<StateGroupIcon stateGroup={selectedOption.group} color={selectedOption.color} />
{addSpaceIfCamelCase(selectedOption?.name ?? "")}
</div>
) : inboxIssueId ? (
<div className="flex items-center gap-1.5 text-left text-custom-text-100">
@ -54,27 +68,9 @@ export const SidebarStateSelect: React.FC<Props> = ({ value, onChange, disabled
)}
</div>
}
value={value}
onChange={onChange}
optionsClassName="w-min"
width="min-w-[10rem]"
noChevron
disabled={disabled}
>
{states ? (
states.length > 0 ? (
states.map((state) => (
<CustomSelect.Option key={state.id} value={state.id}>
<>
<StateGroupIcon stateGroup={state.group} color={state.color} />
{state.name}
</>
</CustomSelect.Option>
))
) : (
<div className="text-center">No states found</div>
)
) : (
<Spinner />
)}
</CustomSelect>
/>
);
};

View File

@ -37,7 +37,7 @@ import { ContrastIcon, DiceIcon, DoubleCircleIcon, UserGroupIcon } from "@plane/
// helpers
import { copyTextToClipboard } from "helpers/string.helper";
// types
import type { ICycle, IIssue, IIssueLink, linkDetails, IModule } from "types";
import type { IIssue, IIssueLink, linkDetails } from "types";
// fetch-keys
import { ISSUE_DETAILS, PROJECT_ISSUES_ACTIVITY } from "constants/fetch-keys";
@ -97,14 +97,14 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
const { setToastAlert } = useToast();
const handleCycleChange = useCallback(
(cycleDetails: ICycle) => {
(cycleId: string) => {
if (!workspaceSlug || !projectId || !issueDetail || !user) return;
issueService
.addIssueToCycle(
workspaceSlug as string,
projectId as string,
cycleDetails.id,
cycleId,
{
issues: [issueDetail.id],
},
@ -118,14 +118,14 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
);
const handleModuleChange = useCallback(
(moduleDetail: IModule) => {
(moduleId: string) => {
if (!workspaceSlug || !projectId || !issueDetail || !user) return;
moduleService
.addIssuesToModule(
workspaceSlug as string,
projectId as string,
moduleDetail.id,
moduleId,
{
issues: [issueDetail.id],
},