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:
Joel Jacob Stephen
2024-07-26 17:44:41 +03:00
committed by GitHub
parent 9cde6c597b
commit 9e445cda84
6 changed files with 96 additions and 19 deletions

View File

@@ -166,6 +166,8 @@
"link_copied_to_clipboard": "Link copied to clipboard",
"logged_out": "Logged out",
"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",
"magic_link_sign_in": "Click on the link to sign in.",
"magic_link_success": "We sent a magic link to",
@@ -269,10 +271,12 @@
"valid_owner_email": "Please enter a valid owner email"
},
"users": {
"add_user": "Add User",
"admin": "Admin",
"admin_id": "Admin ID",
"cancel": "Cancel",
"created_on": "Created On",
"copy_invite_link": "Copy Invite Link",
"copy_link": "Copy Link",
"date": "Date",
"delete": "Delete",
@@ -296,6 +300,7 @@
"load_list_error": "Unable to Load Users List",
"make_admin": "Make Admin",
"name": "Name",
"new_user_added": "New User Added",
"no_invite": "No pending invites found",
"no_shared_requests": "No shared requests created by the user",
"no_users": "No users found",

View File

@@ -17,21 +17,29 @@ declare module '@vue/runtime-core' {
HoppButtonPrimary: typeof import('@hoppscotch/ui')['HoppButtonPrimary']
HoppButtonSecondary: typeof import('@hoppscotch/ui')['HoppButtonSecondary']
HoppSmartAnchor: typeof import('@hoppscotch/ui')['HoppSmartAnchor']
HoppSmartAutoComplete: typeof import('@hoppscotch/ui')['HoppSmartAutoComplete']
HoppSmartCheckbox: typeof import('@hoppscotch/ui')['HoppSmartCheckbox']
HoppSmartConfirmModal: typeof import('@hoppscotch/ui')['HoppSmartConfirmModal']
HoppSmartInput: typeof import('@hoppscotch/ui')['HoppSmartInput']
HoppSmartIntersection: typeof import('@hoppscotch/ui')['HoppSmartIntersection']
HoppSmartItem: typeof import('@hoppscotch/ui')['HoppSmartItem']
HoppSmartLink: typeof import('@hoppscotch/ui')['HoppSmartLink']
HoppSmartModal: typeof import('@hoppscotch/ui')['HoppSmartModal']
HoppSmartPicture: typeof import('@hoppscotch/ui')['HoppSmartPicture']
HoppSmartPlaceholder: typeof import('@hoppscotch/ui')['HoppSmartPlaceholder']
HoppSmartSelectWrapper: typeof import('@hoppscotch/ui')['HoppSmartSelectWrapper']
HoppSmartSpinner: typeof import('@hoppscotch/ui')['HoppSmartSpinner']
HoppSmartTab: typeof import('@hoppscotch/ui')['HoppSmartTab']
HoppSmartTable: typeof import('@hoppscotch/ui')['HoppSmartTable']
HoppSmartTabs: typeof import('@hoppscotch/ui')['HoppSmartTabs']
HoppSmartToggle: typeof import('@hoppscotch/ui')['HoppSmartToggle']
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']
IconLucideSearch: typeof import('~icons/lucide/search')['default']
IconLucideUser: typeof import('~icons/lucide/user')['default']
SettingsAuthProvider: typeof import('./components/settings/AuthProvider.vue')['default']
SettingsConfigurations: typeof import('./components/settings/Configurations.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']
UsersInviteModal: typeof import('./components/users/InviteModal.vue')['default']
UsersSharedRequests: typeof import('./components/users/SharedRequests.vue')['default']
UsersSuccessInviteModal: typeof import('./components/users/SuccessInviteModal.vue')['default']
}
}

View File

@@ -15,8 +15,7 @@
<template #footer>
<span class="flex space-x-2">
<HoppButtonPrimary
:disabled="!smtpEnabled"
:label="t('users.send_invite')"
:label="t('users.add_user')"
@click="emit('send-invite', email)"
/>
<HoppButtonSecondary
@@ -26,12 +25,6 @@
@click="hideModal"
/>
</span>
<HoppButtonSecondary
:label="t('users.copy_link')"
outline
filled
@click="emit('copy-invite-link', email)"
/>
</template>
</HoppSmartModal>
</template>

View File

@@ -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>

View File

@@ -203,6 +203,7 @@
</div>
</div>
<!-- Modals -->
<UsersInviteModal
v-if="showInviteUserModal"
:smtp-enabled="smtpEnabled"
@@ -210,6 +211,14 @@
@send-invite="sendInvite"
@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
:show="confirmUsersToAdmin"
:title="
@@ -453,6 +462,13 @@ const goToUserDetails = (user: UserInfoQuery['infra']['userInfo']) =>
// Check if SMTP is enabled
const { data: status } = useQuery({ query: IsSmtpEnabledDocument });
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
const showInviteUserModal = ref(false);
@@ -482,18 +498,11 @@ const sendInvite = async (email: string) => {
} else {
if (smtpEnabled.value) toast.success(t('state.email_success'));
showInviteUserModal.value = false;
inviteSuccessModal.value = 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
const confirmUsersToAdmin = ref(false);
const usersToAdminUID = ref<string | null>(null);

View File

@@ -6,13 +6,14 @@
</button>
</div>
<div class="flex justify-between items-center">
<h3 class="text-lg font-bold text-accentContrast pt-6 pb-4">
<div class="flex justify-between items-center py-6">
<h3 class="text-lg font-bold text-accentContrast">
{{ t('users.pending_invites') }}
</h3>
<HoppButtonSecondary
:label="t('users.copy_link')"
v-if="pendingInvites?.length"
:label="t('users.copy_invite_link')"
outline
filled
@click="copyInviteLink"