mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
chore: use new cycle and module store. (#3172)
* chore: use new cycle and module store. * chore: minor improvements.
This commit is contained in:
parent
cad2706286
commit
9595493c42
@ -1,9 +1,9 @@
|
|||||||
import { useCallback, useState } from "react";
|
import React, { useCallback, useState } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
// hooks
|
// hooks
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
import { useApplication, useLabel, useProject, useProjectState, useUser } from "hooks/store";
|
import { useApplication, useCycle, useLabel, useProject, useProjectState, useUser } from "hooks/store";
|
||||||
import useLocalStorage from "hooks/use-local-storage";
|
import useLocalStorage from "hooks/use-local-storage";
|
||||||
// components
|
// components
|
||||||
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
|
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
|
||||||
@ -23,6 +23,30 @@ import { EFilterType } from "store_legacy/issues/types";
|
|||||||
import { EProjectStore } from "store_legacy/command-palette.store";
|
import { EProjectStore } from "store_legacy/command-palette.store";
|
||||||
import { EUserProjectRoles } from "constants/project";
|
import { EUserProjectRoles } from "constants/project";
|
||||||
|
|
||||||
|
const CycleDropdownOption: React.FC<{ cycleId: string }> = ({ cycleId }) => {
|
||||||
|
// router
|
||||||
|
const router = useRouter();
|
||||||
|
const { workspaceSlug, projectId } = router.query;
|
||||||
|
// store hooks
|
||||||
|
const { getCycleById } = useCycle();
|
||||||
|
// derived values
|
||||||
|
const cycle = getCycleById(cycleId);
|
||||||
|
|
||||||
|
if (!cycle) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CustomMenu.MenuItem
|
||||||
|
key={cycle.id}
|
||||||
|
onClick={() => router.push(`/${workspaceSlug}/projects/${projectId}/cycles/${cycle.id}`)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-1.5">
|
||||||
|
<ContrastIcon className="h-3 w-3" />
|
||||||
|
{truncateText(cycle.name, 40)}
|
||||||
|
</div>
|
||||||
|
</CustomMenu.MenuItem>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const CycleIssuesHeader: React.FC = observer(() => {
|
export const CycleIssuesHeader: React.FC = observer(() => {
|
||||||
// states
|
// states
|
||||||
const [analyticsModal, setAnalyticsModal] = useState(false);
|
const [analyticsModal, setAnalyticsModal] = useState(false);
|
||||||
@ -35,11 +59,11 @@ export const CycleIssuesHeader: React.FC = observer(() => {
|
|||||||
};
|
};
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const {
|
||||||
cycle: cycleStore,
|
|
||||||
projectIssuesFilter: projectIssueFiltersStore,
|
projectIssuesFilter: projectIssueFiltersStore,
|
||||||
projectMember: { projectMembers },
|
projectMember: { projectMembers },
|
||||||
cycleIssuesFilter: { issueFilters, updateFilters },
|
cycleIssuesFilter: { issueFilters, updateFilters },
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
const { projectAllCycles, getCycleById } = useCycle();
|
||||||
const {
|
const {
|
||||||
commandPalette: { toggleCreateIssueModal },
|
commandPalette: { toggleCreateIssueModal },
|
||||||
eventTracker: { setTrackElement },
|
eventTracker: { setTrackElement },
|
||||||
@ -105,9 +129,8 @@ export const CycleIssuesHeader: React.FC = observer(() => {
|
|||||||
[workspaceSlug, projectId, cycleId, updateFilters]
|
[workspaceSlug, projectId, cycleId, updateFilters]
|
||||||
);
|
);
|
||||||
|
|
||||||
const cyclesList = cycleStore.projectCycles;
|
// derived values
|
||||||
const cycleDetails = cycleId ? cycleStore.getCycleById(cycleId.toString()) : undefined;
|
const cycleDetails = cycleId ? getCycleById(cycleId.toString()) : undefined;
|
||||||
|
|
||||||
const canUserCreateIssue =
|
const canUserCreateIssue =
|
||||||
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
|
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
|
||||||
|
|
||||||
@ -157,16 +180,8 @@ export const CycleIssuesHeader: React.FC = observer(() => {
|
|||||||
width="auto"
|
width="auto"
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
>
|
>
|
||||||
{cyclesList?.map((cycle) => (
|
{projectAllCycles?.map((cycleId) => (
|
||||||
<CustomMenu.MenuItem
|
<CycleDropdownOption key={cycleId} cycleId={cycleId} />
|
||||||
key={cycle.id}
|
|
||||||
onClick={() => router.push(`/${workspaceSlug}/projects/${projectId}/cycles/${cycle.id}`)}
|
|
||||||
>
|
|
||||||
<div className="flex items-center gap-1.5">
|
|
||||||
<ContrastIcon className="h-3 w-3" />
|
|
||||||
{truncateText(cycle.name, 40)}
|
|
||||||
</div>
|
|
||||||
</CustomMenu.MenuItem>
|
|
||||||
))}
|
))}
|
||||||
</CustomMenu>
|
</CustomMenu>
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ import { useRouter } from "next/router";
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
// hooks
|
// hooks
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
import { useApplication, useLabel, useProject, useProjectState, useUser } from "hooks/store";
|
import { useApplication, useLabel, useModule, useProject, useProjectState, useUser } from "hooks/store";
|
||||||
import useLocalStorage from "hooks/use-local-storage";
|
import useLocalStorage from "hooks/use-local-storage";
|
||||||
// components
|
// components
|
||||||
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
|
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
|
||||||
@ -24,6 +24,30 @@ import { EFilterType } from "store_legacy/issues/types";
|
|||||||
import { EProjectStore } from "store_legacy/command-palette.store";
|
import { EProjectStore } from "store_legacy/command-palette.store";
|
||||||
import { EUserProjectRoles } from "constants/project";
|
import { EUserProjectRoles } from "constants/project";
|
||||||
|
|
||||||
|
const ModuleDropdownOption: React.FC<{ moduleId: string }> = ({ moduleId }) => {
|
||||||
|
// router
|
||||||
|
const router = useRouter();
|
||||||
|
const { workspaceSlug, projectId } = router.query;
|
||||||
|
// store hooks
|
||||||
|
const { getModuleById } = useModule();
|
||||||
|
// derived values
|
||||||
|
const moduleDetail = getModuleById(moduleId);
|
||||||
|
|
||||||
|
if (!moduleDetail) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CustomMenu.MenuItem
|
||||||
|
key={moduleDetail.id}
|
||||||
|
onClick={() => router.push(`/${workspaceSlug}/projects/${projectId}/modules/${moduleDetail.id}`)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-1.5">
|
||||||
|
<DiceIcon className="h-3 w-3" />
|
||||||
|
{truncateText(moduleDetail.name, 40)}
|
||||||
|
</div>
|
||||||
|
</CustomMenu.MenuItem>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const ModuleIssuesHeader: React.FC = observer(() => {
|
export const ModuleIssuesHeader: React.FC = observer(() => {
|
||||||
// states
|
// states
|
||||||
const [analyticsModal, setAnalyticsModal] = useState(false);
|
const [analyticsModal, setAnalyticsModal] = useState(false);
|
||||||
@ -36,10 +60,10 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
|
|||||||
};
|
};
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const {
|
||||||
module: moduleStore,
|
|
||||||
projectMember: { projectMembers },
|
projectMember: { projectMembers },
|
||||||
moduleIssuesFilter: { issueFilters, updateFilters },
|
moduleIssuesFilter: { issueFilters, updateFilters },
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
const { projectModules, getModuleById } = useModule();
|
||||||
const {
|
const {
|
||||||
commandPalette: { toggleCreateIssueModal },
|
commandPalette: { toggleCreateIssueModal },
|
||||||
eventTracker: { setTrackElement },
|
eventTracker: { setTrackElement },
|
||||||
@ -105,9 +129,8 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
|
|||||||
[workspaceSlug, projectId, moduleId, updateFilters]
|
[workspaceSlug, projectId, moduleId, updateFilters]
|
||||||
);
|
);
|
||||||
|
|
||||||
const modulesList = projectId ? moduleStore.modules[projectId.toString()] : undefined;
|
// derived values
|
||||||
const moduleDetails = moduleId ? moduleStore.getModuleById(moduleId.toString()) : undefined;
|
const moduleDetails = moduleId ? getModuleById(moduleId.toString()) : undefined;
|
||||||
|
|
||||||
const canUserCreateIssue =
|
const canUserCreateIssue =
|
||||||
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
|
currentProjectRole && [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER].includes(currentProjectRole);
|
||||||
|
|
||||||
@ -157,16 +180,8 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
|
|||||||
width="auto"
|
width="auto"
|
||||||
placement="bottom-start"
|
placement="bottom-start"
|
||||||
>
|
>
|
||||||
{modulesList?.map((module) => (
|
{projectModules?.map((moduleId) => (
|
||||||
<CustomMenu.MenuItem
|
<ModuleDropdownOption moduleId={moduleId} />
|
||||||
key={module.id}
|
|
||||||
onClick={() => router.push(`/${workspaceSlug}/projects/${projectId}/modules/${module.id}`)}
|
|
||||||
>
|
|
||||||
<div className="flex items-center gap-1.5">
|
|
||||||
<DiceIcon className="h-3 w-3" />
|
|
||||||
{truncateText(module.name, 40)}
|
|
||||||
</div>
|
|
||||||
</CustomMenu.MenuItem>
|
|
||||||
))}
|
))}
|
||||||
</CustomMenu>
|
</CustomMenu>
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,6 @@ export const CycleLayoutRoot: React.FC = observer(() => {
|
|||||||
};
|
};
|
||||||
// store hooks
|
// store hooks
|
||||||
const {
|
const {
|
||||||
cycle: cycleStore,
|
|
||||||
cycleIssues: { loader, getIssues, fetchIssues },
|
cycleIssues: { loader, getIssues, fetchIssues },
|
||||||
cycleIssuesFilter: { issueFilters, fetchFilters },
|
cycleIssuesFilter: { issueFilters, fetchFilters },
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { usePopper } from "react-popper";
|
import { usePopper } from "react-popper";
|
||||||
// mobx store
|
// hooks
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useCycle } from "hooks/store";
|
||||||
// ui
|
// ui
|
||||||
import { Combobox } from "@headlessui/react";
|
import { Combobox } from "@headlessui/react";
|
||||||
// icons
|
// icons
|
||||||
@ -28,28 +28,27 @@ export const IssueCycleSelect: React.FC<IssueCycleSelectProps> = observer((props
|
|||||||
placement: "bottom-start",
|
placement: "bottom-start",
|
||||||
});
|
});
|
||||||
|
|
||||||
const { cycle: cycleStore } = useMobxStore();
|
const { projectAllCycles, fetchAllCycles, getCycleById } = useCycle();
|
||||||
|
|
||||||
const fetchCycles = () => {
|
const fetchCycles = () => {
|
||||||
if (workspaceSlug && projectId) cycleStore.fetchCycles(workspaceSlug, projectId, "all");
|
if (workspaceSlug && projectId) fetchAllCycles(workspaceSlug, projectId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const cycles = cycleStore.projectCycles;
|
const selectedCycle = value ? getCycleById(value) : null;
|
||||||
|
|
||||||
const selectedCycle = cycles ? cycles?.find((i) => i.id === value) : undefined;
|
const options = projectAllCycles?.map((cycleId) => {
|
||||||
|
const cycleDetail = getCycleById(cycleId);
|
||||||
const options = cycles?.map((cycle) => ({
|
return {
|
||||||
value: cycle.id,
|
value: cycleId,
|
||||||
query: cycle.name,
|
query: cycleDetail?.name ?? "",
|
||||||
content: (
|
content: (
|
||||||
<div className="flex items-center gap-1.5 truncate">
|
<div className="flex items-center gap-1.5">
|
||||||
<span className="flex h-3.5 w-3.5 flex-shrink-0 items-center justify-center">
|
<ContrastIcon className="h-3 w-3" />
|
||||||
<ContrastIcon />
|
{cycleDetail?.name}
|
||||||
</span>
|
</div>
|
||||||
<span className="flex-grow truncate">{cycle.name}</span>
|
),
|
||||||
</div>
|
};
|
||||||
),
|
});
|
||||||
}));
|
|
||||||
|
|
||||||
const filteredOptions =
|
const filteredOptions =
|
||||||
query === "" ? options : options?.filter((option) => option.query.toLowerCase().includes(query.toLowerCase()));
|
query === "" ? options : options?.filter((option) => option.query.toLowerCase().includes(query.toLowerCase()));
|
||||||
|
@ -2,7 +2,8 @@ import React, { useState } from "react";
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import { mutate } from "swr";
|
import { mutate } from "swr";
|
||||||
// mobx store
|
// hooks
|
||||||
|
import { useModule } from "hooks/store";
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// ui
|
// ui
|
||||||
import { CustomSearchSelect, DiceIcon, Spinner, Tooltip } from "@plane/ui";
|
import { CustomSearchSelect, DiceIcon, Spinner, Tooltip } from "@plane/ui";
|
||||||
@ -25,9 +26,9 @@ export const SidebarModuleSelect: React.FC<Props> = observer((props) => {
|
|||||||
const { workspaceSlug, projectId } = router.query;
|
const { workspaceSlug, projectId } = router.query;
|
||||||
// mobx store
|
// mobx store
|
||||||
const {
|
const {
|
||||||
module: { projectModules },
|
|
||||||
moduleIssues: { removeIssueFromModule, addIssueToModule },
|
moduleIssues: { removeIssueFromModule, addIssueToModule },
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
const { projectModules, getModuleById } = useModule();
|
||||||
|
|
||||||
const [isUpdating, setIsUpdating] = useState(false);
|
const [isUpdating, setIsUpdating] = useState(false);
|
||||||
|
|
||||||
@ -63,21 +64,25 @@ export const SidebarModuleSelect: React.FC<Props> = observer((props) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const options = projectModules?.map((module) => ({
|
const options = projectModules?.map((moduleId) => {
|
||||||
value: module.id,
|
const moduleDetail = getModuleById(moduleId);
|
||||||
query: module.name,
|
return {
|
||||||
content: (
|
value: moduleId,
|
||||||
<div className="flex items-center gap-1.5 truncate">
|
query: moduleDetail?.name ?? "",
|
||||||
<span className="flex h-3.5 w-3.5 flex-shrink-0 items-center justify-center">
|
content: (
|
||||||
<DiceIcon />
|
<div className="flex items-center gap-1.5 truncate">
|
||||||
</span>
|
<span className="flex h-3.5 w-3.5 flex-shrink-0 items-center justify-center">
|
||||||
<span className="flex-grow truncate">{module.name}</span>
|
<DiceIcon />
|
||||||
</div>
|
</span>
|
||||||
),
|
<span className="flex-grow truncate">{moduleDetail?.name}</span>
|
||||||
}));
|
</div>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// derived values
|
||||||
const issueModule = issueDetail?.issue_module;
|
const issueModule = issueDetail?.issue_module;
|
||||||
|
const selectedModule = issueModule?.module ? getModuleById(issueModule?.module) : null;
|
||||||
const disableSelect = disabled || isUpdating;
|
const disableSelect = disabled || isUpdating;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -88,16 +93,13 @@ export const SidebarModuleSelect: React.FC<Props> = observer((props) => {
|
|||||||
value === issueModule?.module_detail.id
|
value === issueModule?.module_detail.id
|
||||||
? handleRemoveIssueFromModule(issueModule?.id ?? "", issueModule?.module ?? "")
|
? handleRemoveIssueFromModule(issueModule?.id ?? "", issueModule?.module ?? "")
|
||||||
: handleModuleChange
|
: handleModuleChange
|
||||||
? handleModuleChange(value)
|
? handleModuleChange(value)
|
||||||
: handleModuleStoreChange(value);
|
: handleModuleStoreChange(value);
|
||||||
}}
|
}}
|
||||||
options={options}
|
options={options}
|
||||||
customButton={
|
customButton={
|
||||||
<div>
|
<div>
|
||||||
<Tooltip
|
<Tooltip position="left" tooltipContent={`${selectedModule?.name ?? "No module"}`}>
|
||||||
position="left"
|
|
||||||
tooltipContent={`${projectModules?.find((m) => m.id === issueModule?.module)?.name ?? "No module"}`}
|
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={`flex w-full items-center rounded bg-custom-background-80 px-2.5 py-0.5 text-xs ${
|
className={`flex w-full items-center rounded bg-custom-background-80 px-2.5 py-0.5 text-xs ${
|
||||||
@ -110,9 +112,7 @@ export const SidebarModuleSelect: React.FC<Props> = observer((props) => {
|
|||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<span className="flex-shrink-0">{issueModule && <DiceIcon className="h-3.5 w-3.5" />}</span>
|
<span className="flex-shrink-0">{issueModule && <DiceIcon className="h-3.5 w-3.5" />}</span>
|
||||||
<span className="truncate">
|
<span className="truncate">{selectedModule?.name ?? "No module"}</span>
|
||||||
{projectModules?.find((m) => m.id === issueModule?.module)?.name ?? "No module"}
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
Loading…
Reference in New Issue
Block a user