diff --git a/web/components/issues/form.tsx b/web/components/issues/form.tsx index 7aff699f5..fdaa0f276 100644 --- a/web/components/issues/form.tsx +++ b/web/components/issues/form.tsx @@ -20,6 +20,8 @@ import { IssuePrioritySelect, IssueProjectSelect, IssueStateSelect, + IssueModuleSelect, + IssueCycleSelect, } from "components/issues/select"; import { CreateStateModal } from "components/states"; import { CreateLabelModal } from "components/labels"; @@ -70,6 +72,8 @@ export interface IssueFormProps { | "estimate" | "parent" | "all" + | "module" + | "cycle" )[]; } @@ -108,7 +112,7 @@ export const IssueForm: FC = observer((props) => { const user = userStore.currentUser; - const editorSuggestion = useEditorSuggestions(workspaceSlug as string | undefined, projectId) + const editorSuggestion = useEditorSuggestions(workspaceSlug as string | undefined, projectId); const { setToastAlert } = useToast(); @@ -488,6 +492,38 @@ export const IssueForm: FC = observer((props) => { /> )} + {(fieldsToShow.includes("all") || fieldsToShow.includes("module")) && ( + ( + { + onChange(val); + }} + /> + )} + /> + )} + {(fieldsToShow.includes("all") || fieldsToShow.includes("cycle")) && ( + ( + { + onChange(val); + }} + /> + )} + /> + )} {(fieldsToShow.includes("all") || fieldsToShow.includes("estimate")) && ( <> = observer((props) => { <> + + + +
+
+ + setQuery(e.target.value)} + placeholder="Search" + displayValue={(assigned: any) => assigned?.name} + /> +
+
+ {filteredOptions ? ( + filteredOptions.length > 0 ? ( + filteredOptions.map((option) => ( + + `flex items-center justify-between gap-2 cursor-pointer select-none truncate rounded px-1 py-1.5 ${ + active && !selected ? "bg-custom-background-80" : "" + } w-full truncate ${selected ? "text-custom-text-100" : "text-custom-text-200"}` + } + > + {({ selected }) => ( + <> + {option.content} + {selected && } + + )} + + )) + ) : ( + +

No matching results

+
+ ) + ) : ( +

Loading...

+ )} +
+
+
+ + ); +}); diff --git a/web/components/issues/select/index.ts b/web/components/issues/select/index.ts index 4f1a20372..40c435bc0 100644 --- a/web/components/issues/select/index.ts +++ b/web/components/issues/select/index.ts @@ -5,3 +5,5 @@ export * from "./label"; export * from "./priority"; export * from "./project"; export * from "./state"; +export * from "./module"; +export * from "./cycle"; diff --git a/web/components/issues/select/module.tsx b/web/components/issues/select/module.tsx new file mode 100644 index 000000000..bb9117d54 --- /dev/null +++ b/web/components/issues/select/module.tsx @@ -0,0 +1,143 @@ +import React, { useState } from "react"; +import { observer } from "mobx-react-lite"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +// popper js +import { usePopper } from "react-popper"; +// ui +import { Combobox } from "@headlessui/react"; +// icons +import { DiceIcon } from "@plane/ui"; +// icons +import { Check, Search } from "lucide-react"; + +export interface IssueModuleSelectProps { + workspaceSlug: string; + projectId: string; + value: string | null; + onChange: (value: string) => void; +} + +export const IssueModuleSelect: React.FC = observer((props) => { + const { workspaceSlug, projectId, value, onChange } = props; + const [query, setQuery] = useState(""); + + const [referenceElement, setReferenceElement] = useState(null); + const [popperElement, setPopperElement] = useState(null); + + const { styles, attributes } = usePopper(referenceElement, popperElement, { + placement: "bottom-start", + }); + + const { module: moduleStore } = useMobxStore(); + + const fetchModules = () => { + if (workspaceSlug && projectId) moduleStore.fetchModules(workspaceSlug, projectId); + }; + + const modules = projectId ? moduleStore.modules[projectId] : undefined; + + const selectedModule = modules ? modules?.find((i) => i.id === value) : undefined; + + const options = modules?.map((module) => ({ + value: module.id, + query: module.name, + content: ( +
+ + + + {module.name} +
+ ), + })); + + const filteredOptions = + query === "" ? options : options?.filter((option) => option.query.toLowerCase().includes(query.toLowerCase())); + + const label = selectedModule ? ( +
+ + + +
{selectedModule.name}
+
+ ) : ( + <> + + Select Module + + ); + + return ( + onChange(val)} + disabled={false} + > + + + + +
+
+ + setQuery(e.target.value)} + placeholder="Search" + displayValue={(assigned: any) => assigned?.name} + /> +
+
+ {filteredOptions ? ( + filteredOptions.length > 0 ? ( + filteredOptions.map((option) => ( + + `flex items-center justify-between gap-2 cursor-pointer select-none truncate rounded px-1 py-1.5 ${ + active && !selected ? "bg-custom-background-80" : "" + } w-full truncate ${selected ? "text-custom-text-100" : "text-custom-text-200"}` + } + > + {({ selected }) => ( + <> + {option.content} + {selected && } + + )} + + )) + ) : ( + +

No matching results

+
+ ) + ) : ( +

Loading...

+ )} +
+
+
+
+ ); +});