forked from github/plane
chore: user cannot update their own role (#1041)
This commit is contained in:
parent
290318603d
commit
9fdc56db0f
@ -5,6 +5,7 @@ import Image from "next/image";
|
||||
|
||||
import useSWR, { mutate } from "swr";
|
||||
|
||||
// react-hook-form
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
// layouts
|
||||
import { ProjectAuthorizationWrapper } from "layouts/auth-layout";
|
||||
@ -12,6 +13,8 @@ import { ProjectAuthorizationWrapper } from "layouts/auth-layout";
|
||||
import projectService from "services/project.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// components
|
||||
import { SettingsHeader } from "components/project";
|
||||
// ui
|
||||
import { CustomSelect, Loader, SecondaryButton } from "components/ui";
|
||||
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
||||
@ -20,7 +23,6 @@ import { IProject, IWorkspace } from "types";
|
||||
import type { NextPage } from "next";
|
||||
// fetch-keys
|
||||
import { PROJECTS_LIST, PROJECT_DETAILS, PROJECT_MEMBERS } from "constants/fetch-keys";
|
||||
import { SettingsHeader } from "components/project";
|
||||
|
||||
const defaultValues: Partial<IProject> = {
|
||||
project_lead: null,
|
||||
@ -104,7 +106,7 @@ const ControlSettings: NextPage = () => {
|
||||
</Breadcrumbs>
|
||||
}
|
||||
>
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="px-24 py-8">
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="p-8 lg:px-24">
|
||||
<SettingsHeader />
|
||||
<div className="space-y-8 sm:space-y-12">
|
||||
<div className="grid grid-cols-12 items-start gap-4 sm:gap-16">
|
||||
|
@ -13,6 +13,7 @@ import useProjectDetails from "hooks/use-project-details";
|
||||
import { ProjectAuthorizationWrapper } from "layouts/auth-layout";
|
||||
// components
|
||||
import { CreateUpdateEstimateModal, SingleEstimate } from "components/estimates";
|
||||
import { SettingsHeader } from "components/project";
|
||||
//hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// ui
|
||||
@ -27,7 +28,6 @@ import { IEstimate, IProject } from "types";
|
||||
import type { NextPage } from "next";
|
||||
// fetch-keys
|
||||
import { ESTIMATES_LIST, PROJECT_DETAILS } from "constants/fetch-keys";
|
||||
import { SettingsHeader } from "components/project";
|
||||
|
||||
const EstimatesSettings: NextPage = () => {
|
||||
const [estimateFormOpen, setEstimateFormOpen] = useState(false);
|
||||
@ -118,7 +118,7 @@ const EstimatesSettings: NextPage = () => {
|
||||
</Breadcrumbs>
|
||||
}
|
||||
>
|
||||
<div className="px-24 py-8">
|
||||
<div className="p-8 lg:px-24">
|
||||
<SettingsHeader />
|
||||
<section className="flex items-center justify-between">
|
||||
<h3 className="text-2xl font-semibold">Estimates</h3>
|
||||
|
@ -11,6 +11,8 @@ import trackEventServices, { MiscellaneousEventType } from "services/track-event
|
||||
import { ProjectAuthorizationWrapper } from "layouts/auth-layout";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// components
|
||||
import { SettingsHeader } from "components/project";
|
||||
// ui
|
||||
import { SecondaryButton, ToggleSwitch } from "components/ui";
|
||||
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
||||
@ -22,7 +24,6 @@ import { IFavoriteProject, IProject } from "types";
|
||||
import type { NextPage } from "next";
|
||||
// fetch-keys
|
||||
import { FAVORITE_PROJECTS_LIST, PROJECTS_LIST, PROJECT_DETAILS } from "constants/fetch-keys";
|
||||
import { SettingsHeader } from "components/project";
|
||||
|
||||
const featuresList = [
|
||||
{
|
||||
@ -163,7 +164,7 @@ const FeaturesSettings: NextPage = () => {
|
||||
</Breadcrumbs>
|
||||
}
|
||||
>
|
||||
<div className="px-24 py-8">
|
||||
<div className="p-8 lg:px-24">
|
||||
<SettingsHeader />
|
||||
<section className="space-y-8">
|
||||
<h3 className="text-2xl font-semibold">Features</h3>
|
||||
|
@ -151,7 +151,7 @@ const GeneralSettings: NextPage = () => {
|
||||
router.push(`/${workspaceSlug}/projects`);
|
||||
}}
|
||||
/>
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="py-8 px-24">
|
||||
<form onSubmit={handleSubmit(onSubmit)} className="p-8 lg:px-24">
|
||||
<SettingsHeader />
|
||||
<div className="space-y-8 sm:space-y-12">
|
||||
<div className="grid grid-cols-12 items-start gap-4 sm:gap-16">
|
||||
|
@ -54,7 +54,7 @@ const ProjectIntegrations: NextPage = () => {
|
||||
</Breadcrumbs>
|
||||
}
|
||||
>
|
||||
<div className="px-24 py-8">
|
||||
<div className="p-8 lg:px-24">
|
||||
<SettingsHeader />
|
||||
{workspaceIntegrations ? (
|
||||
workspaceIntegrations.length > 0 ? (
|
||||
|
@ -16,6 +16,7 @@ import {
|
||||
SingleLabel,
|
||||
SingleLabelGroup,
|
||||
} from "components/labels";
|
||||
import { SettingsHeader } from "components/project";
|
||||
// ui
|
||||
import { Loader, PrimaryButton } from "components/ui";
|
||||
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
||||
@ -26,7 +27,6 @@ import { IIssueLabels } from "types";
|
||||
import type { NextPage } from "next";
|
||||
// fetch-keys
|
||||
import { PROJECT_DETAILS, PROJECT_ISSUE_LABELS } from "constants/fetch-keys";
|
||||
import { SettingsHeader } from "components/project";
|
||||
|
||||
const LabelsSettings: NextPage = () => {
|
||||
// create/edit label form
|
||||
@ -102,7 +102,7 @@ const LabelsSettings: NextPage = () => {
|
||||
</Breadcrumbs>
|
||||
}
|
||||
>
|
||||
<div className="px-24 py-8">
|
||||
<div className="p-8 lg:px-24">
|
||||
<SettingsHeader />
|
||||
<section className="grid grid-cols-12 gap-10">
|
||||
<div className="col-span-12 sm:col-span-5">
|
||||
|
@ -11,11 +11,13 @@ import workspaceService from "services/workspace.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
import useProjectDetails from "hooks/use-project-details";
|
||||
import useUser from "hooks/use-user";
|
||||
// layouts
|
||||
import { ProjectAuthorizationWrapper } from "layouts/auth-layout";
|
||||
// components
|
||||
import ConfirmProjectMemberRemove from "components/project/confirm-project-member-remove";
|
||||
import SendProjectInvitationModal from "components/project/send-project-invitation-modal";
|
||||
import { SettingsHeader } from "components/project";
|
||||
// ui
|
||||
import { CustomMenu, CustomSelect, Loader } from "components/ui";
|
||||
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
||||
@ -27,7 +29,6 @@ import type { NextPage } from "next";
|
||||
import { PROJECT_INVITATIONS, PROJECT_MEMBERS, WORKSPACE_DETAILS } from "constants/fetch-keys";
|
||||
// constants
|
||||
import { ROLE } from "constants/workspace";
|
||||
import { SettingsHeader } from "components/project";
|
||||
|
||||
const MembersSettings: NextPage = () => {
|
||||
const [inviteModal, setInviteModal] = useState(false);
|
||||
@ -36,17 +37,17 @@ const MembersSettings: NextPage = () => {
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const {
|
||||
query: { workspaceSlug, projectId },
|
||||
} = useRouter();
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
|
||||
const { user } = useUser();
|
||||
const { projectDetails } = useProjectDetails();
|
||||
|
||||
const { data: activeWorkspace } = useSWR(
|
||||
workspaceSlug ? WORKSPACE_DETAILS(workspaceSlug as string) : null,
|
||||
() => (workspaceSlug ? workspaceService.getWorkspace(workspaceSlug as string) : null)
|
||||
);
|
||||
|
||||
const { projectDetails } = useProjectDetails();
|
||||
|
||||
const { data: projectMembers, mutate: mutateMembers } = useSWR(
|
||||
workspaceSlug && projectId ? PROJECT_MEMBERS(projectId as string) : null,
|
||||
workspaceSlug && projectId
|
||||
@ -62,8 +63,9 @@ const MembersSettings: NextPage = () => {
|
||||
);
|
||||
|
||||
const members = [
|
||||
...(projectMembers?.map((item: any) => ({
|
||||
...(projectMembers?.map((item) => ({
|
||||
id: item.id,
|
||||
memberId: item.member?.id,
|
||||
avatar: item.member?.avatar,
|
||||
first_name: item.member?.first_name,
|
||||
last_name: item.member?.last_name,
|
||||
@ -74,6 +76,7 @@ const MembersSettings: NextPage = () => {
|
||||
})) || []),
|
||||
...(projectInvitations?.map((item: any) => ({
|
||||
id: item.id,
|
||||
memberId: item.id,
|
||||
avatar: item.avatar ?? "",
|
||||
first_name: item.first_name ?? item.email,
|
||||
last_name: item.last_name ?? "",
|
||||
@ -142,7 +145,7 @@ const MembersSettings: NextPage = () => {
|
||||
</Breadcrumbs>
|
||||
}
|
||||
>
|
||||
<div className="px-24 py-8">
|
||||
<div className="p-8 lg:px-24">
|
||||
<SettingsHeader />
|
||||
<section className="space-y-8">
|
||||
<div className="flex items-end justify-between gap-4">
|
||||
@ -231,6 +234,7 @@ const MembersSettings: NextPage = () => {
|
||||
});
|
||||
}}
|
||||
position="right"
|
||||
disabled={member.memberId === user?.id}
|
||||
>
|
||||
{Object.keys(ROLE).map((key) => (
|
||||
<CustomSelect.Option key={key} value={key}>
|
||||
|
@ -17,6 +17,7 @@ import {
|
||||
SingleState,
|
||||
StateGroup,
|
||||
} from "components/states";
|
||||
import { SettingsHeader } from "components/project";
|
||||
// ui
|
||||
import { Loader } from "components/ui";
|
||||
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
||||
@ -28,7 +29,6 @@ import { getStatesList, orderStateGroups } from "helpers/state.helper";
|
||||
import type { NextPage } from "next";
|
||||
// fetch-keys
|
||||
import { STATES_LIST } from "constants/fetch-keys";
|
||||
import { SettingsHeader } from "components/project";
|
||||
|
||||
const StatesSettings: NextPage = () => {
|
||||
const [activeGroup, setActiveGroup] = useState<StateGroup>(null);
|
||||
@ -67,7 +67,7 @@ const StatesSettings: NextPage = () => {
|
||||
</Breadcrumbs>
|
||||
}
|
||||
>
|
||||
<div className="px-24 py-8">
|
||||
<div className="p-8 lg:px-24">
|
||||
<SettingsHeader />
|
||||
<div className="grid grid-cols-12 gap-10">
|
||||
<div className="col-span-12 sm:col-span-5">
|
||||
|
@ -39,7 +39,7 @@ const BillingSettings: NextPage = () => {
|
||||
</Breadcrumbs>
|
||||
}
|
||||
>
|
||||
<div className="px-24 py-8">
|
||||
<div className="p-8 lg:px-24">
|
||||
<SettingsHeader />
|
||||
<section className="space-y-8">
|
||||
<div>
|
||||
|
@ -23,7 +23,7 @@ const ImportExport: NextPage = () => {
|
||||
</Breadcrumbs>
|
||||
}
|
||||
>
|
||||
<div className="px-24 py-8">
|
||||
<div className="p-8 lg:px-24">
|
||||
<SettingsHeader />
|
||||
<IntegrationGuide />
|
||||
</div>
|
||||
|
@ -173,7 +173,7 @@ const WorkspaceSettings: NextPage = () => {
|
||||
}}
|
||||
data={activeWorkspace ?? null}
|
||||
/>
|
||||
<div className="px-24 py-8">
|
||||
<div className="p-8 lg:px-24">
|
||||
<SettingsHeader />
|
||||
{activeWorkspace ? (
|
||||
<div className="space-y-8 sm:space-y-12">
|
||||
|
@ -47,7 +47,7 @@ const WorkspaceIntegrations: NextPage = () => {
|
||||
</Breadcrumbs>
|
||||
}
|
||||
>
|
||||
<div className="px-24 py-8">
|
||||
<div className="p-8 lg:px-24">
|
||||
<SettingsHeader />
|
||||
<section className="space-y-8">
|
||||
<div className="flex flex-col items-start gap-3">
|
||||
|
@ -5,10 +5,11 @@ import { useRouter } from "next/router";
|
||||
|
||||
import useSWR from "swr";
|
||||
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
// services
|
||||
import workspaceService from "services/workspace.service";
|
||||
// hooks
|
||||
import useToast from "hooks/use-toast";
|
||||
import useUser from "hooks/use-user";
|
||||
// layouts
|
||||
import { WorkspaceAuthorizationLayout } from "layouts/auth-layout";
|
||||
import { SettingsHeader } from "components/workspace";
|
||||
@ -37,24 +38,27 @@ const MembersSettings: NextPage = () => {
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const { user } = useUser();
|
||||
|
||||
const { data: activeWorkspace } = useSWR(
|
||||
workspaceSlug ? WORKSPACE_DETAILS(workspaceSlug as string) : null,
|
||||
() => (workspaceSlug ? workspaceService.getWorkspace(workspaceSlug as string) : null)
|
||||
workspaceSlug ? WORKSPACE_DETAILS(workspaceSlug.toString()) : null,
|
||||
() => (workspaceSlug ? workspaceService.getWorkspace(workspaceSlug.toString()) : null)
|
||||
);
|
||||
|
||||
const { data: workspaceMembers, mutate: mutateMembers } = useSWR<any[]>(
|
||||
workspaceSlug ? WORKSPACE_MEMBERS(workspaceSlug as string) : null,
|
||||
workspaceSlug ? () => workspaceService.workspaceMembers(workspaceSlug as string) : null
|
||||
const { data: workspaceMembers, mutate: mutateMembers } = useSWR(
|
||||
workspaceSlug ? WORKSPACE_MEMBERS(workspaceSlug.toString()) : null,
|
||||
workspaceSlug ? () => workspaceService.workspaceMembers(workspaceSlug.toString()) : null
|
||||
);
|
||||
|
||||
const { data: workspaceInvitations, mutate: mutateInvitations } = useSWR<any[]>(
|
||||
const { data: workspaceInvitations, mutate: mutateInvitations } = useSWR(
|
||||
workspaceSlug ? WORKSPACE_INVITATIONS : null,
|
||||
workspaceSlug ? () => workspaceService.workspaceInvitations(workspaceSlug as string) : null
|
||||
workspaceSlug ? () => workspaceService.workspaceInvitations(workspaceSlug.toString()) : null
|
||||
);
|
||||
|
||||
const members = [
|
||||
...(workspaceMembers?.map((item) => ({
|
||||
id: item.id,
|
||||
memberId: item.member?.id,
|
||||
avatar: item.member?.avatar,
|
||||
first_name: item.member?.first_name,
|
||||
last_name: item.member?.last_name,
|
||||
@ -65,9 +69,10 @@ const MembersSettings: NextPage = () => {
|
||||
})) || []),
|
||||
...(workspaceInvitations?.map((item) => ({
|
||||
id: item.id,
|
||||
avatar: item.avatar ?? "",
|
||||
first_name: item.first_name ?? item.email,
|
||||
last_name: item.last_name ?? "",
|
||||
memberId: item.id,
|
||||
avatar: "",
|
||||
first_name: item.email,
|
||||
last_name: "",
|
||||
email: item.email,
|
||||
role: item.role,
|
||||
status: item.accepted,
|
||||
@ -138,7 +143,7 @@ const MembersSettings: NextPage = () => {
|
||||
</Breadcrumbs>
|
||||
}
|
||||
>
|
||||
<div className="px-24 py-8">
|
||||
<div className="p-8 lg:px-24">
|
||||
<SettingsHeader />
|
||||
<section className="space-y-8">
|
||||
<div className="flex items-end justify-between gap-4">
|
||||
@ -197,8 +202,10 @@ const MembersSettings: NextPage = () => {
|
||||
label={ROLE[member.role as keyof typeof ROLE]}
|
||||
value={member.role}
|
||||
onChange={(value: any) => {
|
||||
if (!workspaceSlug) return;
|
||||
|
||||
workspaceService
|
||||
.updateWorkspaceMember(activeWorkspace?.slug as string, member.id, {
|
||||
.updateWorkspaceMember(workspaceSlug?.toString(), member.id, {
|
||||
role: value,
|
||||
})
|
||||
.then(() => {
|
||||
@ -224,6 +231,7 @@ const MembersSettings: NextPage = () => {
|
||||
});
|
||||
}}
|
||||
position="right"
|
||||
disabled={member.memberId === user?.id}
|
||||
>
|
||||
{Object.keys(ROLE).map((key) => (
|
||||
<CustomSelect.Option key={key} value={key}>
|
||||
|
1
apps/app/types/projects.d.ts
vendored
1
apps/app/types/projects.d.ts
vendored
@ -60,6 +60,7 @@ type ProjectViewTheme = {
|
||||
};
|
||||
|
||||
export interface IProjectMember {
|
||||
id: string;
|
||||
member: IUserLite;
|
||||
project: IProject;
|
||||
workspace: IWorkspace;
|
||||
|
2
apps/app/types/workspace.d.ts
vendored
2
apps/app/types/workspace.d.ts
vendored
@ -23,9 +23,9 @@ export interface IWorkspaceLite {
|
||||
}
|
||||
|
||||
export interface IWorkspaceMemberInvitation {
|
||||
accepted: boolean;
|
||||
readonly id: string;
|
||||
email: string;
|
||||
accepted: boolean;
|
||||
token: string;
|
||||
message: string;
|
||||
responded_at: Date;
|
||||
|
Loading…
Reference in New Issue
Block a user