feat: complete admin invite and user creation and recovery flows
This commit is contained in:
@@ -9,6 +9,8 @@ type UserRow = {
|
||||
role: 'admin' | 'user';
|
||||
banned: boolean;
|
||||
twoFactorEnabled: boolean;
|
||||
// created by an admin and has not set a password yet
|
||||
pending: boolean;
|
||||
createdAt: string;
|
||||
lastActiveAt: string | null;
|
||||
};
|
||||
@@ -139,6 +141,12 @@ const signOutEverywhere = (user: UserRow) => run(user, `${user.name} could not b
|
||||
return !error;
|
||||
});
|
||||
|
||||
// The dialog makes the link and shows it, nothing in the list changes. For an
|
||||
// account that has no password yet it is a new setup link instead.
|
||||
const recoverAccount = (user: UserRow) => {
|
||||
openDialog(DialogType.RecoverAccount, undefined, { user });
|
||||
};
|
||||
|
||||
const resetTwoFactor = (user: UserRow) => confirmThen({
|
||||
title: `Reset two-factor for ${user.name}?`,
|
||||
message: 'Turns two-factor sign-in off for this account and throws away its backup codes. They sign in with only their password until they set it up again.',
|
||||
@@ -196,9 +204,8 @@ const createInvite = () => {
|
||||
};
|
||||
|
||||
const openCreateUser = () => {
|
||||
openDialog(DialogType.CreateUser, async (result) => {
|
||||
if (result.ok === false) return;
|
||||
|
||||
// the dialog can be closed after the account was made, so always reload
|
||||
openDialog(DialogType.CreateUser, async () => {
|
||||
await loadUsers();
|
||||
});
|
||||
};
|
||||
@@ -278,9 +285,9 @@ const openCreateUser = () => {
|
||||
</span>
|
||||
|
||||
<span role="cell" class="flex items-center gap-1.5 text-[13px]"
|
||||
:class="u.banned ? 'text-red-400' : 'text-green-400'">
|
||||
:class="u.banned ? 'text-red-400' : u.pending ? 'text-amber-400' : 'text-green-400'">
|
||||
<span aria-hidden="true" class="w-1.75 h-1.75 rounded-full bg-current"></span>
|
||||
{{ u.banned ? 'Disabled' : 'Active' }}
|
||||
{{ u.banned ? 'Disabled' : u.pending ? 'Invited' : 'Active' }}
|
||||
</span>
|
||||
|
||||
<span role="cell" class="text-[13px]"
|
||||
@@ -323,6 +330,11 @@ const openCreateUser = () => {
|
||||
<span class="i-tabler-shield-check text-4.5" aria-hidden="true"></span>
|
||||
Make admin
|
||||
</button>
|
||||
<button type="button" role="menuitem" :class="MENU_ITEM_CLASS"
|
||||
@click="close(); recoverAccount(u)">
|
||||
<span class="i-tabler-key text-4.5" aria-hidden="true"></span>
|
||||
{{ u.pending ? 'New setup link…' : 'Recover account…' }}
|
||||
</button>
|
||||
<button v-if="u.twoFactorEnabled" type="button" role="menuitem" :class="MENU_ITEM_CLASS"
|
||||
@click="close(); resetTwoFactor(u)">
|
||||
<span class="i-tabler-shield-off text-4.5" aria-hidden="true"></span>
|
||||
@@ -365,7 +377,8 @@ const openCreateUser = () => {
|
||||
|
||||
<p class="mt-4 max-w-190 text-[13px] leading-relaxed text-[var(--text-secondary)]">
|
||||
Admins manage accounts, not content: chats, agents, files and API keys of other users are never
|
||||
shown here. Disabled accounts can't sign in and nothing of theirs is deleted. Your own account is
|
||||
shown here. Invited accounts have no password until their owner opens the setup link. Disabled
|
||||
accounts can't sign in and nothing of theirs is deleted. Your own account is
|
||||
managed from Profile and Security, and you can't remove your own admin role.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user