Skip to content

Commit

Permalink
Merge pull request #1311 from makeplane/stage-release
Browse files Browse the repository at this point in the history
promote: staging to master
  • Loading branch information
vamsi authored Jun 16, 2023
2 parents 02111d7 + 4c08572 commit dbbce43
Show file tree
Hide file tree
Showing 7 changed files with 412 additions and 329 deletions.
5 changes: 5 additions & 0 deletions apiserver/plane/api/views/workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@ def post(self, request, slug):
{"error": "Emails are required"}, status=status.HTTP_400_BAD_REQUEST
)

# check for role level
requesting_user = WorkspaceMember.objects.get(workspace__slug=slug, member=request.user)
if len([email for email in emails if int(email.get("role", 10)) > requesting_user.role]):
return Response({"error": "You cannot invite a user with higher role"}, status=status.HTTP_400_BAD_REQUEST)

workspace = Workspace.objects.get(slug=slug)

# Check if user is already a member of workspace
Expand Down
21 changes: 14 additions & 7 deletions apps/app/components/project/send-project-invitation-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ import { useForm, Controller } from "react-hook-form";
import { Dialog, Transition } from "@headlessui/react";
// ui
import { CustomSelect, PrimaryButton, SecondaryButton, TextArea } from "components/ui";
// hooks
import useToast from "hooks/use-toast";
// services
import projectService from "services/project.service";
import workspaceService from "services/workspace.service";
// contexts
import { useProjectMyMembership } from "contexts/project-member.context";
// hooks
import useToast from "hooks/use-toast";
// types
import { ICurrentUserResponse, IProjectMemberInvitation } from "types";
// fetch-keys
Expand Down Expand Up @@ -46,6 +48,7 @@ const SendProjectInvitationModal: React.FC<Props> = ({ isOpen, setIsOpen, member
const { workspaceSlug, projectId } = router.query;

const { setToastAlert } = useToast();
const { memberDetails } = useProjectMyMembership();

const { data: people } = useSWR(
workspaceSlug ? WORKSPACE_MEMBERS(workspaceSlug as string) : null,
Expand Down Expand Up @@ -202,11 +205,15 @@ const SendProjectInvitationModal: React.FC<Props> = ({ isOpen, setIsOpen, member
input
width="w-full"
>
{Object.entries(ROLE).map(([key, label]) => (
<CustomSelect.Option key={key} value={key}>
{label}
</CustomSelect.Option>
))}
{Object.entries(ROLE).map(([key, label]) => {
if (parseInt(key) > (memberDetails?.role ?? 5)) return null;

return (
<CustomSelect.Option key={key} value={key}>
{label}
</CustomSelect.Option>
);
})}
</CustomSelect>
)}
/>
Expand Down
26 changes: 18 additions & 8 deletions apps/app/components/workspace/send-workspace-invitation-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
import React from "react";

import { mutate } from "swr";

// react-hook-form
import { Controller, useForm } from "react-hook-form";
// headless
import { Dialog, Transition } from "@headlessui/react";
// services
import workspaceService from "services/workspace.service";
// ui
import { CustomSelect, Input, PrimaryButton, SecondaryButton } from "components/ui";
// contexts
import { useWorkspaceMyMembership } from "contexts/workspace-member.context";
// hooks
import useToast from "hooks/use-toast";
// ui
import { CustomSelect, Input, PrimaryButton, SecondaryButton } from "components/ui";
// types
import { ICurrentUserResponse, IWorkspaceMemberInvitation } from "types";
// fetch keys
// fetch-keys
import { WORKSPACE_INVITATIONS } from "constants/fetch-keys";
// constants
import { ROLE } from "constants/workspace";
Expand All @@ -37,6 +42,7 @@ const SendWorkspaceInvitationModal: React.FC<Props> = ({
user,
}) => {
const { setToastAlert } = useToast();
const { memberDetails } = useWorkspaceMyMembership();

const {
control,
Expand Down Expand Up @@ -145,11 +151,15 @@ const SendWorkspaceInvitationModal: React.FC<Props> = ({
width="w-full"
input
>
{Object.entries(ROLE).map(([key, value]) => (
<CustomSelect.Option key={key} value={key}>
{value}
</CustomSelect.Option>
))}
{Object.entries(ROLE).map(([key, value]) => {
if (parseInt(key) > (memberDetails?.role ?? 5)) return null;

return (
<CustomSelect.Option key={key} value={key}>
{value}
</CustomSelect.Option>
);
})}
</CustomSelect>
)}
/>
Expand Down
61 changes: 61 additions & 0 deletions apps/app/contexts/workspace-member.context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { createContext, useContext } from "react";

// next
import { useRouter } from "next/router";

import useSWR from "swr";
// services
import workspaceService from "services/workspace.service";
// types
import { IWorkspaceMember } from "types";
// fetch-keys
import { WORKSPACE_MEMBERS_ME } from "constants/fetch-keys";

type ContextType = {
loading: boolean;
memberDetails?: IWorkspaceMember;
error: any;
};

export const WorkspaceMemberContext = createContext<ContextType>({} as ContextType);

type Props = {
children: React.ReactNode;
};

export const WorkspaceMemberProvider: React.FC<Props> = (props) => {
const { children } = props;

const router = useRouter();
const { workspaceSlug } = router.query;

const { data: memberDetails, error } = useSWR(
workspaceSlug ? WORKSPACE_MEMBERS_ME(workspaceSlug.toString()) : null,
workspaceSlug ? () => workspaceService.workspaceMemberMe(workspaceSlug.toString()) : null
);

const loading = !memberDetails && !error;

return (
<WorkspaceMemberContext.Provider value={{ loading, memberDetails, error }}>
{children}
</WorkspaceMemberContext.Provider>
);
};

export const useWorkspaceMyMembership = () => {
const context = useContext(WorkspaceMemberContext);

if (context === undefined)
throw new Error(`useWorkspaceMember must be used within a WorkspaceMemberProvider.`);

return {
...context,
memberRole: {
isOwner: context.memberDetails?.role === 20,
isMember: context.memberDetails?.role === 15,
isViewer: context.memberDetails?.role === 10,
isGuest: context.memberDetails?.role === 5,
},
};
};
86 changes: 45 additions & 41 deletions apps/app/layouts/auth-layout/workspace-authorization-wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import useSWR from "swr";

// services
import workspaceServices from "services/workspace.service";
// contexts
import { WorkspaceMemberProvider } from "contexts/workspace-member.context";
// layouts
import AppSidebar from "layouts/app-layout/app-sidebar";
import AppHeader from "layouts/app-layout/app-header";
Expand Down Expand Up @@ -78,48 +80,50 @@ export const WorkspaceAuthorizationLayout: React.FC<Props> = ({

return (
<UserAuthorizationLayout>
<CommandPalette />
<div className="relative flex h-screen w-full overflow-hidden">
<AppSidebar toggleSidebar={toggleSidebar} setToggleSidebar={setToggleSidebar} />
{settingsLayout && (memberType?.isGuest || memberType?.isViewer) ? (
<NotAuthorizedView
actionButton={
<Link href={`/${workspaceSlug}`}>
<a>
<PrimaryButton className="flex items-center gap-1">
<LayerDiagonalIcon height={16} width={16} color="white" /> Go to workspace
</PrimaryButton>
</a>
</Link>
}
type="workspace"
/>
) : (
<main
className={`relative flex h-full w-full flex-col overflow-hidden ${
bg === "primary"
? "bg-brand-surface-1"
: bg === "secondary"
? "bg-brand-sidebar"
: "bg-brand-base"
}`}
>
{!noHeader && (
<AppHeader
breadcrumbs={breadcrumbs}
left={left}
right={right}
setToggleSidebar={setToggleSidebar}
/>
)}
<div className="h-full w-full overflow-hidden">
<div className="relative h-full w-full overflow-x-hidden overflow-y-scroll">
{children}
<WorkspaceMemberProvider>
<CommandPalette />
<div className="relative flex h-screen w-full overflow-hidden">
<AppSidebar toggleSidebar={toggleSidebar} setToggleSidebar={setToggleSidebar} />
{settingsLayout && (memberType?.isGuest || memberType?.isViewer) ? (
<NotAuthorizedView
actionButton={
<Link href={`/${workspaceSlug}`}>
<a>
<PrimaryButton className="flex items-center gap-1">
<LayerDiagonalIcon height={16} width={16} color="white" /> Go to workspace
</PrimaryButton>
</a>
</Link>
}
type="workspace"
/>
) : (
<main
className={`relative flex h-full w-full flex-col overflow-hidden ${
bg === "primary"
? "bg-brand-surface-1"
: bg === "secondary"
? "bg-brand-sidebar"
: "bg-brand-base"
}`}
>
{!noHeader && (
<AppHeader
breadcrumbs={breadcrumbs}
left={left}
right={right}
setToggleSidebar={setToggleSidebar}
/>
)}
<div className="h-full w-full overflow-hidden">
<div className="relative h-full w-full overflow-x-hidden overflow-y-scroll">
{children}
</div>
</div>
</div>
</main>
)}
</div>
</main>
)}
</div>
</WorkspaceMemberProvider>
</UserAuthorizationLayout>
);
};
Loading

1 comment on commit dbbce43

@vercel
Copy link

@vercel vercel bot commented on dbbce43 Jun 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

plane – ./apps/app

plane-plane.vercel.app
plane-theta.vercel.app
plane-git-master-plane.vercel.app
app.plane.so

Please sign in to comment.