mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
fix: project automation settings flickering (#2680)
* fix: cycle and module sidebar z-index * fix: project automation settings flickering
This commit is contained in:
parent
da799b5a63
commit
df8bdfd5b9
@ -1,7 +1,9 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
// mobx store
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// component
|
// component
|
||||||
import { CustomSelect, ToggleSwitch } from "@plane/ui";
|
import { CustomSelect, Loader, ToggleSwitch } from "@plane/ui";
|
||||||
import { SelectMonthModal } from "components/automation";
|
import { SelectMonthModal } from "components/automation";
|
||||||
// icon
|
// icon
|
||||||
import { ArchiveRestore } from "lucide-react";
|
import { ArchiveRestore } from "lucide-react";
|
||||||
@ -11,15 +13,21 @@ import { PROJECT_AUTOMATION_MONTHS } from "constants/project";
|
|||||||
import { IProject } from "types";
|
import { IProject } from "types";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
projectDetails: IProject | undefined;
|
|
||||||
handleChange: (formData: Partial<IProject>) => Promise<void>;
|
handleChange: (formData: Partial<IProject>) => Promise<void>;
|
||||||
disabled?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AutoArchiveAutomation: React.FC<Props> = ({ projectDetails, handleChange, disabled = false }) => {
|
const initialValues: Partial<IProject> = { archive_in: 1 };
|
||||||
|
|
||||||
|
export const AutoArchiveAutomation: React.FC<Props> = observer((props) => {
|
||||||
|
const { handleChange } = props;
|
||||||
|
// states
|
||||||
const [monthModal, setmonthModal] = useState(false);
|
const [monthModal, setmonthModal] = useState(false);
|
||||||
|
|
||||||
const initialValues: Partial<IProject> = { archive_in: 1 };
|
const { user: userStore, project: projectStore } = useMobxStore();
|
||||||
|
|
||||||
|
const projectDetails = projectStore.currentProjectDetails;
|
||||||
|
const userRole = userStore.currentProjectRole;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SelectMonthModal
|
<SelectMonthModal
|
||||||
@ -48,13 +56,14 @@ export const AutoArchiveAutomation: React.FC<Props> = ({ projectDetails, handleC
|
|||||||
projectDetails?.archive_in === 0 ? handleChange({ archive_in: 1 }) : handleChange({ archive_in: 0 })
|
projectDetails?.archive_in === 0 ? handleChange({ archive_in: 1 }) : handleChange({ archive_in: 0 })
|
||||||
}
|
}
|
||||||
size="sm"
|
size="sm"
|
||||||
disabled={disabled}
|
disabled={userRole !== 20}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{projectDetails?.archive_in !== 0 && (
|
{projectDetails ? (
|
||||||
|
projectDetails.archive_in !== 0 && (
|
||||||
<div className="ml-12">
|
<div className="ml-12">
|
||||||
<div className="flex items-center justify-between rounded px-5 py-4 bg-custom-background-90 border-[0.5px] border-custom-border-200 gap-2 w-full">
|
<div className="flex items-center justify-between rounded px-5 py-4 bg-custom-background-90 border border-custom-border-200 gap-2 w-full">
|
||||||
<div className="w-1/2 text-sm font-medium">Auto-archive issues that are closed for</div>
|
<div className="w-1/2 text-sm font-medium">Auto-archive issues that are closed for</div>
|
||||||
<div className="w-1/2">
|
<div className="w-1/2">
|
||||||
<CustomSelect
|
<CustomSelect
|
||||||
@ -65,7 +74,7 @@ export const AutoArchiveAutomation: React.FC<Props> = ({ projectDetails, handleC
|
|||||||
}}
|
}}
|
||||||
input
|
input
|
||||||
width="w-full"
|
width="w-full"
|
||||||
disabled={disabled}
|
disabled={userRole !== 20}
|
||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
{PROJECT_AUTOMATION_MONTHS.map((month) => (
|
{PROJECT_AUTOMATION_MONTHS.map((month) => (
|
||||||
@ -86,8 +95,13 @@ export const AutoArchiveAutomation: React.FC<Props> = ({ projectDetails, handleC
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<Loader className="ml-12">
|
||||||
|
<Loader.Item height="50px" />
|
||||||
|
</Loader>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
@ -1,41 +1,33 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import useSWR from "swr";
|
import { observer } from "mobx-react-lite";
|
||||||
import { useRouter } from "next/router";
|
// mobx store
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// component
|
// component
|
||||||
import { SelectMonthModal } from "components/automation";
|
import { SelectMonthModal } from "components/automation";
|
||||||
import { CustomSelect, CustomSearchSelect, ToggleSwitch, StateGroupIcon, DoubleCircleIcon } from "@plane/ui";
|
import { CustomSelect, CustomSearchSelect, ToggleSwitch, StateGroupIcon, DoubleCircleIcon, Loader } from "@plane/ui";
|
||||||
// icons
|
// icons
|
||||||
import { ArchiveX } from "lucide-react";
|
import { ArchiveX } from "lucide-react";
|
||||||
// services
|
// helpers
|
||||||
import { ProjectStateService } from "services/project";
|
import { getStatesList } from "helpers/state.helper";
|
||||||
// constants
|
|
||||||
import { PROJECT_AUTOMATION_MONTHS } from "constants/project";
|
|
||||||
import { STATES_LIST } from "constants/fetch-keys";
|
|
||||||
// types
|
// types
|
||||||
import { IProject } from "types";
|
import { IProject } from "types";
|
||||||
// helper
|
// fetch keys
|
||||||
import { getStatesList } from "helpers/state.helper";
|
import { PROJECT_AUTOMATION_MONTHS } from "constants/project";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
projectDetails: IProject | undefined;
|
|
||||||
handleChange: (formData: Partial<IProject>) => Promise<void>;
|
handleChange: (formData: Partial<IProject>) => Promise<void>;
|
||||||
disabled?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const projectStateService = new ProjectStateService();
|
export const AutoCloseAutomation: React.FC<Props> = observer((props) => {
|
||||||
|
const { handleChange } = props;
|
||||||
export const AutoCloseAutomation: React.FC<Props> = ({ projectDetails, handleChange, disabled = false }) => {
|
// states
|
||||||
const [monthModal, setmonthModal] = useState(false);
|
const [monthModal, setmonthModal] = useState(false);
|
||||||
|
|
||||||
const router = useRouter();
|
const { user: userStore, project: projectStore } = useMobxStore();
|
||||||
const { workspaceSlug, projectId } = router.query;
|
|
||||||
|
|
||||||
const { data: stateGroups } = useSWR(
|
const userRole = userStore.currentProjectRole;
|
||||||
workspaceSlug && projectId ? STATES_LIST(projectId as string) : null,
|
const projectDetails = projectStore.currentProjectDetails;
|
||||||
workspaceSlug && projectId
|
const stateGroups = projectStore.projectStatesByGroups ?? undefined;
|
||||||
? () => projectStateService.getStates(workspaceSlug as string, projectId as string)
|
|
||||||
: null
|
|
||||||
);
|
|
||||||
const states = getStatesList(stateGroups);
|
const states = getStatesList(stateGroups);
|
||||||
|
|
||||||
const options = states
|
const options = states
|
||||||
@ -72,8 +64,7 @@ export const AutoCloseAutomation: React.FC<Props> = ({ projectDetails, handleCha
|
|||||||
handleClose={() => setmonthModal(false)}
|
handleClose={() => setmonthModal(false)}
|
||||||
handleChange={handleChange}
|
handleChange={handleChange}
|
||||||
/>
|
/>
|
||||||
|
<div className="flex flex-col gap-4 border-b border-custom-border-200 px-4 py-6">
|
||||||
<div className="flex flex-col gap-4 border-b border-custom-border-100 px-4 py-6">
|
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-start gap-3">
|
<div className="flex items-start gap-3">
|
||||||
<div className="flex items-center justify-center p-3 rounded bg-custom-background-90">
|
<div className="flex items-center justify-center p-3 rounded bg-custom-background-90">
|
||||||
@ -82,7 +73,7 @@ export const AutoCloseAutomation: React.FC<Props> = ({ projectDetails, handleCha
|
|||||||
<div className="">
|
<div className="">
|
||||||
<h4 className="text-sm font-medium">Auto-close issues</h4>
|
<h4 className="text-sm font-medium">Auto-close issues</h4>
|
||||||
<p className="text-sm text-custom-text-200 tracking-tight">
|
<p className="text-sm text-custom-text-200 tracking-tight">
|
||||||
Plane will automatically close issue that haven’t been completed or cancelled.
|
Plane will automatically close issue that haven{"'"}t been completed or cancelled.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -94,13 +85,14 @@ export const AutoCloseAutomation: React.FC<Props> = ({ projectDetails, handleCha
|
|||||||
: handleChange({ close_in: 0, default_state: null })
|
: handleChange({ close_in: 0, default_state: null })
|
||||||
}
|
}
|
||||||
size="sm"
|
size="sm"
|
||||||
disabled={disabled}
|
disabled={userRole !== 20}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{projectDetails?.close_in !== 0 && (
|
{projectDetails ? (
|
||||||
|
projectDetails.close_in !== 0 && (
|
||||||
<div className="ml-12">
|
<div className="ml-12">
|
||||||
<div className="flex flex-col rounded bg-custom-background-90 border-[0.5px] border-custom-border-200 p-2">
|
<div className="flex flex-col rounded bg-custom-background-90 border border-custom-border-200">
|
||||||
<div className="flex items-center justify-between px-5 py-4 gap-2 w-full">
|
<div className="flex items-center justify-between px-5 py-4 gap-2 w-full">
|
||||||
<div className="w-1/2 text-sm font-medium">Auto-close issues that are inactive for</div>
|
<div className="w-1/2 text-sm font-medium">Auto-close issues that are inactive for</div>
|
||||||
<div className="w-1/2">
|
<div className="w-1/2">
|
||||||
@ -112,7 +104,7 @@ export const AutoCloseAutomation: React.FC<Props> = ({ projectDetails, handleCha
|
|||||||
}}
|
}}
|
||||||
input
|
input
|
||||||
width="w-full"
|
width="w-full"
|
||||||
disabled={disabled}
|
disabled={userRole !== 20}
|
||||||
>
|
>
|
||||||
<>
|
<>
|
||||||
{PROJECT_AUTOMATION_MONTHS.map((month) => (
|
{PROJECT_AUTOMATION_MONTHS.map((month) => (
|
||||||
@ -136,7 +128,7 @@ export const AutoCloseAutomation: React.FC<Props> = ({ projectDetails, handleCha
|
|||||||
<div className="w-1/2 text-sm font-medium">Auto-close Status</div>
|
<div className="w-1/2 text-sm font-medium">Auto-close Status</div>
|
||||||
<div className="w-1/2 ">
|
<div className="w-1/2 ">
|
||||||
<CustomSearchSelect
|
<CustomSearchSelect
|
||||||
value={projectDetails?.default_state ? projectDetails?.default_state : defaultState}
|
value={projectDetails?.default_state ?? defaultState}
|
||||||
label={
|
label={
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{selectedOption ? (
|
{selectedOption ? (
|
||||||
@ -173,8 +165,13 @@ export const AutoCloseAutomation: React.FC<Props> = ({ projectDetails, handleCha
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<Loader className="ml-12">
|
||||||
|
<Loader.Item height="50px" />
|
||||||
|
</Loader>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
@ -41,7 +41,7 @@ export const CyclePeekOverview: React.FC<Props> = observer(({ projectId, workspa
|
|||||||
{peekCycle && (
|
{peekCycle && (
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className="flex flex-col gap-3.5 h-full w-[24rem] z-10 overflow-y-auto border-l border-custom-border-100 bg-custom-sidebar-background-100 px-6 py-3.5 duration-300 flex-shrink-0"
|
className="flex flex-col gap-3.5 h-full w-[24rem] overflow-y-auto border-l border-custom-border-100 bg-custom-sidebar-background-100 px-6 py-3.5 duration-300 flex-shrink-0"
|
||||||
style={{
|
style={{
|
||||||
boxShadow:
|
boxShadow:
|
||||||
"0px 1px 4px 0px rgba(0, 0, 0, 0.06), 0px 2px 4px 0px rgba(16, 24, 40, 0.06), 0px 1px 8px -1px rgba(16, 24, 40, 0.06)",
|
"0px 1px 4px 0px rgba(0, 0, 0, 0.06), 0px 2px 4px 0px rgba(16, 24, 40, 0.06), 0px 1px 8px -1px rgba(16, 24, 40, 0.06)",
|
||||||
|
@ -41,7 +41,7 @@ export const ModulePeekOverview: React.FC<Props> = observer(({ projectId, worksp
|
|||||||
{peekModule && (
|
{peekModule && (
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className="flex flex-col gap-3.5 h-full w-[24rem] z-10 overflow-y-auto border-l border-custom-border-100 bg-custom-sidebar-background-100 px-6 py-3.5 duration-300 flex-shrink-0"
|
className="flex flex-col gap-3.5 h-full w-[24rem] overflow-y-auto border-l border-custom-border-100 bg-custom-sidebar-background-100 px-6 py-3.5 duration-300 flex-shrink-0"
|
||||||
style={{
|
style={{
|
||||||
boxShadow:
|
boxShadow:
|
||||||
"0px 1px 4px 0px rgba(0, 0, 0, 0.06), 0px 2px 4px 0px rgba(16, 24, 40, 0.06), 0px 1px 8px -1px rgba(16, 24, 40, 0.06)",
|
"0px 1px 4px 0px rgba(0, 0, 0, 0.06), 0px 2px 4px 0px rgba(16, 24, 40, 0.06), 0px 1px 8px -1px rgba(16, 24, 40, 0.06)",
|
||||||
|
@ -58,7 +58,7 @@ const CycleDetailPage: NextPageWithLayout = () => {
|
|||||||
</div>
|
</div>
|
||||||
{cycleId && !isSidebarCollapsed && (
|
{cycleId && !isSidebarCollapsed && (
|
||||||
<div
|
<div
|
||||||
className="flex flex-col gap-3.5 h-full w-[24rem] z-10 overflow-y-auto border-l border-custom-border-100 bg-custom-sidebar-background-100 px-6 py-3.5 duration-300 flex-shrink-0"
|
className="flex flex-col gap-3.5 h-full w-[24rem] overflow-y-auto border-l border-custom-border-100 bg-custom-sidebar-background-100 px-6 py-3.5 duration-300 flex-shrink-0"
|
||||||
style={{
|
style={{
|
||||||
boxShadow:
|
boxShadow:
|
||||||
"0px 1px 4px 0px rgba(0, 0, 0, 0.06), 0px 2px 4px 0px rgba(16, 24, 40, 0.06), 0px 1px 8px -1px rgba(16, 24, 40, 0.06)",
|
"0px 1px 4px 0px rgba(0, 0, 0, 0.06), 0px 2px 4px 0px rgba(16, 24, 40, 0.06), 0px 1px 8px -1px rgba(16, 24, 40, 0.06)",
|
||||||
|
@ -58,7 +58,7 @@ const ModuleIssuesPage: NextPageWithLayout = () => {
|
|||||||
</div>
|
</div>
|
||||||
{moduleId && !isSidebarCollapsed && (
|
{moduleId && !isSidebarCollapsed && (
|
||||||
<div
|
<div
|
||||||
className="flex flex-col gap-3.5 h-full w-[24rem] z-10 overflow-y-auto border-l border-custom-border-100 bg-custom-sidebar-background-100 px-6 py-3.5 duration-300 flex-shrink-0"
|
className="flex flex-col gap-3.5 h-full w-[24rem] overflow-y-auto border-l border-custom-border-100 bg-custom-sidebar-background-100 px-6 py-3.5 duration-300 flex-shrink-0"
|
||||||
style={{
|
style={{
|
||||||
boxShadow:
|
boxShadow:
|
||||||
"0px 1px 4px 0px rgba(0, 0, 0, 0.06), 0px 2px 4px 0px rgba(16, 24, 40, 0.06), 0px 1px 8px -1px rgba(16, 24, 40, 0.06)",
|
"0px 1px 4px 0px rgba(0, 0, 0, 0.06), 0px 2px 4px 0px rgba(16, 24, 40, 0.06), 0px 1px 8px -1px rgba(16, 24, 40, 0.06)",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { ReactElement } from "react";
|
import React, { ReactElement } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import useSWR, { mutate } from "swr";
|
import useSWR from "swr";
|
||||||
// services
|
// services
|
||||||
import { ProjectService } from "services/project";
|
import { ProjectService } from "services/project";
|
||||||
// layouts
|
// layouts
|
||||||
@ -17,7 +17,7 @@ import { ProjectSettingHeader } from "components/headers";
|
|||||||
import { NextPageWithLayout } from "types/app";
|
import { NextPageWithLayout } from "types/app";
|
||||||
import { IProject } from "types";
|
import { IProject } from "types";
|
||||||
// constant
|
// constant
|
||||||
import { PROJECTS_LIST, PROJECT_DETAILS, USER_PROJECT_VIEW } from "constants/fetch-keys";
|
import { USER_PROJECT_VIEW } from "constants/fetch-keys";
|
||||||
|
|
||||||
// services
|
// services
|
||||||
const projectService = new ProjectService();
|
const projectService = new ProjectService();
|
||||||
@ -41,18 +41,6 @@ const AutomationSettingsPage: NextPageWithLayout = () => {
|
|||||||
const handleChange = async (formData: Partial<IProject>) => {
|
const handleChange = async (formData: Partial<IProject>) => {
|
||||||
if (!workspaceSlug || !projectId || !projectDetails) return;
|
if (!workspaceSlug || !projectId || !projectDetails) return;
|
||||||
|
|
||||||
mutate<IProject>(
|
|
||||||
PROJECT_DETAILS(projectId as string),
|
|
||||||
(prevData) => ({ ...(prevData as IProject), ...formData }),
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
mutate<IProject[]>(
|
|
||||||
PROJECTS_LIST(workspaceSlug as string, { is_favorite: "all" }),
|
|
||||||
(prevData) => (prevData ?? []).map((p) => (p.id === projectDetails.id ? { ...p, ...formData } : p)),
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
await projectService
|
await projectService
|
||||||
.updateProject(workspaceSlug as string, projectId as string, formData, user)
|
.updateProject(workspaceSlug as string, projectId as string, formData, user)
|
||||||
.then(() => {})
|
.then(() => {})
|
||||||
@ -72,8 +60,8 @@ const AutomationSettingsPage: NextPageWithLayout = () => {
|
|||||||
<div className="flex items-center py-3.5 border-b border-custom-border-100">
|
<div className="flex items-center py-3.5 border-b border-custom-border-100">
|
||||||
<h3 className="text-xl font-medium">Automations</h3>
|
<h3 className="text-xl font-medium">Automations</h3>
|
||||||
</div>
|
</div>
|
||||||
<AutoArchiveAutomation projectDetails={projectDetails} handleChange={handleChange} disabled={!isAdmin} />
|
<AutoArchiveAutomation handleChange={handleChange} />
|
||||||
<AutoCloseAutomation projectDetails={projectDetails} handleChange={handleChange} disabled={!isAdmin} />
|
<AutoCloseAutomation handleChange={handleChange} />
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user