fix: project automation settings flickering (#2680)

* fix: cycle and module sidebar z-index

* fix: project automation settings flickering
This commit is contained in:
Aaryan Khandelwal 2023-11-08 17:34:42 +05:30 committed by GitHub
parent da799b5a63
commit df8bdfd5b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 156 additions and 157 deletions

View File

@ -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,46 +56,52 @@ 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 ? (
<div className="ml-12"> projectDetails.archive_in !== 0 && (
<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="ml-12">
<div className="w-1/2 text-sm font-medium">Auto-archive issues that are closed for</div> <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"> <div className="w-1/2 text-sm font-medium">Auto-archive issues that are closed for</div>
<CustomSelect <div className="w-1/2">
value={projectDetails?.archive_in} <CustomSelect
label={`${projectDetails?.archive_in} ${projectDetails?.archive_in === 1 ? "Month" : "Months"}`} value={projectDetails?.archive_in}
onChange={(val: number) => { label={`${projectDetails?.archive_in} ${projectDetails?.archive_in === 1 ? "Month" : "Months"}`}
handleChange({ archive_in: val }); onChange={(val: number) => {
}} handleChange({ archive_in: val });
input }}
width="w-full" input
disabled={disabled} width="w-full"
> disabled={userRole !== 20}
<> >
{PROJECT_AUTOMATION_MONTHS.map((month) => ( <>
<CustomSelect.Option key={month.label} value={month.value}> {PROJECT_AUTOMATION_MONTHS.map((month) => (
<span className="text-sm">{month.label}</span> <CustomSelect.Option key={month.label} value={month.value}>
</CustomSelect.Option> <span className="text-sm">{month.label}</span>
))} </CustomSelect.Option>
))}
<button <button
type="button" type="button"
className="flex w-full text-sm select-none items-center rounded px-1 py-1.5 text-custom-text-200 hover:bg-custom-background-80" className="flex w-full text-sm select-none items-center rounded px-1 py-1.5 text-custom-text-200 hover:bg-custom-background-80"
onClick={() => setmonthModal(true)} onClick={() => setmonthModal(true)}
> >
Customise Time Range Customise Time Range
</button> </button>
</> </>
</CustomSelect> </CustomSelect>
</div>
</div> </div>
</div> </div>
</div> )
) : (
<Loader className="ml-12">
<Loader.Item height="50px" />
</Loader>
)} )}
</div> </div>
</> </>
); );
}; });

View File

@ -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 havent been completed or cancelled. Plane will automatically close issue that haven{"'"}t been completed or cancelled.
</p> </p>
</div> </div>
</div> </div>
@ -94,87 +85,93 @@ 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 ? (
<div className="ml-12"> projectDetails.close_in !== 0 && (
<div className="flex flex-col rounded bg-custom-background-90 border-[0.5px] border-custom-border-200 p-2"> <div className="ml-12">
<div className="flex items-center justify-between px-5 py-4 gap-2 w-full"> <div className="flex flex-col rounded bg-custom-background-90 border border-custom-border-200">
<div className="w-1/2 text-sm font-medium">Auto-close issues that are inactive for</div> <div className="flex items-center justify-between px-5 py-4 gap-2 w-full">
<div className="w-1/2"> <div className="w-1/2 text-sm font-medium">Auto-close issues that are inactive for</div>
<CustomSelect <div className="w-1/2">
value={projectDetails?.close_in} <CustomSelect
label={`${projectDetails?.close_in} ${projectDetails?.close_in === 1 ? "Month" : "Months"}`} value={projectDetails?.close_in}
onChange={(val: number) => { label={`${projectDetails?.close_in} ${projectDetails?.close_in === 1 ? "Month" : "Months"}`}
handleChange({ close_in: val }); onChange={(val: number) => {
}} handleChange({ close_in: val });
input }}
width="w-full" input
disabled={disabled} width="w-full"
> disabled={userRole !== 20}
<> >
{PROJECT_AUTOMATION_MONTHS.map((month) => ( <>
<CustomSelect.Option key={month.label} value={month.value}> {PROJECT_AUTOMATION_MONTHS.map((month) => (
{month.label} <CustomSelect.Option key={month.label} value={month.value}>
</CustomSelect.Option> {month.label}
))} </CustomSelect.Option>
<button ))}
type="button" <button
className="flex w-full select-none items-center rounded px-1 py-1.5 text-custom-text-200 hover:bg-custom-background-80" type="button"
onClick={() => setmonthModal(true)} className="flex w-full select-none items-center rounded px-1 py-1.5 text-custom-text-200 hover:bg-custom-background-80"
> onClick={() => setmonthModal(true)}
Customise Time Range >
</button> Customise Time Range
</> </button>
</CustomSelect> </>
</CustomSelect>
</div>
</div> </div>
</div>
<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 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 ? (
<StateGroupIcon <StateGroupIcon
stateGroup={selectedOption.group} stateGroup={selectedOption.group}
color={selectedOption.color} color={selectedOption.color}
height="16px" height="16px"
width="16px" width="16px"
/> />
) : currentDefaultState ? ( ) : currentDefaultState ? (
<StateGroupIcon <StateGroupIcon
stateGroup={currentDefaultState.group} stateGroup={currentDefaultState.group}
color={currentDefaultState.color} color={currentDefaultState.color}
height="16px" height="16px"
width="16px" width="16px"
/> />
) : ( ) : (
<DoubleCircleIcon className="h-3.5 w-3.5 text-custom-text-200" /> <DoubleCircleIcon className="h-3.5 w-3.5 text-custom-text-200" />
)} )}
{selectedOption?.name {selectedOption?.name
? selectedOption.name ? selectedOption.name
: currentDefaultState?.name ?? <span className="text-custom-text-200">State</span>} : currentDefaultState?.name ?? <span className="text-custom-text-200">State</span>}
</div> </div>
} }
onChange={(val: string) => { onChange={(val: string) => {
handleChange({ default_state: val }); handleChange({ default_state: val });
}} }}
options={options} options={options}
disabled={!multipleOptions} disabled={!multipleOptions}
width="w-full" width="w-full"
input input
/> />
</div>
</div> </div>
</div> </div>
</div> </div>
</div> )
) : (
<Loader className="ml-12">
<Loader.Item height="50px" />
</Loader>
)} )}
</div> </div>
</> </>
); );
}; });

View File

@ -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)",

View File

@ -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)",

View File

@ -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)",

View File

@ -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)",

View File

@ -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>
); );
}; };