Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script lang="ts">
import Avatar from "@rilldata/web-common/components/avatar/Avatar.svelte";
import Chip from "@rilldata/web-common/components/chip/core/Chip.svelte";
import { getRandomBgColor } from "@rilldata/web-common/features/themes/color-config";
import { cn } from "@rilldata/web-common/lib/shadcn";

Expand All @@ -10,6 +11,7 @@
export let pendingAcceptance: boolean = false;
export let shape: "circle" | "square" = "circle";
export let count: number = 0;
export let role: string | null = null;

function getInitials(name: string) {
return name.charAt(0).toUpperCase();
Expand All @@ -35,11 +37,16 @@
</div>
{/if}
<div class="flex flex-col text-left">
<span class="text-sm font-medium text-gray-900">
<span class="text-sm font-medium text-gray-900 flex flex-row gap-x-1">
{name}
<span class="text-gray-500 font-normal">
{isCurrentUser ? "(You)" : ""}
</span>
{#if role === "guest"}
<Chip type="guest" label="Guest" compact readOnly>
<svelte:fragment slot="body">Guest</svelte:fragment>
</Chip>
{/if}
</span>
{#if pendingAcceptance || email}
<span class="text-xs text-gray-500">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<script lang="ts">
import {
AlertDialog,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@rilldata/web-common/components/alert-dialog/index.js";
import Button from "@rilldata/web-common/components/button/Button.svelte";

export let open = false;
export let email: string;
export let newRole: string;
export let onUpgrade: (email: string, role: string) => void;

async function handleUpgrade() {
try {
onUpgrade(email, newRole);
open = false;
} catch (error) {
console.error("Failed to upgrade user role:", error);
}
}
</script>

<AlertDialog bind:open>
<AlertDialogTrigger asChild>
<div class="hidden"></div>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Upgrade guest to {newRole}?</AlertDialogTitle>
<AlertDialogDescription>
<div class="mt-1">
Upgrading a guest to {newRole} will grant this user access to all open
projects in the organization. Would you like to upgrade this guest user
to {newRole}?
</div>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<Button
type="plain"
on:click={() => {
open = false;
}}>Cancel</Button
>
<Button type="primary" on:click={handleUpgrade}>Yes, upgrade</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
RpcStatus
>;
export let currentUserEmail: string;
export let currentUserRole: string;

const ROW_HEIGHT = 69;
const OVERSCAN = 5;
Expand Down Expand Up @@ -72,19 +73,21 @@
pendingAcceptance: Boolean(row.original.invitedBy),
isCurrentUser: row.original.userEmail === currentUserEmail,
photoUrl: row.original.userPhotoUrl,
role: row.original.roleName,
}),
meta: {
widthPercent: 5,
},
},
{
accessorKey: "roleName",
header: "Role",
header: "Organization Role",
cell: ({ row }) =>
flexRender(OrgUsersTableRoleCell, {
email: row.original.userEmail,
role: row.original.roleName,
isCurrentUser: row.original.userEmail === currentUserEmail,
currentUserRole: currentUserRole,
}),
meta: {
widthPercent: 5,
Expand All @@ -98,7 +101,9 @@
cell: ({ row }) =>
flexRender(OrgUsersTableActionsCell, {
email: row.original.userEmail,
role: row.original.roleName,
isCurrentUser: row.original.userEmail === currentUserEmail,
currentUserRole: currentUserRole,
}),
meta: {
widthPercent: 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,21 @@
import { page } from "$app/stores";

export let email: string;
export let role: string;
export let isCurrentUser: boolean;
export let currentUserRole: string;

let isDropdownOpen = false;
let isRemoveConfirmOpen = false;

$: organization = $page.params.organization;
$: isAdmin = currentUserRole === "admin";
$: isEditor = currentUserRole === "editor";
$: canManageUser =
!isCurrentUser &&
(isAdmin ||
(isEditor &&
(role === "editor" || role === "viewer" || role === "guest")));

const queryClient = useQueryClient();
const removeOrganizationMemberUser =
Expand Down Expand Up @@ -81,7 +90,7 @@
}
</script>

{#if !isCurrentUser}
{#if canManageUser}
<DropdownMenu.Root bind:open={isDropdownOpen}>
<DropdownMenu.Trigger class="flex-none">
<IconButton rounded active={isDropdownOpen}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,39 @@
import { page } from "$app/stores";
import { useQueryClient } from "@tanstack/svelte-query";
import { eventBus } from "@rilldata/web-common/lib/event-bus/event-bus";
import OrgUpgradeGuestConfirmDialog from "./OrgUpgradeGuestConfirmDialog.svelte";
export let email: string;
export let role: string;
export let isCurrentUser: boolean;
export let currentUserRole: string;
let isDropdownOpen = false;
let isUpgradeConfirmOpen = false;
let newRole = "";
$: organization = $page.params.organization;
$: isAdmin = currentUserRole === "admin";
$: isEditor = currentUserRole === "editor";
$: isGuest = role === "guest";
$: canManageUser =
!isCurrentUser &&
(isAdmin ||
(isEditor &&
(role === "editor" || role === "viewer" || role === "guest")));
const queryClient = useQueryClient();
const setOrganizationMemberUserRole =
createAdminServiceSetOrganizationMemberUserRole();
async function handleSetRole(role: string) {
try {
if (isGuest) {
newRole = role;
isUpgradeConfirmOpen = true;
return;
}
await $setOrganizationMemberUserRole.mutateAsync({
organization: organization,
email: email,
Expand Down Expand Up @@ -53,32 +71,65 @@
});
}
}
async function handleUpgrade(email: string, role: string) {
try {
await $setOrganizationMemberUserRole.mutateAsync({
organization: organization,
email: email,
data: {
role: role,
},
});
await queryClient.invalidateQueries({
queryKey:
getAdminServiceListOrganizationMemberUsersQueryKey(organization),
});
await queryClient.invalidateQueries({
queryKey: getAdminServiceListOrganizationInvitesQueryKey(organization),
});
eventBus.emit("notification", {
message: `Guest upgraded to ${role}`,
});
} catch (error) {
console.error("Error upgrading user role", error);
eventBus.emit("notification", {
message: "Error upgrading user role",
type: "error",
});
}
}
</script>

{#if !isCurrentUser}
{#if canManageUser}
<DropdownMenu.Root bind:open={isDropdownOpen}>
<DropdownMenu.Trigger
class="w-18 flex flex-row gap-1 items-center rounded-sm {isDropdownOpen
? 'bg-slate-200'
: 'hover:bg-slate-100'} px-2 py-1"
>
{role ? `Org ${role}` : "-"}
<span class="capitalize">{role ? `${role}` : "-"}</span>
{#if isDropdownOpen}
<CaretUpIcon size="12px" />
{:else}
<CaretDownIcon size="12px" />
{/if}
</DropdownMenu.Trigger>
<DropdownMenu.Content align="start">
<DropdownMenu.CheckboxItem
class="font-normal flex items-center"
checked={role === "admin"}
on:click={() => {
handleSetRole("admin");
}}
>
<span>Admin</span>
</DropdownMenu.CheckboxItem>
{#if isAdmin}
<DropdownMenu.CheckboxItem
class="font-normal flex items-center"
checked={role === "admin"}
on:click={() => {
handleSetRole("admin");
}}
>
<span>Admin</span>
</DropdownMenu.CheckboxItem>
{/if}
<DropdownMenu.CheckboxItem
class="font-normal flex items-center"
checked={role === "editor"}
Expand All @@ -97,19 +148,28 @@
>
<span>Viewer</span>
</DropdownMenu.CheckboxItem>
<DropdownMenu.CheckboxItem
class="font-normal flex items-center"
checked={role === "guest"}
on:click={() => {
handleSetRole("guest");
}}
>
<span>Guest</span>
</DropdownMenu.CheckboxItem>
{#if isAdmin}
<DropdownMenu.CheckboxItem
class="font-normal flex items-center"
checked={role === "guest"}
on:click={() => {
handleSetRole("guest");
}}
>
<span>Guest</span>
</DropdownMenu.CheckboxItem>
{/if}
</DropdownMenu.Content>
</DropdownMenu.Root>
{:else}
<div class="w-18 flex flex-row gap-1 items-center rounded-sm px-2 py-1">
<span>Org {role}</span>
<span class="capitalize">{role}</span>
</div>
{/if}

<OrgUpgradeGuestConfirmDialog
bind:open={isUpgradeConfirmOpen}
{email}
{newRole}
onUpgrade={handleUpgrade}
/>
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@
export let isCurrentUser: boolean;
export let pendingAcceptance: boolean;
export let photoUrl: string | null;
export let role: string;
</script>

<AvatarListItem {name} {email} {isCurrentUser} {pendingAcceptance} {photoUrl} />
<AvatarListItem
{name}
{email}
{isCurrentUser}
{pendingAcceptance}
{photoUrl}
{role}
/>
2 changes: 1 addition & 1 deletion web-admin/src/routes/[organization]/-/users/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
];
</script>

<ContentContainer title="Users" maxWidth={1100}>
<ContentContainer title="Manage users" maxWidth={1100}>
<div class="container flex-col md:flex-row">
<LeftNav {basePage} baseRoute="/[organization]/-/users" {navItems} />
<slot />
Expand Down
Loading