fix: project setting member role validation (#2369)

* fix: project setting member role validation

* chore: opacity removed from member setting page

* chore: member setting page validation
This commit is contained in:
Anmol Singh Bhatia 2023-10-04 19:24:13 +05:30 committed by GitHub
parent 9482cc3a73
commit 4ec2811388
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 89 additions and 17 deletions

View File

@ -13,9 +13,14 @@ import { IProject } from "types";
type Props = { type Props = {
projectDetails: IProject | undefined; 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 }) => { export const AutoArchiveAutomation: React.FC<Props> = ({
projectDetails,
handleChange,
disabled = false,
}) => {
const [monthModal, setmonthModal] = useState(false); const [monthModal, setmonthModal] = useState(false);
const initialValues: Partial<IProject> = { archive_in: 1 }; const initialValues: Partial<IProject> = { archive_in: 1 };
@ -49,6 +54,7 @@ export const AutoArchiveAutomation: React.FC<Props> = ({ projectDetails, handleC
: handleChange({ archive_in: 0 }) : handleChange({ archive_in: 0 })
} }
size="sm" size="sm"
disabled={disabled}
/> />
</div> </div>
@ -70,6 +76,7 @@ export const AutoArchiveAutomation: React.FC<Props> = ({ projectDetails, handleC
input input
verticalPosition="bottom" verticalPosition="bottom"
width="w-full" width="w-full"
disabled={disabled}
> >
<> <>
{PROJECT_AUTOMATION_MONTHS.map((month) => ( {PROJECT_AUTOMATION_MONTHS.map((month) => (

View File

@ -24,9 +24,14 @@ import { getStatesList } from "helpers/state.helper";
type Props = { type Props = {
projectDetails: IProject | undefined; projectDetails: IProject | undefined;
handleChange: (formData: Partial<IProject>) => Promise<void>; handleChange: (formData: Partial<IProject>) => Promise<void>;
disabled?: boolean;
}; };
export const AutoCloseAutomation: React.FC<Props> = ({ projectDetails, handleChange }) => { export const AutoCloseAutomation: React.FC<Props> = ({
projectDetails,
handleChange,
disabled = false,
}) => {
const [monthModal, setmonthModal] = useState(false); const [monthModal, setmonthModal] = useState(false);
const router = useRouter(); const router = useRouter();
@ -98,6 +103,7 @@ 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}
/> />
</div> </div>
@ -119,6 +125,7 @@ export const AutoCloseAutomation: React.FC<Props> = ({ projectDetails, handleCha
}} }}
input input
width="w-full" width="w-full"
disabled={disabled}
> >
<> <>
{PROJECT_AUTOMATION_MONTHS.map((month) => ( {PROJECT_AUTOMATION_MONTHS.map((month) => (

View File

@ -16,9 +16,10 @@ import { PROJECT_MEMBERS } from "constants/fetch-keys";
type Props = { type Props = {
value: any; value: any;
onChange: (val: string) => void; onChange: (val: string) => void;
isDisabled?: boolean;
}; };
export const MemberSelect: React.FC<Props> = ({ value, onChange }) => { export const MemberSelect: React.FC<Props> = ({ value, onChange, isDisabled = false }) => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId } = router.query; const { workspaceSlug, projectId } = router.query;
@ -79,6 +80,7 @@ export const MemberSelect: React.FC<Props> = ({ value, onChange }) => {
position="right" position="right"
width="w-full" width="w-full"
onChange={onChange} onChange={onChange}
disabled={isDisabled}
/> />
); );
}; };

View File

@ -46,6 +46,7 @@ const CustomMenu = ({
type="button" type="button"
onClick={menuButtonOnClick} onClick={menuButtonOnClick}
className={customButtonClassName} className={customButtonClassName}
disabled={disabled}
> >
{customButton} {customButton}
</Menu.Button> </Menu.Button>

View File

@ -16,6 +16,7 @@ type Props = {
}; };
secondaryButton?: React.ReactNode; secondaryButton?: React.ReactNode;
isFullScreen?: boolean; isFullScreen?: boolean;
disabled?: boolean;
}; };
export const EmptyState: React.FC<Props> = ({ export const EmptyState: React.FC<Props> = ({
@ -25,6 +26,7 @@ export const EmptyState: React.FC<Props> = ({
primaryButton, primaryButton,
secondaryButton, secondaryButton,
isFullScreen = true, isFullScreen = true,
disabled = false,
}) => ( }) => (
<div <div
className={`h-full w-full mx-auto grid place-items-center p-8 ${ className={`h-full w-full mx-auto grid place-items-center p-8 ${
@ -37,7 +39,11 @@ export const EmptyState: React.FC<Props> = ({
{description && <p className="text-custom-text-300 mb-7 sm:mb-8">{description}</p>} {description && <p className="text-custom-text-300 mb-7 sm:mb-8">{description}</p>}
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
{primaryButton && ( {primaryButton && (
<PrimaryButton className="flex items-center gap-1.5" onClick={primaryButton.onClick}> <PrimaryButton
className="flex items-center gap-1.5"
onClick={primaryButton.onClick}
disabled={disabled}
>
{primaryButton.icon} {primaryButton.icon}
{primaryButton.text} {primaryButton.text}
</PrimaryButton> </PrimaryButton>

View File

@ -21,7 +21,7 @@ export const ToggleSwitch: React.FC<Props> = (props) => {
size === "sm" ? "h-4 w-6" : size === "md" ? "h-5 w-8" : "h-6 w-10" size === "sm" ? "h-4 w-6" : size === "md" ? "h-5 w-8" : "h-6 w-10"
} flex-shrink-0 cursor-pointer rounded-full border border-custom-border-200 transition-colors duration-200 ease-in-out focus:outline-none ${ } flex-shrink-0 cursor-pointer rounded-full border border-custom-border-200 transition-colors duration-200 ease-in-out focus:outline-none ${
value ? "bg-custom-primary-100" : "bg-gray-700" value ? "bg-custom-primary-100" : "bg-gray-700"
} ${className || ""}`} } ${className || ""} ${disabled ? "cursor-not-allowed" : ""}`}
> >
<span className="sr-only">{label}</span> <span className="sr-only">{label}</span>
<span <span
@ -36,7 +36,7 @@ export const ToggleSwitch: React.FC<Props> = (props) => {
? "translate-x-4" ? "translate-x-4"
: "translate-x-5") + " bg-white" : "translate-x-5") + " bg-white"
: "translate-x-0.5 bg-custom-background-90" : "translate-x-0.5 bg-custom-background-90"
}`} } ${disabled ? "cursor-not-allowed" : ""}`}
/> />
</Switch> </Switch>
); );

View File

@ -2,7 +2,7 @@ import React from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { mutate } from "swr"; import useSWR, { mutate } from "swr";
// services // services
import projectService from "services/project.service"; import projectService from "services/project.service";
@ -21,7 +21,7 @@ import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
import type { NextPage } from "next"; import type { NextPage } from "next";
import { IProject } from "types"; import { IProject } from "types";
// constant // constant
import { PROJECTS_LIST, PROJECT_DETAILS } from "constants/fetch-keys"; import { PROJECTS_LIST, PROJECT_DETAILS, USER_PROJECT_VIEW } from "constants/fetch-keys";
// helper // helper
import { truncateText } from "helpers/string.helper"; import { truncateText } from "helpers/string.helper";
@ -34,6 +34,13 @@ const AutomationsSettings: NextPage = () => {
const { projectDetails } = useProjectDetails(); const { projectDetails } = useProjectDetails();
const { data: memberDetails } = useSWR(
workspaceSlug && projectId ? USER_PROJECT_VIEW(projectId.toString()) : null,
workspaceSlug && projectId
? () => projectService.projectMemberMe(workspaceSlug.toString(), projectId.toString())
: null
);
const handleChange = async (formData: Partial<IProject>) => { const handleChange = async (formData: Partial<IProject>) => {
if (!workspaceSlug || !projectId || !projectDetails) return; if (!workspaceSlug || !projectId || !projectDetails) return;
@ -62,6 +69,8 @@ const AutomationsSettings: NextPage = () => {
}); });
}; };
const isAdmin = memberDetails?.role === 20;
return ( return (
<ProjectAuthorizationWrapper <ProjectAuthorizationWrapper
breadcrumbs={ breadcrumbs={
@ -79,12 +88,20 @@ const AutomationsSettings: NextPage = () => {
<div className="w-80 pt-8 overflow-y-hidden flex-shrink-0"> <div className="w-80 pt-8 overflow-y-hidden flex-shrink-0">
<SettingsSidebar /> <SettingsSidebar />
</div> </div>
<section className="pr-9 py-8 w-full overflow-y-auto"> <section className={`pr-9 py-8 w-full overflow-y-auto ${isAdmin ? "" : "opacity-60"}`}>
<div className="flex items-center py-3.5 border-b border-custom-border-200"> <div className="flex items-center py-3.5 border-b border-custom-border-200">
<h3 className="text-xl font-medium">Automations</h3> <h3 className="text-xl font-medium">Automations</h3>
</div> </div>
<AutoArchiveAutomation projectDetails={projectDetails} handleChange={handleChange} /> <AutoArchiveAutomation
<AutoCloseAutomation projectDetails={projectDetails} handleChange={handleChange} /> projectDetails={projectDetails}
handleChange={handleChange}
disabled={!isAdmin}
/>
<AutoCloseAutomation
projectDetails={projectDetails}
handleChange={handleChange}
disabled={!isAdmin}
/>
</section> </section>
</div> </div>
</ProjectAuthorizationWrapper> </ProjectAuthorizationWrapper>

View File

@ -25,7 +25,7 @@ import { ContrastOutlined } from "@mui/icons-material";
import { IProject } from "types"; import { IProject } from "types";
import type { NextPage } from "next"; import type { NextPage } from "next";
// fetch-keys // fetch-keys
import { PROJECTS_LIST, PROJECT_DETAILS } from "constants/fetch-keys"; import { PROJECTS_LIST, PROJECT_DETAILS, USER_PROJECT_VIEW } from "constants/fetch-keys";
// helper // helper
import { truncateText } from "helpers/string.helper"; import { truncateText } from "helpers/string.helper";
@ -102,6 +102,13 @@ const FeaturesSettings: NextPage = () => {
: null : null
); );
const { data: memberDetails } = useSWR(
workspaceSlug && projectId ? USER_PROJECT_VIEW(projectId.toString()) : null,
workspaceSlug && projectId
? () => projectService.projectMemberMe(workspaceSlug.toString(), projectId.toString())
: null
);
const handleSubmit = async (formData: Partial<IProject>) => { const handleSubmit = async (formData: Partial<IProject>) => {
if (!workspaceSlug || !projectId || !projectDetails) return; if (!workspaceSlug || !projectId || !projectDetails) return;
@ -140,6 +147,8 @@ const FeaturesSettings: NextPage = () => {
); );
}; };
const isAdmin = memberDetails?.role === 20;
return ( return (
<ProjectAuthorizationWrapper <ProjectAuthorizationWrapper
breadcrumbs={ breadcrumbs={
@ -157,7 +166,7 @@ const FeaturesSettings: NextPage = () => {
<div className="w-80 pt-8 overflow-y-hidden flex-shrink-0"> <div className="w-80 pt-8 overflow-y-hidden flex-shrink-0">
<SettingsSidebar /> <SettingsSidebar />
</div> </div>
<section className="pr-9 py-8 w-full overflow-y-auto"> <section className={`pr-9 py-8 w-full overflow-y-auto ${isAdmin ? "" : "opacity-60"}`}>
<div className="flex items-center py-3.5 border-b border-custom-border-200"> <div className="flex items-center py-3.5 border-b border-custom-border-200">
<h3 className="text-xl font-medium">Features</h3> <h3 className="text-xl font-medium">Features</h3>
</div> </div>
@ -199,6 +208,7 @@ const FeaturesSettings: NextPage = () => {
[feature.property]: !projectDetails?.[feature.property as keyof IProject], [feature.property]: !projectDetails?.[feature.property as keyof IProject],
}); });
}} }}
disabled={!isAdmin}
size="sm" size="sm"
/> />
</div> </div>

View File

@ -22,7 +22,7 @@ import emptyIntegration from "public/empty-state/integration.svg";
import { IProject } from "types"; import { IProject } from "types";
import type { NextPage } from "next"; import type { NextPage } from "next";
// fetch-keys // fetch-keys
import { PROJECT_DETAILS, WORKSPACE_INTEGRATIONS } from "constants/fetch-keys"; import { PROJECT_DETAILS, USER_PROJECT_VIEW, WORKSPACE_INTEGRATIONS } from "constants/fetch-keys";
// helper // helper
import { truncateText } from "helpers/string.helper"; import { truncateText } from "helpers/string.helper";
@ -45,6 +45,15 @@ const ProjectIntegrations: NextPage = () => {
: null : null
); );
const { data: memberDetails } = useSWR(
workspaceSlug && projectId ? USER_PROJECT_VIEW(projectId.toString()) : null,
workspaceSlug && projectId
? () => projectService.projectMemberMe(workspaceSlug.toString(), projectId.toString())
: null
);
const isAdmin = memberDetails?.role === 20;
return ( return (
<ProjectAuthorizationWrapper <ProjectAuthorizationWrapper
breadcrumbs={ breadcrumbs={
@ -62,7 +71,7 @@ const ProjectIntegrations: NextPage = () => {
<div className="w-80 pt-8 overflow-y-hidden flex-shrink-0"> <div className="w-80 pt-8 overflow-y-hidden flex-shrink-0">
<SettingsSidebar /> <SettingsSidebar />
</div> </div>
<div className="pr-9 py-8 gap-10 w-full overflow-y-auto"> <div className={`pr-9 py-8 gap-10 w-full overflow-y-auto ${isAdmin ? "" : "opacity-60"}`}>
<div className="flex items-center py-3.5 border-b border-custom-border-200"> <div className="flex items-center py-3.5 border-b border-custom-border-200">
<h3 className="text-xl font-medium">Integrations</h3> <h3 className="text-xl font-medium">Integrations</h3>
</div> </div>
@ -85,6 +94,7 @@ const ProjectIntegrations: NextPage = () => {
text: "Configure now", text: "Configure now",
onClick: () => router.push(`/${workspaceSlug}/settings/integrations`), onClick: () => router.push(`/${workspaceSlug}/settings/integrations`),
}} }}
disabled={!isAdmin}
/> />
) )
) : ( ) : (

View File

@ -43,6 +43,7 @@ import {
PROJECT_INVITATIONS_WITH_EMAIL, PROJECT_INVITATIONS_WITH_EMAIL,
PROJECT_MEMBERS, PROJECT_MEMBERS,
PROJECT_MEMBERS_WITH_EMAIL, PROJECT_MEMBERS_WITH_EMAIL,
USER_PROJECT_VIEW,
WORKSPACE_DETAILS, WORKSPACE_DETAILS,
} from "constants/fetch-keys"; } from "constants/fetch-keys";
// constants // constants
@ -111,6 +112,13 @@ const MembersSettings: NextPage = () => {
: null : null
); );
const { data: memberDetails } = useSWR(
workspaceSlug && projectId ? USER_PROJECT_VIEW(projectId.toString()) : null,
workspaceSlug && projectId
? () => projectService.projectMemberMe(workspaceSlug.toString(), projectId.toString())
: null
);
const members = [ const members = [
...(projectMembers?.map((item) => ({ ...(projectMembers?.map((item) => ({
id: item.id, id: item.id,
@ -212,6 +220,8 @@ const MembersSettings: NextPage = () => {
}); });
}; };
const isAdmin = memberDetails?.role === 20;
return ( return (
<ProjectAuthorizationWrapper <ProjectAuthorizationWrapper
breadcrumbs={ breadcrumbs={
@ -277,7 +287,7 @@ const MembersSettings: NextPage = () => {
<div className="w-80 pt-8 overflow-y-hidden flex-shrink-0"> <div className="w-80 pt-8 overflow-y-hidden flex-shrink-0">
<SettingsSidebar /> <SettingsSidebar />
</div> </div>
<section className="pr-9 py-8 w-full overflow-y-auto"> <section className={`pr-9 py-8 w-full overflow-y-auto`}>
<div className="flex items-center py-3.5 border-b border-custom-border-200"> <div className="flex items-center py-3.5 border-b border-custom-border-200">
<h3 className="text-xl font-medium">Defaults</h3> <h3 className="text-xl font-medium">Defaults</h3>
</div> </div>
@ -296,6 +306,7 @@ const MembersSettings: NextPage = () => {
onChange={(val: string) => { onChange={(val: string) => {
submitChanges({ project_lead: val }); submitChanges({ project_lead: val });
}} }}
isDisabled={!isAdmin}
/> />
)} )}
/> />
@ -320,6 +331,7 @@ const MembersSettings: NextPage = () => {
onChange={(val: string) => { onChange={(val: string) => {
submitChanges({ default_assignee: val }); submitChanges({ default_assignee: val });
}} }}
isDisabled={!isAdmin}
/> />
)} )}
/> />
@ -467,7 +479,7 @@ const MembersSettings: NextPage = () => {
); );
})} })}
</CustomSelect> </CustomSelect>
<CustomMenu ellipsis> <CustomMenu ellipsis disabled={!isAdmin}>
<CustomMenu.MenuItem <CustomMenu.MenuItem
onClick={() => { onClick={() => {
if (member.member) setSelectedRemoveMember(member.id); if (member.member) setSelectedRemoveMember(member.id);