diff --git a/packages/ui/src/icons/panel-center-icon.tsx b/packages/ui/src/icons/center-panel-icon.tsx similarity index 50% rename from packages/ui/src/icons/panel-center-icon.tsx rename to packages/ui/src/icons/center-panel-icon.tsx index 4546474e4..321b61b88 100644 --- a/packages/ui/src/icons/panel-center-icon.tsx +++ b/packages/ui/src/icons/center-panel-icon.tsx @@ -2,7 +2,7 @@ import * as React from "react"; import { ISvgIcons } from "./type"; -export const FullScreenPeekIcon: React.FC = ({ +export const CenterPanelIcon: React.FC = ({ className = "text-current", ...rest }) => ( @@ -16,14 +16,18 @@ export const FullScreenPeekIcon: React.FC = ({ > ); diff --git a/packages/ui/src/icons/modal-peek-icon.tsx b/packages/ui/src/icons/full-screen-panel-icon.tsx similarity index 93% rename from packages/ui/src/icons/modal-peek-icon.tsx rename to packages/ui/src/icons/full-screen-panel-icon.tsx index 9df00f54a..da21aca1f 100644 --- a/packages/ui/src/icons/modal-peek-icon.tsx +++ b/packages/ui/src/icons/full-screen-panel-icon.tsx @@ -2,7 +2,7 @@ import * as React from "react"; import { ISvgIcons } from "./type"; -export const ModalPeekIcon: React.FC = ({ +export const FullScreenPanelIcon: React.FC = ({ className = "text-current", ...rest }) => ( diff --git a/packages/ui/src/icons/index.ts b/packages/ui/src/icons/index.ts index 518a4bfad..4cb4e30f7 100644 --- a/packages/ui/src/icons/index.ts +++ b/packages/ui/src/icons/index.ts @@ -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"; diff --git a/packages/ui/src/icons/side-peek-icon.tsx b/packages/ui/src/icons/side-panel-icon.tsx similarity index 92% rename from packages/ui/src/icons/side-peek-icon.tsx rename to packages/ui/src/icons/side-panel-icon.tsx index de4103a1a..8185da768 100644 --- a/packages/ui/src/icons/side-peek-icon.tsx +++ b/packages/ui/src/icons/side-panel-icon.tsx @@ -2,7 +2,7 @@ import * as React from "react"; import { ISvgIcons } from "./type"; -export const SidePeekIcon: React.FC = ({ +export const SidePanelIcon: React.FC = ({ className = "text-current", ...rest }) => ( diff --git a/web/components/issues/issue-peek-overview/properties.tsx b/web/components/issues/issue-peek-overview/properties.tsx index d1d609f7b..8de442aa0 100644 --- a/web/components/issues/issue-peek-overview/properties.tsx +++ b/web/components/issues/issue-peek-overview/properties.tsx @@ -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 = observer((pro const [linkModal, setLinkModal] = useState(false); const [selectedLinkToUpdate, setSelectedLinkToUpdate] = useState(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 = 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) => { issueUpdate({ ...issue, ...formData }); @@ -187,7 +191,7 @@ export const PeekOverviewProperties: FC = observer((pro
{/* state */}
-
+

State

@@ -198,7 +202,7 @@ export const PeekOverviewProperties: FC = observer((pro {/* assignee */}
-
+

Assignees

@@ -209,7 +213,7 @@ export const PeekOverviewProperties: FC = observer((pro {/* priority */}
-
+

Priority

@@ -220,7 +224,7 @@ export const PeekOverviewProperties: FC = observer((pro {/* estimate */}
-
+

Estimate

@@ -231,7 +235,7 @@ export const PeekOverviewProperties: FC = observer((pro {/* start date */}
-
+

Start date

@@ -249,7 +253,7 @@ export const PeekOverviewProperties: FC = observer((pro {/* due date */}
-
+

Due date

@@ -267,7 +271,7 @@ export const PeekOverviewProperties: FC = observer((pro {/* parent */}
-
+

Parent

@@ -281,26 +285,26 @@ export const PeekOverviewProperties: FC = observer((pro
-
+

Cycle

- +
-
+

Module

- +
-
+

Label

@@ -321,8 +325,8 @@ export const PeekOverviewProperties: FC = observer((pro
-
- +
+

Links

diff --git a/web/components/issues/issue-peek-overview/view.tsx b/web/components/issues/issue-peek-overview/view.tsx index 83eb7737a..95ab28f2a 100644 --- a/web/components/issues/issue-peek-overview/view.tsx +++ b/web/components/issues/issue-peek-overview/view.tsx @@ -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 = observer((props) => { > {peekOptions.map((mode) => ( -
+
{mode.title}
diff --git a/web/components/issues/peek-overview/header.tsx b/web/components/issues/peek-overview/header.tsx index d271533af..55586ddf8 100644 --- a/web/components/issues/peek-overview/header.tsx +++ b/web/components/issues/peek-overview/header.tsx @@ -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 = ({ )} - + void; projectId: string; + label?: JSX.Element; }; const issueLabelService = new IssueLabelService(); -export const IssueLabelSelect: React.FC = ({ setIsOpen, value, onChange, projectId }) => { +export const IssueLabelSelect: React.FC = ({ setIsOpen, value, onChange, projectId, label }) => { // states const [query, setQuery] = useState(""); @@ -56,7 +57,9 @@ export const IssueLabelSelect: React.FC = ({ 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 ? ( issueLabels?.find((l) => l.id === v)) ?? []} diff --git a/web/components/issues/sidebar-select/cycle.tsx b/web/components/issues/sidebar-select/cycle.tsx index 5f4ef6e13..ff37cc68f 100644 --- a/web/components/issues/sidebar-select/cycle.tsx +++ b/web/components/issues/sidebar-select/cycle.tsx @@ -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 = ({ issueDetail, handleCycleCh }); }; + const options = incompleteCycles?.map((cycle) => ({ + value: cycle.id, + query: cycle.name, + content: ( +
+ + + + {cycle.name} +
+ ), + })); + const issueCycle = issueDetail?.issue_cycle; return ( - { + value === issueCycle?.cycle_detail.id + ? removeIssueFromCycle(issueCycle?.id ?? "", issueCycle?.cycle ?? "") + : handleCycleChange(value); + }} + options={options} customButton={
@@ -65,41 +83,21 @@ export const SidebarCycleSelect: React.FC = ({ issueDetail, handleCycleCh disabled ? "cursor-not-allowed" : "" }`} > - + + {issueCycle && } {issueCycle ? issueCycle.cycle_detail.name : "No cycle"}
} - 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) => ( - - - {truncateText(option.name, 25)} - - - ))} - None - - ) : ( -
No cycles found
- ) - ) : ( - - )} -
+ /> ); }; diff --git a/web/components/issues/sidebar-select/label.tsx b/web/components/issues/sidebar-select/label.tsx index 6cd0cff2a..464554aec 100644 --- a/web/components/issues/sidebar-select/label.tsx +++ b/web/components/issues/sidebar-select/label.tsx @@ -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 = ({
); })} - submitChanges({ labels: val })} - className="flex-shrink-0" - multiple - disabled={isNotAllowed || uneditable} - > - {({ open }) => ( -
- - Select Label - - - - -
- {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 ( - - `${active || selected ? "bg-custom-background-90" : ""} ${ - selected ? "" : "text-custom-text-200" - } flex cursor-pointer select-none items-center gap-2 truncate p-2` - } - value={label.id} - > - - {label.name} - - ); - } else - return ( -
-
- - {label.name} -
-
- {children.map((child) => ( - - `${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} - > - - {child.name} - - ))} -
-
- ); - }) - ) : ( -
No labels found
- ) - ) : ( - - )} -
-
-
-
- )} -
+ projectId={issueDetails?.project_detail.id ?? ""} + label={ + + Select Label + + } + /> {!isNotAllowed && (
} - 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) => ( - - - {truncateText(option.name, 25)} - - - ))} - None - - ) : ( -
No modules found
- ) - ) : ( - - )} - + /> ); }; diff --git a/web/components/issues/sidebar-select/state.tsx b/web/components/issues/sidebar-select/state.tsx index 730da4b5b..a109ddab9 100644 --- a/web/components/issues/sidebar-select/state.tsx +++ b/web/components/issues/sidebar-select/state.tsx @@ -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 = ({ 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: ( +
+ + {state.name} +
+ ), + })); return ( - - {selectedState ? ( + {selectedOption ? (
- - {addSpaceIfCamelCase(selectedState?.name ?? "")} + + {addSpaceIfCamelCase(selectedOption?.name ?? "")}
) : inboxIssueId ? (
@@ -54,27 +68,9 @@ export const SidebarStateSelect: React.FC = ({ value, onChange, disabled )}
} - value={value} - onChange={onChange} - optionsClassName="w-min" + width="min-w-[10rem]" + noChevron disabled={disabled} - > - {states ? ( - states.length > 0 ? ( - states.map((state) => ( - - <> - - {state.name} - - - )) - ) : ( -
No states found
- ) - ) : ( - - )} -
+ /> ); }; diff --git a/web/components/issues/sidebar.tsx b/web/components/issues/sidebar.tsx index 5ce7ff59a..3ef335309 100644 --- a/web/components/issues/sidebar.tsx +++ b/web/components/issues/sidebar.tsx @@ -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 = 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 = 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], },