diff --git a/web/components/core/modals/bulk-delete-issues-modal-item.tsx b/web/components/core/modals/bulk-delete-issues-modal-item.tsx index 8b03c311b..ee18f15cc 100644 --- a/web/components/core/modals/bulk-delete-issues-modal-item.tsx +++ b/web/components/core/modals/bulk-delete-issues-modal-item.tsx @@ -1,13 +1,18 @@ import { observer } from "mobx-react-lite"; import { Combobox } from "@headlessui/react"; // hooks -import { useProjectState } from "@/hooks/store"; +import { ISearchIssueResponse } from "@plane/types"; -export const BulkDeleteIssuesModalItem: React.FC = observer((props) => { - const { issue, delete_issue_ids, identifier } = props; - const { getStateById } = useProjectState(); +interface Props { + issue: ISearchIssueResponse; + canDeleteIssueIds: boolean; + identifier: string | undefined; +} - const color = getStateById(issue.state_id)?.color; +export const BulkDeleteIssuesModalItem: React.FC = observer((props: Props) => { + const { issue, canDeleteIssueIds, identifier } = props; + + const color = issue.state__color; return ( = observer((props) => { } >
- + = observer((props) => { ))} diff --git a/web/components/inbox/modals/select-duplicate.tsx b/web/components/inbox/modals/select-duplicate.tsx index 740978d91..8cdfc863a 100644 --- a/web/components/inbox/modals/select-duplicate.tsx +++ b/web/components/inbox/modals/select-duplicate.tsx @@ -1,20 +1,21 @@ import React, { useEffect, useState } from "react"; import { useRouter } from "next/router"; -import useSWR from "swr"; import { Search } from "lucide-react"; import { Combobox, Dialog, Transition } from "@headlessui/react"; -// hooks // icons // components +// types +import { ISearchIssueResponse } from "@plane/types"; // ui -import { Button, TOAST_TYPE, setToast } from "@plane/ui"; +import { Button, Loader, TOAST_TYPE, setToast } from "@plane/ui"; import { EmptyState } from "@/components/empty-state"; -// services // constants import { EmptyStateType } from "@/constants/empty-state"; -import { PROJECT_ISSUES_LIST } from "@/constants/fetch-keys"; -import { useProject, useProjectState } from "@/hooks/store"; -import { IssueService } from "@/services/issue"; +// hooks +import { useProject } from "@/hooks/store"; +import useDebounce from "@/hooks/use-debounce"; +// services +import { ProjectService } from "@/services/project"; type Props = { isOpen: boolean; @@ -23,7 +24,7 @@ type Props = { onSubmit: (issueId: string) => void; }; -const issueService = new IssueService(); +const projectService = new ProjectService(); export const SelectDuplicateInboxIssueModal: React.FC = (props) => { const { isOpen, onClose, onSubmit, value } = props; @@ -35,18 +36,27 @@ export const SelectDuplicateInboxIssueModal: React.FC = (props) => { const { workspaceSlug, projectId, issueId } = router.query; // hooks - const { getProjectStates } = useProjectState(); const { getProjectById } = useProject(); - const { data: issues } = useSWR( - workspaceSlug && projectId ? PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string) : null, - workspaceSlug && projectId - ? () => - issueService - .getIssues(workspaceSlug as string, projectId as string) - .then((res) => Object.values(res ?? {}).filter((issue) => issue.id !== issueId)) - : null - ); + const [issues, setIssues] = useState([]); + const [isSearching, setIsSearching] = useState(false); + + const debouncedSearchTerm: string = useDebounce(query, 500); + + useEffect(() => { + if (!isOpen || !workspaceSlug || !projectId) return; + + setIsSearching(true); + projectService + .projectIssuesSearch(workspaceSlug.toString(), projectId.toString(), { + search: debouncedSearchTerm, + workspace_search: false, + }) + .then((res: ISearchIssueResponse[]) => setIssues(res)) + .finally(() => setIsSearching(false)); + }, [debouncedSearchTerm, isOpen, projectId, workspaceSlug]); + + const filteredIssues = issues.filter((issue) => issue.id !== issueId); useEffect(() => { if (!value) { @@ -69,7 +79,52 @@ export const SelectDuplicateInboxIssueModal: React.FC = (props) => { handleClose(); }; - const filteredIssues = (query === "" ? issues : issues?.filter((issue) => issue.name.includes(query))) ?? []; + const issueList = + filteredIssues.length > 0 ? ( +
  • + {query === "" &&

    Select issue

    } +
      + {filteredIssues.map((issue) => { + const stateColor = issue.state__color || ""; + + return ( + + `flex w-full cursor-pointer select-none items-center gap-2 rounded-md px-3 py-2 text-custom-text-200 ${ + active || selected ? "bg-custom-background-80 text-custom-text-100" : "" + } ` + } + > +
      + + + {getProjectById(issue?.project_id)?.identifier}-{issue.sequence_id} + + {issue.name} +
      +
      + ); + })} +
    +
  • + ) : ( +
    + +
    + ); return ( setQuery("")} appear> @@ -122,56 +177,15 @@ export const SelectDuplicateInboxIssueModal: React.FC = (props) => { static className="max-h-80 scroll-py-2 divide-y divide-custom-border-200 overflow-y-auto" > - {filteredIssues.length > 0 ? ( -
  • - {query === "" && ( -

    Select issue

    - )} -
      - {filteredIssues.map((issue) => { - const stateColor = - getProjectStates(issue?.project_id ?? "")?.find((state) => state?.id == issue?.state_id) - ?.color || ""; - - return ( - - `flex w-full cursor-pointer select-none items-center gap-2 rounded-md px-3 py-2 text-custom-text-200 ${ - active || selected ? "bg-custom-background-80 text-custom-text-100" : "" - } ` - } - > -
      - - - {getProjectById(issue?.project_id)?.identifier}-{issue.sequence_id} - - {issue.name} -
      -
      - ); - })} -
    -
  • + {isSearching ? ( + + + + + + ) : ( -
    - -
    + <>{issueList} )} diff --git a/web/components/profile/profile-issues.tsx b/web/components/profile/profile-issues.tsx index 270dff658..89ab8771c 100644 --- a/web/components/profile/profile-issues.tsx +++ b/web/components/profile/profile-issues.tsx @@ -3,13 +3,10 @@ import { observer } from "mobx-react-lite"; import { useRouter } from "next/router"; import useSWR from "swr"; // components -import { EmptyState } from "@/components/empty-state"; import { IssuePeekOverview, ProfileIssuesAppliedFiltersRoot } from "@/components/issues"; import { ProfileIssuesKanBanLayout } from "@/components/issues/issue-layouts/kanban/roots/profile-issues-root"; import { ProfileIssuesListLayout } from "@/components/issues/issue-layouts/list/roots/profile-issues-root"; -import { KanbanLayoutLoader, ListLayoutLoader } from "@/components/ui"; // hooks -import { EMPTY_STATE_DETAILS } from "@/constants/empty-state"; import { EIssuesStoreType } from "@/constants/issue"; import { useIssues } from "@/hooks/store"; // constants @@ -28,7 +25,7 @@ export const ProfileIssuesPage = observer((props: IProfileIssuesPage) => { }; // store hooks const { - issues: { loader, groupedIssueIds, fetchIssues, setViewId }, + issues: { setViewId }, issuesFilter: { issueFilters, fetchFilters }, } = useIssues(EIssuesStoreType.PROFILE); @@ -41,7 +38,6 @@ export const ProfileIssuesPage = observer((props: IProfileIssuesPage) => { async () => { if (workspaceSlug && userId) { await fetchFilters(workspaceSlug, userId); - await fetchIssues(workspaceSlug, undefined, groupedIssueIds ? "mutation" : "init-loader", userId, type); } }, { revalidateIfStale: false, revalidateOnFocus: false } @@ -49,15 +45,6 @@ export const ProfileIssuesPage = observer((props: IProfileIssuesPage) => { const activeLayout = issueFilters?.displayFilters?.layout || undefined; - const emptyStateType = `profile-${type}`; - - if (!groupedIssueIds || loader === "init-loader") - return <>{activeLayout === "list" ? : }; - - if (groupedIssueIds.length === 0) { - return ; - } - return ( <>