Add role management
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
@@ -5,6 +5,8 @@ import {
|
||||
DialogFooter,
|
||||
Field,
|
||||
Checkbox,
|
||||
Select,
|
||||
Option,
|
||||
useDialogRef,
|
||||
} from "@probo/ui";
|
||||
import type { PropsWithChildren } from "react";
|
||||
@@ -15,6 +17,8 @@ import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
|
||||
import { z } from "zod";
|
||||
import { useFormWithSchema } from "/hooks/useFormWithSchema";
|
||||
import { Controller } from "react-hook-form";
|
||||
import { Suspense } from "react";
|
||||
import { getAssignableRoles } from "/permissions";
|
||||
|
||||
const inviteMutation = graphql`
|
||||
mutation InviteUserDialogMutation(
|
||||
@@ -40,6 +44,7 @@ const inviteMutation = graphql`
|
||||
const schema = z.object({
|
||||
email: z.string().email(),
|
||||
fullName: z.string(),
|
||||
role: z.enum(["OWNER", "ADMIN", "FULL", "VIEWER"]).default("VIEWER"),
|
||||
createPeople: z.boolean().default(false),
|
||||
});
|
||||
|
||||
@@ -48,16 +53,17 @@ type Props = PropsWithChildren & {
|
||||
onRefetch: () => void;
|
||||
};
|
||||
|
||||
export function InviteUserDialog({ children, connectionId, onRefetch }: Props) {
|
||||
function InviteUserDialogContent({ children, connectionId, onRefetch }: Props) {
|
||||
const { __ } = useTranslate();
|
||||
const organizationId = useOrganizationId();
|
||||
const assignableRoles = getAssignableRoles(organizationId);
|
||||
const [inviteUser, isInviting] = useMutationWithToasts(inviteMutation, {
|
||||
successMessage: __("Invitation sent successfully"),
|
||||
errorMessage: __("Failed to send invitation"),
|
||||
});
|
||||
const { register, handleSubmit, formState, reset, control } = useFormWithSchema(
|
||||
schema,
|
||||
{ defaultValues: { createPeople: false } },
|
||||
{ defaultValues: { role: "VIEWER", createPeople: false } },
|
||||
);
|
||||
|
||||
const dialogRef = useDialogRef();
|
||||
@@ -69,6 +75,7 @@ export function InviteUserDialog({ children, connectionId, onRefetch }: Props) {
|
||||
organizationId,
|
||||
email: data.email,
|
||||
fullName: data.fullName,
|
||||
role: data.role,
|
||||
createPeople: data.createPeople,
|
||||
},
|
||||
connections: connectionId ? [connectionId] : ["SettingsPageInvitations_invitations"],
|
||||
@@ -107,6 +114,32 @@ export function InviteUserDialog({ children, connectionId, onRefetch }: Props) {
|
||||
{...register("fullName")}
|
||||
error={formState.errors.fullName?.message}
|
||||
/>
|
||||
<Field label={__("Role")} required>
|
||||
<Controller
|
||||
name="role"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<>
|
||||
<Select value={field.value} onValueChange={field.onChange}>
|
||||
{assignableRoles.includes("OWNER") && <Option value="OWNER">{__("Owner")}</Option>}
|
||||
{assignableRoles.includes("ADMIN") && <Option value="ADMIN">{__("Admin")}</Option>}
|
||||
{assignableRoles.includes("VIEWER") && <Option value="VIEWER">{__("Viewer")}</Option>}
|
||||
</Select>
|
||||
<div className="mt-2 text-sm text-txt-tertiary">
|
||||
{field.value === "OWNER" && (
|
||||
<p>{__("Full access to everything")}</p>
|
||||
)}
|
||||
{field.value === "ADMIN" && (
|
||||
<p>{__("Full access except organization setup and API keys")}</p>
|
||||
)}
|
||||
{field.value === "VIEWER" && (
|
||||
<p>{__("Read-only access")}</p>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</Field>
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center space-x-3">
|
||||
<Controller
|
||||
@@ -142,3 +175,11 @@ export function InviteUserDialog({ children, connectionId, onRefetch }: Props) {
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export function InviteUserDialog(props: Props) {
|
||||
return (
|
||||
<Suspense fallback={props.children}>
|
||||
<InviteUserDialogContent {...props} />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Badge, Button, Card } from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { sprintf } from "@probo/helpers";
|
||||
import type { TrustCenterGraphQuery$data } from "/hooks/graph/__generated__/TrustCenterGraphQuery.graphql";
|
||||
import { Authorized } from "/permissions";
|
||||
|
||||
type Props = {
|
||||
organizationId: string;
|
||||
@@ -76,9 +77,11 @@ export function SlackConnections({ organizationId, slackConnections: connectedSl
|
||||
</Badge>
|
||||
</div>
|
||||
) : (
|
||||
<Button variant="secondary" asChild>
|
||||
<a href={getUrl(slackConnection.id)}>{__("Connect")}</a>
|
||||
</Button>
|
||||
<Authorized entity="TrustCenter" action="updateTrustCenter">
|
||||
<Button variant="secondary" asChild>
|
||||
<a href={getUrl(slackConnection.id)}>{__("Connect")}</a>
|
||||
</Button>
|
||||
</Authorized>
|
||||
)}
|
||||
</Card>
|
||||
))}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<617ea2708c8402c706671dc1a63b1316>>
|
||||
* @generated SignedSource<<eda42f72473c65692ddd9cee68c0ce81>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -9,12 +9,13 @@
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type Role = "ADMIN" | "MEMBER" | "OWNER" | "VIEWER";
|
||||
export type MembershipRole = "ADMIN" | "OWNER" | "VIEWER";
|
||||
export type InviteUserInput = {
|
||||
createPeople: boolean;
|
||||
email: string;
|
||||
fullName: string;
|
||||
organizationId: string;
|
||||
role: MembershipRole;
|
||||
};
|
||||
export type InviteUserDialogMutation$variables = {
|
||||
connections: ReadonlyArray<string>;
|
||||
@@ -30,7 +31,7 @@ export type InviteUserDialogMutation$data = {
|
||||
readonly expiresAt: any;
|
||||
readonly fullName: string;
|
||||
readonly id: string;
|
||||
readonly role: Role;
|
||||
readonly role: MembershipRole;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user