Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User management UI #5793

Merged
merged 100 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
59f4f9d
clean up org tabs
lovincyrus Sep 25, 2024
cd0f48c
org users table
lovincyrus Sep 25, 2024
0e091f1
user groups
lovincyrus Sep 26, 2024
ed9ab6c
capitalize
lovincyrus Sep 26, 2024
cd421c3
delete user group action
lovincyrus Sep 26, 2024
f93355a
fix user group deletion
lovincyrus Sep 26, 2024
d308066
rerender
lovincyrus Sep 26, 2024
b622674
class tweaks
lovincyrus Sep 26, 2024
d810323
add basic table component
lovincyrus Sep 26, 2024
3ee0b2b
apply basic table to project resources table
lovincyrus Sep 26, 2024
9b84aa7
set user group role
lovincyrus Sep 26, 2024
e148719
add then set role for user group
lovincyrus Sep 26, 2024
2b724e3
conditional item or checkbox item based on role
lovincyrus Sep 26, 2024
2191491
revoke user group role
lovincyrus Sep 26, 2024
7fd75cd
bug fix
lovincyrus Sep 26, 2024
0ca248c
rename user group dialog
lovincyrus Sep 26, 2024
b0af671
rework forms in user group dialogs
lovincyrus Sep 26, 2024
3476cda
user composite cell
lovincyrus Sep 27, 2024
6d1418b
lint
lovincyrus Sep 27, 2024
5eebb57
lint
lovincyrus Sep 27, 2024
0b5c526
lint
lovincyrus Sep 27, 2024
a014d6d
add user to org
lovincyrus Sep 27, 2024
73689e4
remove user from org
lovincyrus Sep 27, 2024
c279ff6
fix disabled
lovincyrus Sep 27, 2024
631415e
add avatar component
lovincyrus Sep 27, 2024
13f4114
set role for org member user
lovincyrus Sep 27, 2024
641c5bd
lint
lovincyrus Sep 27, 2024
bfae997
disallow current user to change role or remove themselves
lovincyrus Sep 27, 2024
16c4147
show org invites
lovincyrus Sep 27, 2024
4a0314e
fix interface
lovincyrus Sep 27, 2024
5f0ab46
add member to user group
lovincyrus Sep 27, 2024
b1c1de4
allow adding user to user group for verified users
lovincyrus Sep 27, 2024
f239bca
organize dialog footer actions
lovincyrus Sep 27, 2024
18b7d2f
add users to usergroup
lovincyrus Sep 27, 2024
4fd03ff
wip before edit user group dialog
lovincyrus Sep 27, 2024
526c00c
pending invite copy
lovincyrus Sep 30, 2024
5b37dc0
update user round for avatar placeholder when no alt
lovincyrus Sep 30, 2024
55d9970
allow add to group for current user
lovincyrus Sep 30, 2024
2a2a6d2
edit user group dialog, remove user from group
lovincyrus Sep 30, 2024
f60e1fc
lint
lovincyrus Sep 30, 2024
1cb8977
remove rename user group dialog
lovincyrus Sep 30, 2024
53b29c2
polish user group member users list
lovincyrus Sep 30, 2024
3cf2114
pass current user email down
lovincyrus Sep 30, 2024
687e128
update copy
lovincyrus Sep 30, 2024
6590afc
org groups table role cell, set or update role
lovincyrus Sep 30, 2024
eb4c815
clean up
lovincyrus Sep 30, 2024
77a4722
org users table role cell
lovincyrus Sep 30, 2024
9d251da
lint
lovincyrus Sep 30, 2024
341396f
rework add users
lovincyrus Oct 1, 2024
e6a7304
lint
lovincyrus Oct 1, 2024
ad5cf66
basic table tweaks, widthPercent addition
lovincyrus Oct 1, 2024
951662f
lint
lovincyrus Oct 1, 2024
1a9f987
search in users table, empty text
lovincyrus Oct 1, 2024
6c44acb
search in groups table
lovincyrus Oct 1, 2024
556a987
nit
lovincyrus Oct 1, 2024
5b59f14
lint
lovincyrus Oct 1, 2024
d6c0670
clean up, hide remove for all-users
lovincyrus Oct 1, 2024
393cd8d
lint
lovincyrus Oct 1, 2024
dea2391
pre combobox
lovincyrus Oct 1, 2024
416e4db
use combobox, search and add users in create group dialog
lovincyrus Oct 1, 2024
0e3405a
remove unused, clean up for all-users group
lovincyrus Oct 1, 2024
60210fc
lint
lovincyrus Oct 1, 2024
9995fe7
ability to add users to usergroup in edit group dialog
lovincyrus Oct 2, 2024
d162cee
remove adding users to usergroup in create group dialog
lovincyrus Oct 2, 2024
f4453e4
add avatar circle list, polish typeahead
lovincyrus Oct 2, 2024
eec700f
lint
lovincyrus Oct 2, 2024
f825e5d
filter out users that are already in the group
lovincyrus Oct 2, 2024
ce0b231
lint
lovincyrus Oct 2, 2024
d194b5a
danger text for remove text button
lovincyrus Oct 2, 2024
5fa7abe
clean up
lovincyrus Oct 2, 2024
f2d3832
fix input styles in combobox
lovincyrus Oct 2, 2024
e733ae0
remove unused
lovincyrus Oct 2, 2024
0e1fea8
try
lovincyrus Oct 2, 2024
42ca4d3
lint
lovincyrus Oct 2, 2024
01a2702
bump typecheck nonsvelte files
lovincyrus Oct 2, 2024
dced8a7
add scrollable table config, random color wip
lovincyrus Oct 3, 2024
8f1d963
comment
lovincyrus Oct 3, 2024
9b58bb7
clean up
lovincyrus Oct 3, 2024
c33dd80
clean up
lovincyrus Oct 3, 2024
9c50c19
clean up
lovincyrus Oct 3, 2024
5737505
scope out usergroup changes
lovincyrus Oct 3, 2024
bc1b6ac
comment
lovincyrus Oct 3, 2024
9f77ae4
remove collaborator role option
lovincyrus Oct 3, 2024
ea657d1
use random color, non-gray, non-primary-secondary
lovincyrus Oct 4, 2024
b88aa25
clean up
lovincyrus Oct 4, 2024
b4d3bb0
Revert "bump typecheck nonsvelte files"
lovincyrus Oct 7, 2024
094b558
add chevron to role change
lovincyrus Oct 7, 2024
df4845e
Reapply "bump typecheck nonsvelte files"
lovincyrus Oct 8, 2024
3412c43
horizontal line feedback
lovincyrus Oct 8, 2024
ae2f7e6
lint
lovincyrus Oct 8, 2024
c93683f
text danger
lovincyrus Oct 8, 2024
1425285
deterministic bg color client side
lovincyrus Oct 8, 2024
ad36018
comment out desc from settings
lovincyrus Oct 8, 2024
ca0f662
noop comment
lovincyrus Oct 8, 2024
4a6764e
Revert "comment out desc from settings"
lovincyrus Oct 8, 2024
a3a6603
feedback
lovincyrus Oct 11, 2024
9cfebb5
update src alt conditionals from avatar
lovincyrus Oct 11, 2024
dc81139
feedback
lovincyrus Oct 11, 2024
f18abca
fix rebase
lovincyrus Oct 11, 2024
2245fd5
avoid prop drilling, feedback
lovincyrus Oct 11, 2024
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
7 changes: 1 addition & 6 deletions web-admin/src/features/navigation/TopNavigationBar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import { useReports } from "../scheduled-reports/selectors";
import {
isMetricsExplorerPage,
isOrganizationPage,
isProjectPage,
isPublicURLPage,
} from "./nav-utils";
Expand All @@ -56,7 +55,6 @@
$: onReportPage = !!report;
$: onMetricsExplorerPage = isMetricsExplorerPage($page);
$: onPublicURLPage = isPublicURLPage($page);
$: onOrgPage = isOrganizationPage($page);

$: loggedIn = !!$user.data?.user;
$: rillLogoHref = !loggedIn ? "https://www.rilldata.com" : "/";
Expand Down Expand Up @@ -160,10 +158,7 @@
$: currentPath = [organization, project, dashboard, report || alert];
</script>

<div
class="flex items-center w-full pr-4 pl-2 py-1"
class:border-b={!onProjectPage && !onOrgPage}
>
<div class="flex items-center w-full pr-4 pl-2 py-1">
<!-- Left side -->
<a
href={rillLogoHref}
Expand Down
14 changes: 8 additions & 6 deletions web-admin/src/features/organizations/OrganizationTabs.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,17 @@
];

if (data.permissions.manageOrgMembers) {
// TODO: users page
tabs.push({
route: `/${organization}/-/users`,
label: "Users",
});
}

if (data.permissions.manageOrg) {
// TODO: once settings page is filled in we add these
// tabs.push({
// route: `/${organization}/-/settings`,
// label: "Settings",
// });
tabs.push({
route: `/${organization}/-/settings`,
label: "Settings",
});
}

return tabs;
Expand Down
199 changes: 199 additions & 0 deletions web-admin/src/features/organizations/users/AddUsersDialog.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
<script lang="ts">
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@rilldata/web-common/components/dialog-v2";
import { Button } from "@rilldata/web-common/components/button/index.js";
import { defaults, superForm } from "sveltekit-superforms";
import { yup } from "sveltekit-superforms/adapters";
import { array, object, string } from "yup";
import MultiInput from "@rilldata/web-common/components/forms/MultiInput.svelte";
import UserRoleSelect from "@rilldata/web-admin/features/projects/user-invite/UserRoleSelect.svelte";
import { RFC5322EmailRegex } from "@rilldata/web-common/components/forms/validation";
import { eventBus } from "@rilldata/web-common/lib/event-bus/event-bus";
import { useQueryClient } from "@tanstack/svelte-query";
import {
createAdminServiceAddOrganizationMemberUser,
getAdminServiceListOrganizationInvitesQueryKey,
getAdminServiceListOrganizationMemberUsersQueryKey,
} from "@rilldata/web-admin/client";
import { page } from "$app/stores";

export let open = false;
export let email: string;
export let role: string;
export let isSuperUser: boolean;

$: organization = $page.params.organization;

const queryClient = useQueryClient();
const addOrganizationMemberUser =
createAdminServiceAddOrganizationMemberUser();

async function handleCreate(
newEmail: string,
newRole: string,
isSuperUser: boolean = false,
) {
try {
await $addOrganizationMemberUser.mutateAsync({
organization: organization,
data: {
email: newEmail,
role: newRole,
superuserForceAccess: isSuperUser,
},
});

await queryClient.invalidateQueries(
getAdminServiceListOrganizationMemberUsersQueryKey(organization),
);

await queryClient.invalidateQueries(
getAdminServiceListOrganizationInvitesQueryKey(organization),
);

email = "";
role = "";
isSuperUser = false;
open = false;

eventBus.emit("notification", { message: "User added to organization" });
} catch (error) {
console.error("Error adding user to organization", error);
eventBus.emit("notification", {
message: "Error adding user to organization",
type: "error",
});
}
}

const formId = "add-user-form";

const initialValues: {
emails: string[];
role: string;
} = {
emails: [""],
role: "viewer",
};
const schema = yup(
object({
emails: array(
string().matches(RFC5322EmailRegex, {
excludeEmptyString: true,
message: "Invalid email",
}),
), // yup's email regex is too simple
role: string().required(),
}),
);

const { form, errors, enhance, submit, submitting } = superForm(
defaults(initialValues, schema),
{
SPA: true,
validators: schema,
async onUpdate({ form }) {
if (!form.valid) return;
const values = form.data;
const emails = values.emails.map((e) => e.trim()).filter(Boolean);
if (emails.length === 0) return;

const succeeded = [];
let errored = false;
await Promise.all(
emails.map(async (email) => {
try {
await handleCreate(email, values.role, isSuperUser);
succeeded.push(email);
} catch (error) {
console.error("Error adding user to organization", error);
errored = true;
}
}),
);

eventBus.emit("notification", {
type: "success",
message: `Invited ${succeeded.length} ${succeeded.length === 1 ? "person" : "people"} as ${values.role}`,
});

if (errored) {
eventBus.emit("notification", {
type: "error",
message:
"Some invitations could not be sent. Please check the email addresses and try again.",
});
}
},
validationMethod: "oninput",
},
);

$: hasInvalidEmails = $form.emails.some(
(e, i) => e.length > 0 && $errors.emails?.[i] !== undefined,
);
</script>

<Dialog
bind:open
onOutsideClick={(e) => {
e.preventDefault();
open = false;
email = "";
role = "";
isSuperUser = false;
}}
onOpenChange={(open) => {
if (!open) {
email = "";
role = "";
isSuperUser = false;
}
}}
>
<DialogTrigger asChild>
<div class="hidden"></div>
</DialogTrigger>
<DialogContent class="translate-y-[-200px]">
<DialogHeader>
<DialogTitle>Add users</DialogTitle>
</DialogHeader>
<form
id={formId}
on:submit|preventDefault={submit}
class="w-full"
use:enhance
>
<MultiInput
id="emails"
placeholder="Add emails, separated by commas"
contentClassName="relative"
bind:values={$form.emails}
errors={$errors.emails}
singular="email"
plural="emails"
>
<div slot="within-input" class="flex items-center h-full">
<UserRoleSelect bind:value={$form.role} />
</div>
<svelte:fragment slot="beside-input" let:hasSomeValue>
<Button
submitForm
type="primary"
form={formId}
loading={$submitting}
disabled={hasInvalidEmails || !hasSomeValue}
forcedStyle="height: 32px !important;"
>
Invite
</Button>
</svelte:fragment>
</MultiInput>
</form>
</DialogContent>
</Dialog>
28 changes: 28 additions & 0 deletions web-admin/src/features/organizations/users/AvatarCircleList.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<script lang="ts">
import Avatar from "@rilldata/web-common/components/avatar/Avatar.svelte";
import { getRandomColor } from "@rilldata/web-common/features/themes/color-config";

export let name: string;
export let email: string;
export let isCurrentUser: boolean = false;
export let pendingAcceptance: boolean = false;
</script>

<div class="flex items-center gap-2">
<Avatar
size="h-7 w-7"
alt={pendingAcceptance ? null : name}
bgColor={getRandomColor(email)}
/>
<div class="flex flex-col text-left">
<span class="text-sm font-medium text-gray-900">
{name}
<span class="text-gray-500 font-normal">
{isCurrentUser ? "(You)" : ""}
</span>
</span>
<span class="text-xs text-gray-500"
>{pendingAcceptance ? "Pending invitation" : email}</span
>
</div>
</div>
Loading
Loading