refactor(sh-admin): updated invite flow for the admin dashboard (#4176)
* feat: new success invite link modal introduced * refactor: replaced button with lucide icon * style: vertical padding to lucide icon * feat: new success invite modal component * refactor: hide copy link button when there are no pending invites * refactor: changed copy link button to copy invite link * chore: minor UI update --------- Co-authored-by: nivedin <nivedinp@gmail.com>
This commit is contained in:
committed by
GitHub
parent
9cde6c597b
commit
9e445cda84
@@ -166,6 +166,8 @@
|
|||||||
"link_copied_to_clipboard": "Link copied to clipboard",
|
"link_copied_to_clipboard": "Link copied to clipboard",
|
||||||
"logged_out": "Logged out",
|
"logged_out": "Logged out",
|
||||||
"login_as_admin": "and login with an admin account.",
|
"login_as_admin": "and login with an admin account.",
|
||||||
|
"login_using_email": "Please ask the user to check their email or share the link below",
|
||||||
|
"login_using_link": "Please ask the user to login using the link below",
|
||||||
"logout": "Logout",
|
"logout": "Logout",
|
||||||
"magic_link_sign_in": "Click on the link to sign in.",
|
"magic_link_sign_in": "Click on the link to sign in.",
|
||||||
"magic_link_success": "We sent a magic link to",
|
"magic_link_success": "We sent a magic link to",
|
||||||
@@ -269,10 +271,12 @@
|
|||||||
"valid_owner_email": "Please enter a valid owner email"
|
"valid_owner_email": "Please enter a valid owner email"
|
||||||
},
|
},
|
||||||
"users": {
|
"users": {
|
||||||
|
"add_user": "Add User",
|
||||||
"admin": "Admin",
|
"admin": "Admin",
|
||||||
"admin_id": "Admin ID",
|
"admin_id": "Admin ID",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"created_on": "Created On",
|
"created_on": "Created On",
|
||||||
|
"copy_invite_link": "Copy Invite Link",
|
||||||
"copy_link": "Copy Link",
|
"copy_link": "Copy Link",
|
||||||
"date": "Date",
|
"date": "Date",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
@@ -296,6 +300,7 @@
|
|||||||
"load_list_error": "Unable to Load Users List",
|
"load_list_error": "Unable to Load Users List",
|
||||||
"make_admin": "Make Admin",
|
"make_admin": "Make Admin",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
|
"new_user_added": "New User Added",
|
||||||
"no_invite": "No pending invites found",
|
"no_invite": "No pending invites found",
|
||||||
"no_shared_requests": "No shared requests created by the user",
|
"no_shared_requests": "No shared requests created by the user",
|
||||||
"no_users": "No users found",
|
"no_users": "No users found",
|
||||||
|
|||||||
@@ -17,21 +17,29 @@ declare module '@vue/runtime-core' {
|
|||||||
HoppButtonPrimary: typeof import('@hoppscotch/ui')['HoppButtonPrimary']
|
HoppButtonPrimary: typeof import('@hoppscotch/ui')['HoppButtonPrimary']
|
||||||
HoppButtonSecondary: typeof import('@hoppscotch/ui')['HoppButtonSecondary']
|
HoppButtonSecondary: typeof import('@hoppscotch/ui')['HoppButtonSecondary']
|
||||||
HoppSmartAnchor: typeof import('@hoppscotch/ui')['HoppSmartAnchor']
|
HoppSmartAnchor: typeof import('@hoppscotch/ui')['HoppSmartAnchor']
|
||||||
|
HoppSmartAutoComplete: typeof import('@hoppscotch/ui')['HoppSmartAutoComplete']
|
||||||
HoppSmartCheckbox: typeof import('@hoppscotch/ui')['HoppSmartCheckbox']
|
HoppSmartCheckbox: typeof import('@hoppscotch/ui')['HoppSmartCheckbox']
|
||||||
HoppSmartConfirmModal: typeof import('@hoppscotch/ui')['HoppSmartConfirmModal']
|
HoppSmartConfirmModal: typeof import('@hoppscotch/ui')['HoppSmartConfirmModal']
|
||||||
HoppSmartInput: typeof import('@hoppscotch/ui')['HoppSmartInput']
|
HoppSmartInput: typeof import('@hoppscotch/ui')['HoppSmartInput']
|
||||||
|
HoppSmartIntersection: typeof import('@hoppscotch/ui')['HoppSmartIntersection']
|
||||||
HoppSmartItem: typeof import('@hoppscotch/ui')['HoppSmartItem']
|
HoppSmartItem: typeof import('@hoppscotch/ui')['HoppSmartItem']
|
||||||
HoppSmartLink: typeof import('@hoppscotch/ui')['HoppSmartLink']
|
HoppSmartLink: typeof import('@hoppscotch/ui')['HoppSmartLink']
|
||||||
HoppSmartModal: typeof import('@hoppscotch/ui')['HoppSmartModal']
|
HoppSmartModal: typeof import('@hoppscotch/ui')['HoppSmartModal']
|
||||||
HoppSmartPicture: typeof import('@hoppscotch/ui')['HoppSmartPicture']
|
HoppSmartPicture: typeof import('@hoppscotch/ui')['HoppSmartPicture']
|
||||||
|
HoppSmartPlaceholder: typeof import('@hoppscotch/ui')['HoppSmartPlaceholder']
|
||||||
|
HoppSmartSelectWrapper: typeof import('@hoppscotch/ui')['HoppSmartSelectWrapper']
|
||||||
HoppSmartSpinner: typeof import('@hoppscotch/ui')['HoppSmartSpinner']
|
HoppSmartSpinner: typeof import('@hoppscotch/ui')['HoppSmartSpinner']
|
||||||
HoppSmartTab: typeof import('@hoppscotch/ui')['HoppSmartTab']
|
HoppSmartTab: typeof import('@hoppscotch/ui')['HoppSmartTab']
|
||||||
HoppSmartTable: typeof import('@hoppscotch/ui')['HoppSmartTable']
|
HoppSmartTable: typeof import('@hoppscotch/ui')['HoppSmartTable']
|
||||||
HoppSmartTabs: typeof import('@hoppscotch/ui')['HoppSmartTabs']
|
HoppSmartTabs: typeof import('@hoppscotch/ui')['HoppSmartTabs']
|
||||||
HoppSmartToggle: typeof import('@hoppscotch/ui')['HoppSmartToggle']
|
HoppSmartToggle: typeof import('@hoppscotch/ui')['HoppSmartToggle']
|
||||||
IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default']
|
IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default']
|
||||||
|
IconLucideCheck: typeof import('~icons/lucide/check')['default']
|
||||||
|
IconLucideChevronDown: typeof import('~icons/lucide/chevron-down')['default']
|
||||||
|
IconLucideHelpCircle: typeof import('~icons/lucide/help-circle')['default']
|
||||||
IconLucideInbox: typeof import('~icons/lucide/inbox')['default']
|
IconLucideInbox: typeof import('~icons/lucide/inbox')['default']
|
||||||
IconLucideSearch: typeof import('~icons/lucide/search')['default']
|
IconLucideSearch: typeof import('~icons/lucide/search')['default']
|
||||||
|
IconLucideUser: typeof import('~icons/lucide/user')['default']
|
||||||
SettingsAuthProvider: typeof import('./components/settings/AuthProvider.vue')['default']
|
SettingsAuthProvider: typeof import('./components/settings/AuthProvider.vue')['default']
|
||||||
SettingsConfigurations: typeof import('./components/settings/Configurations.vue')['default']
|
SettingsConfigurations: typeof import('./components/settings/Configurations.vue')['default']
|
||||||
SettingsDataSharing: typeof import('./components/settings/DataSharing.vue')['default']
|
SettingsDataSharing: typeof import('./components/settings/DataSharing.vue')['default']
|
||||||
@@ -49,6 +57,7 @@ declare module '@vue/runtime-core' {
|
|||||||
UsersDetails: typeof import('./components/users/Details.vue')['default']
|
UsersDetails: typeof import('./components/users/Details.vue')['default']
|
||||||
UsersInviteModal: typeof import('./components/users/InviteModal.vue')['default']
|
UsersInviteModal: typeof import('./components/users/InviteModal.vue')['default']
|
||||||
UsersSharedRequests: typeof import('./components/users/SharedRequests.vue')['default']
|
UsersSharedRequests: typeof import('./components/users/SharedRequests.vue')['default']
|
||||||
|
UsersSuccessInviteModal: typeof import('./components/users/SuccessInviteModal.vue')['default']
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,7 @@
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="flex space-x-2">
|
<span class="flex space-x-2">
|
||||||
<HoppButtonPrimary
|
<HoppButtonPrimary
|
||||||
:disabled="!smtpEnabled"
|
:label="t('users.add_user')"
|
||||||
:label="t('users.send_invite')"
|
|
||||||
@click="emit('send-invite', email)"
|
@click="emit('send-invite', email)"
|
||||||
/>
|
/>
|
||||||
<HoppButtonSecondary
|
<HoppButtonSecondary
|
||||||
@@ -26,12 +25,6 @@
|
|||||||
@click="hideModal"
|
@click="hideModal"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<HoppButtonSecondary
|
|
||||||
:label="t('users.copy_link')"
|
|
||||||
outline
|
|
||||||
filled
|
|
||||||
@click="emit('copy-invite-link', email)"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
</HoppSmartModal>
|
</HoppSmartModal>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
<template>
|
||||||
|
<HoppSmartModal
|
||||||
|
dialog
|
||||||
|
:title="t('users.new_user_added')"
|
||||||
|
@close="inviteSuccessModal = false"
|
||||||
|
>
|
||||||
|
<template #body>
|
||||||
|
<icon-lucide-check
|
||||||
|
class="text-4xl text-emerald-500 w-min mx-auto my-3 p-3 bg-primaryDark rounded-full"
|
||||||
|
/>
|
||||||
|
<p class="text-center my-2">
|
||||||
|
{{
|
||||||
|
smtpEnabled
|
||||||
|
? t('state.login_using_email')
|
||||||
|
: t('state.login_using_link')
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
<div class="flex p-3 mx-10">
|
||||||
|
<input
|
||||||
|
:value="baseURL"
|
||||||
|
class="input rounded-r-none"
|
||||||
|
placeholder=""
|
||||||
|
type="text"
|
||||||
|
autocomplete="off"
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
<div class="bg-primaryDark rounded-r-sm">
|
||||||
|
<UiAutoResetIcon
|
||||||
|
:title="t('users.copy_link')"
|
||||||
|
:icon="{ default: IconCopy, temporary: IconCheck }"
|
||||||
|
@click="emit('copy-invite-link')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</HoppSmartModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useVModel } from '@vueuse/core';
|
||||||
|
import { useI18n } from '~/composables/i18n';
|
||||||
|
|
||||||
|
import IconCheck from '~icons/lucide/check';
|
||||||
|
import IconCopy from '~icons/lucide/copy';
|
||||||
|
|
||||||
|
const t = useI18n();
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
baseURL: string;
|
||||||
|
inviteSuccess: boolean;
|
||||||
|
smtpEnabled: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(event: 'copy-invite-link'): void;
|
||||||
|
(event: 'update:inviteSuccess', v: boolean): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const inviteSuccessModal = useVModel(props, 'inviteSuccess', emit);
|
||||||
|
</script>
|
||||||
@@ -203,6 +203,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Modals -->
|
||||||
<UsersInviteModal
|
<UsersInviteModal
|
||||||
v-if="showInviteUserModal"
|
v-if="showInviteUserModal"
|
||||||
:smtp-enabled="smtpEnabled"
|
:smtp-enabled="smtpEnabled"
|
||||||
@@ -210,6 +211,14 @@
|
|||||||
@send-invite="sendInvite"
|
@send-invite="sendInvite"
|
||||||
@copy-invite-link="copyInviteLink"
|
@copy-invite-link="copyInviteLink"
|
||||||
/>
|
/>
|
||||||
|
<UsersSuccessInviteModal
|
||||||
|
v-if="inviteSuccessModal"
|
||||||
|
v-model:invite-success="inviteSuccessModal"
|
||||||
|
:baseURL="baseURL"
|
||||||
|
:smtp-enabled="smtpEnabled"
|
||||||
|
@hide-modal="inviteSuccessModal = false"
|
||||||
|
@copy-invite-link="copyInviteLink"
|
||||||
|
/>
|
||||||
<HoppSmartConfirmModal
|
<HoppSmartConfirmModal
|
||||||
:show="confirmUsersToAdmin"
|
:show="confirmUsersToAdmin"
|
||||||
:title="
|
:title="
|
||||||
@@ -453,6 +462,13 @@ const goToUserDetails = (user: UserInfoQuery['infra']['userInfo']) =>
|
|||||||
// Check if SMTP is enabled
|
// Check if SMTP is enabled
|
||||||
const { data: status } = useQuery({ query: IsSmtpEnabledDocument });
|
const { data: status } = useQuery({ query: IsSmtpEnabledDocument });
|
||||||
const smtpEnabled = computed(() => status?.value?.isSMTPEnabled);
|
const smtpEnabled = computed(() => status?.value?.isSMTPEnabled);
|
||||||
|
const inviteSuccessModal = ref(false);
|
||||||
|
|
||||||
|
const baseURL = import.meta.env.VITE_BASE_URL ?? '';
|
||||||
|
const copyInviteLink = () => {
|
||||||
|
copyToClipboard(baseURL);
|
||||||
|
toast.success(t('state.link_copied_to_clipboard'));
|
||||||
|
};
|
||||||
|
|
||||||
// Send Invitation through Email
|
// Send Invitation through Email
|
||||||
const showInviteUserModal = ref(false);
|
const showInviteUserModal = ref(false);
|
||||||
@@ -482,18 +498,11 @@ const sendInvite = async (email: string) => {
|
|||||||
} else {
|
} else {
|
||||||
if (smtpEnabled.value) toast.success(t('state.email_success'));
|
if (smtpEnabled.value) toast.success(t('state.email_success'));
|
||||||
showInviteUserModal.value = false;
|
showInviteUserModal.value = false;
|
||||||
|
inviteSuccessModal.value = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const copyInviteLink = async (email: string) => {
|
|
||||||
const result = await sendInvite(email);
|
|
||||||
if (!result) return;
|
|
||||||
const baseURL = import.meta.env.VITE_BASE_URL ?? '';
|
|
||||||
copyToClipboard(baseURL);
|
|
||||||
toast.success(t('state.link_copied_to_clipboard'));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Make Multiple Users Admin
|
// Make Multiple Users Admin
|
||||||
const confirmUsersToAdmin = ref(false);
|
const confirmUsersToAdmin = ref(false);
|
||||||
const usersToAdminUID = ref<string | null>(null);
|
const usersToAdminUID = ref<string | null>(null);
|
||||||
|
|||||||
@@ -6,13 +6,14 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center py-6">
|
||||||
<h3 class="text-lg font-bold text-accentContrast pt-6 pb-4">
|
<h3 class="text-lg font-bold text-accentContrast">
|
||||||
{{ t('users.pending_invites') }}
|
{{ t('users.pending_invites') }}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<HoppButtonSecondary
|
<HoppButtonSecondary
|
||||||
:label="t('users.copy_link')"
|
v-if="pendingInvites?.length"
|
||||||
|
:label="t('users.copy_invite_link')"
|
||||||
outline
|
outline
|
||||||
filled
|
filled
|
||||||
@click="copyInviteLink"
|
@click="copyInviteLink"
|
||||||
|
|||||||
Reference in New Issue
Block a user