feat: introducing i18n support to admin dashboard (#3051)
This commit is contained in:
committed by
GitHub
parent
b07243f131
commit
331d482b22
@@ -5,14 +5,18 @@
|
||||
<div class="flex items-center">
|
||||
<HoppButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
title="Open navigation"
|
||||
:title="t('app.open_navigation')"
|
||||
:icon="IconMenu"
|
||||
class="transform !md:hidden mr-2"
|
||||
@click="isOpen = true"
|
||||
/>
|
||||
<HoppButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="isExpanded ? 'Collapse sidebar' : 'Expand sidebar'"
|
||||
:title="
|
||||
isExpanded
|
||||
? `${t('app.collapse_sidebar')}`
|
||||
: `${t('app.expand_sidebar')}`
|
||||
"
|
||||
:icon="isExpanded ? IconSidebarClose : IconSidebarOpen"
|
||||
class="transform"
|
||||
@click="expandSidebar"
|
||||
@@ -34,13 +38,21 @@
|
||||
theme: 'tooltip',
|
||||
}"
|
||||
:url="currentUser.photoURL"
|
||||
:alt="currentUser.displayName ?? 'No Name'"
|
||||
:title="currentUser.displayName ?? currentUser.email ?? 'No Name'"
|
||||
:alt="currentUser.displayName ?? `${t('app.no_name')}`"
|
||||
:title="
|
||||
currentUser.displayName ??
|
||||
currentUser.email ??
|
||||
`${t('app.no_name')}`
|
||||
"
|
||||
/>
|
||||
<HoppSmartPicture
|
||||
v-else
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="currentUser.displayName ?? currentUser.email ?? 'No Name'"
|
||||
:title="
|
||||
currentUser.displayName ??
|
||||
currentUser.email ??
|
||||
`${t('app.no_name')}`
|
||||
"
|
||||
:initial="currentUser.displayName ?? currentUser.email"
|
||||
/>
|
||||
<template #content="{ hide }">
|
||||
@@ -70,6 +82,9 @@ import { auth } from '~/helpers/auth';
|
||||
import IconMenu from '~icons/lucide/menu';
|
||||
import IconSidebarOpen from '~icons/lucide/sidebar-open';
|
||||
import IconSidebarClose from '~icons/lucide/sidebar-close';
|
||||
import { useI18n } from '~/composables/i18n';
|
||||
|
||||
const t = useI18n();
|
||||
|
||||
const { isOpen, isExpanded } = useSidebar();
|
||||
|
||||
|
||||
@@ -131,6 +131,9 @@ import { setLocalConfig } from '~/helpers/localpersistence';
|
||||
import { useStreamSubscriber } from '~/composables/stream';
|
||||
import { useToast } from '~/composables/toast';
|
||||
import { auth } from '~/helpers/auth';
|
||||
import { useI18n } from '~/composables/i18n';
|
||||
|
||||
const t = useI18n();
|
||||
|
||||
const { subscribeToStream } = useStreamSubscriber();
|
||||
|
||||
@@ -158,7 +161,7 @@ onMounted(() => {
|
||||
subscribeToStream(currentUser$, (user) => {
|
||||
if (user && !user.isAdmin) {
|
||||
nonAdminUser.value = true;
|
||||
toast.error(`You are logged in. But you're not an admin`);
|
||||
toast.error(`${t('state.non_admin_login')}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -174,7 +177,7 @@ async function signInWithGoogle() {
|
||||
A auth/account-exists-with-different-credential Firebase error wont happen between Google and any other providers
|
||||
Seems Google account overwrites accounts of other providers https://github.com/firebase/firebase-android-sdk/issues/25
|
||||
*/
|
||||
toast.error(`Failed to sign in with Google`);
|
||||
toast.error(`${t('state.google_signin_failure')}`);
|
||||
}
|
||||
|
||||
signingInWithGoogle.value = false;
|
||||
@@ -190,7 +193,7 @@ async function signInWithGithub() {
|
||||
A auth/account-exists-with-different-credential Firebase error wont happen between Google and any other providers
|
||||
Seems Google account overwrites accounts of other providers https://github.com/firebase/firebase-android-sdk/issues/25
|
||||
*/
|
||||
toast.error(`Failed to sign in with GitHub`);
|
||||
toast.error(`${t('state.github_signin_failure')}`);
|
||||
}
|
||||
|
||||
signingInWithGitHub.value = false;
|
||||
@@ -211,7 +214,7 @@ async function signInWithMicrosoft() {
|
||||
@firebase/auth: Auth (9.6.11): INTERNAL ASSERTION FAILED: Pending promise was never set
|
||||
They may be related to https://github.com/firebase/firebaseui-web/issues/947
|
||||
*/
|
||||
toast.error(`Something went wrong`);
|
||||
toast.error(`${t('state.error')}`);
|
||||
}
|
||||
|
||||
signingInWithMicrosoft.value = false;
|
||||
@@ -239,10 +242,10 @@ const logout = async () => {
|
||||
try {
|
||||
await auth.signOutUser();
|
||||
window.location.reload();
|
||||
toast.success(`Logged out`);
|
||||
toast.success(`${t('state.logged_out')}`);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
toast.error(`Something went wrong`);
|
||||
toast.error(`${t('state.error')}`);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
<div class="flex" @click="openLogoutModal()">
|
||||
<HoppSmartItem
|
||||
:icon="IconLogOut"
|
||||
:label="'Logout'"
|
||||
:label="t('state.logout')"
|
||||
:outline="outline"
|
||||
:shortcut="shortcut"
|
||||
@click="openLogoutModal()"
|
||||
/>
|
||||
<HoppSmartConfirmModal
|
||||
:show="confirmLogout"
|
||||
:title="`Confirm Logout`"
|
||||
:title="t('state.confirm_logout')"
|
||||
@hide-modal="confirmLogout = false"
|
||||
@resolve="logout"
|
||||
/>
|
||||
@@ -22,6 +22,9 @@ import IconLogOut from '~icons/lucide/log-out';
|
||||
import { useToast } from '~/composables/toast';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { auth } from '~/helpers/auth';
|
||||
import { useI18n } from '~/composables/i18n';
|
||||
|
||||
const t = useI18n();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
@@ -48,10 +51,10 @@ const logout = async () => {
|
||||
try {
|
||||
await auth.signOutUser();
|
||||
router.push(`/`);
|
||||
toast.success(`Logged out`);
|
||||
toast.success(`${t('state.logged_out')}`);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
toast.error(`Something went wrong`);
|
||||
toast.error(`${t('state.error')}`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -18,8 +18,10 @@
|
||||
<HoppSmartLink class="flex items-center space-x-4" to="/dashboard">
|
||||
<img src="/cover.jpg" alt="hoppscotch-logo" class="h-7" />
|
||||
|
||||
<span v-if="isExpanded" class="font-semibold text-accentContrast"
|
||||
>HOPPSCOTCH</span
|
||||
<span
|
||||
v-if="isExpanded"
|
||||
class="font-semibold text-accentContrast"
|
||||
>{{ t('app.name') }}</span
|
||||
>
|
||||
</HoppSmartLink>
|
||||
</div>
|
||||
@@ -59,28 +61,31 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { HoppSmartLink } from '@hoppscotch/ui';
|
||||
import { useSidebar } from '../../composables/useSidebar';
|
||||
import { useSidebar } from '~/composables/useSidebar';
|
||||
import IconDashboard from '~icons/lucide/layout-dashboard';
|
||||
import IconUser from '~icons/lucide/user';
|
||||
import IconUsers from '~icons/lucide/users';
|
||||
import { useI18n } from '~/composables/i18n';
|
||||
|
||||
const t = useI18n();
|
||||
|
||||
const { isOpen, isExpanded } = useSidebar();
|
||||
|
||||
const primaryNavigations = [
|
||||
{
|
||||
label: 'Dashboard',
|
||||
label: t('metrics.dashboard'),
|
||||
icon: IconDashboard,
|
||||
to: '/dashboard',
|
||||
exact: true,
|
||||
},
|
||||
{
|
||||
label: 'Users',
|
||||
label: t('users.users'),
|
||||
icon: IconUser,
|
||||
to: '/users',
|
||||
exact: false,
|
||||
},
|
||||
{
|
||||
label: 'Teams',
|
||||
label: t('teams.teams'),
|
||||
icon: IconUsers,
|
||||
to: '/teams',
|
||||
exact: false,
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
<HoppSmartModal
|
||||
v-if="show"
|
||||
dialog
|
||||
title="Create team"
|
||||
:title="t('teams.create_team')"
|
||||
@close="$emit('hide-modal')"
|
||||
>
|
||||
<template #body>
|
||||
<div class="flex flex-col space-y-4 relative">
|
||||
<div class="flex flex-col relaive">
|
||||
<label for="teamName" class="py-2"> Team owner email </label>
|
||||
<label for="teamName" class="py-2"> {{ t('teams.email') }} </label>
|
||||
<HoppSmartAutoComplete
|
||||
styles="w-full p-2 bg-transparent border border-divider rounded-md "
|
||||
class="flex-1 !flex"
|
||||
@@ -19,7 +19,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<label for="teamName" class="py-2">Team name</label>
|
||||
<label for="teamName" class="py-2">{{ t('teams.name') }} </label>
|
||||
<input
|
||||
id="teamName"
|
||||
v-model="teamName"
|
||||
@@ -35,11 +35,16 @@
|
||||
<template #footer>
|
||||
<span class="flex space-x-2">
|
||||
<HoppButtonPrimary
|
||||
label="Create team"
|
||||
:label="t('teams.create_team')"
|
||||
:loading="loadingState"
|
||||
@click="createTeam"
|
||||
/>
|
||||
<HoppButtonSecondary label="Cancel" outline filled @click="hideModal" />
|
||||
<HoppButtonSecondary
|
||||
:label="t('teams.cancel')"
|
||||
outline
|
||||
filled
|
||||
@click="hideModal"
|
||||
/>
|
||||
</span>
|
||||
</template>
|
||||
</HoppSmartModal>
|
||||
@@ -48,6 +53,9 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useToast } from '~/composables/toast';
|
||||
import { useI18n } from '~/composables/i18n';
|
||||
|
||||
const t = useI18n();
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
@@ -75,11 +83,11 @@ const getOwnerEmail = (email: string) => (ownerEmail.value = email);
|
||||
|
||||
const createTeam = () => {
|
||||
if (teamName.value.trim() === '') {
|
||||
toast.error('Please enter a valid team name');
|
||||
toast.error(`${t('teams.valid_name')}`);
|
||||
return;
|
||||
}
|
||||
if (ownerEmail.value.trim() === '') {
|
||||
toast.error('Please enter a valid owner email');
|
||||
toast.error(`${t('teams.valid_owner_email')}`);
|
||||
return;
|
||||
}
|
||||
emit('create-team', teamName.value, ownerEmail.value);
|
||||
|
||||
@@ -2,14 +2,18 @@
|
||||
<div class="flex flex-col">
|
||||
<div class="flex flex-col space-y-8">
|
||||
<div v-if="team.id" class="flex flex-col space-y-3">
|
||||
<label class="text-accentContrast" for="username">Team ID</label>
|
||||
<label class="text-accentContrast" for="username"
|
||||
>{{ t('teams.id') }}
|
||||
</label>
|
||||
<div class="w-full p-3 bg-divider rounded-md">
|
||||
{{ team.id }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="teamName" class="flex flex-col space-y-3">
|
||||
<label class="text-accentContrast" for="teamname">Team Name </label>
|
||||
<label class="text-accentContrast" for="teamname"
|
||||
>{{ t('teams.name') }}
|
||||
</label>
|
||||
<div
|
||||
class="flex bg-divider rounded-md items-stretch flex-1 border border-divider"
|
||||
:class="{
|
||||
@@ -29,7 +33,9 @@
|
||||
class="!rounded-l-none"
|
||||
filled
|
||||
:icon="showRenameInput ? IconSave : IconEdit"
|
||||
:label="showRenameInput ? 'Rename' : 'Edit'"
|
||||
:label="
|
||||
showRenameInput ? `${t('teams.rename')}` : `${t('teams.edit')}`
|
||||
"
|
||||
@click="handleNameEdit()"
|
||||
/>
|
||||
</div>
|
||||
@@ -37,8 +43,8 @@
|
||||
|
||||
<div v-if="team.teamMembers.length" class="flex flex-col space-y-3">
|
||||
<label class="text-accentContrast" for="username"
|
||||
>Number of Members</label
|
||||
>
|
||||
>{{ t('teams.members') }}
|
||||
</label>
|
||||
<div class="w-full p-3 bg-divider rounded-md">
|
||||
{{ team.teamMembers.length }}
|
||||
</div>
|
||||
@@ -49,7 +55,7 @@
|
||||
<HoppButtonPrimary
|
||||
class="!bg-red-600 !hover:opacity-80"
|
||||
filled
|
||||
label="Delete Team"
|
||||
:label="t('teams.delete_team')"
|
||||
@click="team && $emit('delete-team', team.id)"
|
||||
:icon="IconTrash"
|
||||
/>
|
||||
@@ -58,12 +64,15 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import { useToast } from '~/composables/toast';
|
||||
import { TeamInfoQuery } from '~/helpers/backend/graphql';
|
||||
import IconEdit from '~icons/lucide/edit';
|
||||
import IconSave from '~icons/lucide/save';
|
||||
import IconTrash from '~icons/lucide/trash-2';
|
||||
import { useI18n } from '~/composables/i18n';
|
||||
|
||||
const t = useI18n();
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
@@ -91,7 +100,7 @@ const handleNameEdit = () => {
|
||||
|
||||
const renameTeam = () => {
|
||||
if (newTeamName.value.trim() === '') {
|
||||
toast.error('Team name cannot be empty');
|
||||
toast.error(`${t('teams.empty_name')}`);
|
||||
return;
|
||||
}
|
||||
emit('rename-team', newTeamName.value);
|
||||
|
||||
@@ -115,11 +115,6 @@
|
||||
v-if="newMembersList.length === 0"
|
||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
||||
>
|
||||
<img
|
||||
:src="`/images/states/dark/add_group.svg`"
|
||||
loading="lazy"
|
||||
class="inline-flex flex-col object-contain object-center w-16 h-16 mb-4"
|
||||
/>
|
||||
<span class="pb-4 text-center"> No invites </span>
|
||||
<HoppButtonSecondary label="Add new" filled @click="addNewMember" />
|
||||
</div>
|
||||
@@ -131,7 +126,9 @@
|
||||
<span
|
||||
class="flex items-center justify-center px-2 py-1 mb-4 font-semibold border rounded-full bg-primaryDark border-divider"
|
||||
>
|
||||
<icon-lucide-help-circle class="mr-2 text-secondaryLight svg-icons" />
|
||||
<icon-lucide-help-circle
|
||||
class="mr-2 text-secondaryLight svg-icons"
|
||||
/>
|
||||
Roles
|
||||
</span>
|
||||
<p>
|
||||
@@ -209,6 +206,9 @@ import IconCircleDot from '~icons/lucide/circle-dot';
|
||||
import IconCircle from '~icons/lucide/circle';
|
||||
import { computed } from 'vue';
|
||||
import { usePagedQuery } from '~/composables/usePagedQuery';
|
||||
import { useI18n } from '~/composables/i18n';
|
||||
|
||||
const t = useI18n();
|
||||
|
||||
// Get Users List
|
||||
const { data } = useQuery({ query: MetricsDocument });
|
||||
@@ -286,7 +286,7 @@ const addUserasTeamMember = async () => {
|
||||
|
||||
if (O.isNone(validationResult)) {
|
||||
// Error handling for no validation
|
||||
toast.error('Invalid User!!');
|
||||
toast.error(`${t('users.invalid_user')}`);
|
||||
addingUserToTeam.value = false;
|
||||
return;
|
||||
}
|
||||
@@ -318,12 +318,12 @@ const addUserToTeam = async (
|
||||
.then((result) => {
|
||||
if (result.error) {
|
||||
if (result.error.toString() == '[GraphQL] user/not_found') {
|
||||
toast.error('User not found in the infra!!');
|
||||
toast.error(`${t('state.user_not_found')}`);
|
||||
} else {
|
||||
toast.error('Failed to add user to the team!!');
|
||||
toast.error(`${t('state.add_user_failure')}`);
|
||||
}
|
||||
} else {
|
||||
toast.success('User is now a member of the team!!');
|
||||
toast.success(`${t('state.add_user_success')}`);
|
||||
emit('member');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class="flex">
|
||||
<HoppButtonPrimary
|
||||
:icon="IconUserPlus"
|
||||
label="Add Members"
|
||||
:label="t('teams.add_members')"
|
||||
filled
|
||||
@click="showInvite = !showInvite"
|
||||
/>
|
||||
@@ -16,11 +16,11 @@
|
||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
||||
>
|
||||
<span class="pb-4 text-center">
|
||||
No members in this team. Add members to this team to collaborate
|
||||
{{ t('teams.no_members') }}
|
||||
</span>
|
||||
<HoppButtonSecondary
|
||||
:icon="IconUserPlus"
|
||||
label="Add Members"
|
||||
:label="t('teams.add_members')"
|
||||
@click="
|
||||
() => {
|
||||
showInvite = !showInvite;
|
||||
@@ -122,7 +122,7 @@
|
||||
<HoppButtonSecondary
|
||||
id="member"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
title="Remove"
|
||||
:title="t('teams.remove')"
|
||||
:icon="IconUserMinus"
|
||||
color="red"
|
||||
:loading="isLoadingIndex === index"
|
||||
@@ -134,12 +134,16 @@
|
||||
</div>
|
||||
<div v-if="!fetching && !team" class="flex flex-col items-center">
|
||||
<icon-lucide-help-circle class="mb-4 svg-icons" />
|
||||
Something went wrong. Please try again later.
|
||||
{{ t('teams.error') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex">
|
||||
<HoppButtonPrimary label="Save" outline @click="saveUpdatedTeam" />
|
||||
<HoppButtonPrimary
|
||||
:label="t('teams.save')"
|
||||
outline
|
||||
@click="saveUpdatedTeam"
|
||||
/>
|
||||
</div>
|
||||
<TeamsInvite
|
||||
:show="showInvite"
|
||||
@@ -163,7 +167,7 @@ import IconChevronDown from '~icons/lucide/chevron-down';
|
||||
import { useClientHandle, useMutation } from '@urql/vue';
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useToast } from '../../composables/toast';
|
||||
import { useToast } from '~/composables/toast';
|
||||
import {
|
||||
ChangeUserRoleInTeamByAdminDocument,
|
||||
TeamInfoDocument,
|
||||
@@ -172,6 +176,9 @@ import {
|
||||
TeamInfoQuery,
|
||||
} from '../../helpers/backend/graphql';
|
||||
import { HoppButtonPrimary, HoppButtonSecondary } from '@hoppscotch/ui';
|
||||
import { useI18n } from '~/composables/i18n';
|
||||
|
||||
const t = useI18n();
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
@@ -195,7 +202,7 @@ const getTeamInfo = async () => {
|
||||
.toPromise();
|
||||
|
||||
if (result.error) {
|
||||
return toast.error('Unable to Load Team Info..');
|
||||
return toast.error(`${t('teams.load_info_error')}`);
|
||||
}
|
||||
if (result.data?.admin.teamInfo) {
|
||||
team.value = result.data.admin.teamInfo;
|
||||
@@ -301,10 +308,10 @@ const saveUpdatedTeam = async () => {
|
||||
update.role
|
||||
);
|
||||
if (updateMemberRoleResult.error) {
|
||||
toast.error('Role updation has failed!!');
|
||||
toast.error(`${t('state.role_update_failed')}`);
|
||||
roleUpdates.value = [];
|
||||
} else {
|
||||
toast.success('Roles updated successfully!!');
|
||||
toast.success(`${t('state.role_update_success')}`);
|
||||
roleUpdates.value = [];
|
||||
}
|
||||
isLoading.value = false;
|
||||
@@ -334,12 +341,12 @@ const removeExistingTeamMember = async (userID: string, index: number) => {
|
||||
team.value.id
|
||||
)();
|
||||
if (removeTeamMemberResult.error) {
|
||||
toast.error(`Member couldn't be removed!!`);
|
||||
toast.error(`${t('state.remove_member_failure')}`);
|
||||
} else {
|
||||
team.value.teamMembers = team.value.teamMembers?.filter(
|
||||
(member: any) => member.user.uid !== userID
|
||||
);
|
||||
toast.success('Member removed successfully!!');
|
||||
toast.success(`${t('state.remove_member_success')}`);
|
||||
}
|
||||
isLoadingIndex.value = null;
|
||||
emit('update-team');
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<div class="flex">
|
||||
<HoppButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
title="Remove"
|
||||
:title="t('teams.remove')"
|
||||
:icon="IconTrash"
|
||||
color="red"
|
||||
:loading="isLoadingIndex === index"
|
||||
@@ -41,11 +41,11 @@
|
||||
v-if="team && pendingInvites?.length === 0"
|
||||
class="flex flex-col items-center justify-center p-4 text-secondaryLight"
|
||||
>
|
||||
<span class="text-center"> No pending invites </span>
|
||||
<span class="text-center">{{ t('teams.no_pending_invites') }} </span>
|
||||
</div>
|
||||
<div v-if="!fetching && error" class="flex flex-col items-center p-4">
|
||||
<icon-lucide-help-circle class="mb-4 svg-icons" />
|
||||
Something went wrong. Please try again later.
|
||||
{{ t('teams.error') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -62,6 +62,9 @@ import {
|
||||
} from '~/helpers/backend/graphql';
|
||||
import { useToast } from '~/composables/toast';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useI18n } from '~/composables/i18n';
|
||||
|
||||
const t = useI18n();
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
@@ -83,7 +86,7 @@ const getTeamInfo = async () => {
|
||||
|
||||
if (result.error) {
|
||||
error.value = true;
|
||||
return toast.error('Unable to load team details..');
|
||||
return toast.error(`${t('teams.load_info_error')}`);
|
||||
}
|
||||
|
||||
if (result.data?.admin.teamInfo) {
|
||||
@@ -106,7 +109,7 @@ const removeInvitee = async (id: string, index: number) => {
|
||||
isLoadingIndex.value = index;
|
||||
const result = await revokeTeamInvitation(id);
|
||||
if (result.error) {
|
||||
toast.error('Removal of invitee failed!!');
|
||||
toast.error(`${t('state.remove_invitee_failure')}`);
|
||||
} else {
|
||||
if (pendingInvites.value) {
|
||||
pendingInvites.value = pendingInvites.value.filter(
|
||||
@@ -114,7 +117,7 @@ const removeInvitee = async (id: string, index: number) => {
|
||||
return invite.id !== id;
|
||||
}
|
||||
);
|
||||
toast.success('Removal of invitee is successfull!!');
|
||||
toast.success(`${t('state.remove_invitee_success')}`);
|
||||
}
|
||||
}
|
||||
isLoadingIndex.value = null;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<HoppSmartModal
|
||||
v-if="show"
|
||||
dialog
|
||||
title="Invite User"
|
||||
:title="t('users.invite_user')"
|
||||
@close="$emit('hide-modal')"
|
||||
>
|
||||
<template #body>
|
||||
@@ -17,13 +17,13 @@
|
||||
autocomplete="off"
|
||||
@keyup.enter="sendInvite"
|
||||
/>
|
||||
<label for="inviteUserEmail">Email Address</label>
|
||||
<label for="inviteUserEmail">{{ t('users.email_address') }}</label>
|
||||
</div>
|
||||
</template>
|
||||
<template #footer>
|
||||
<span class="flex space-x-2">
|
||||
<HoppButtonPrimary
|
||||
label="Send Invite"
|
||||
:label="t('users.send_invite')"
|
||||
:loading="loadingState"
|
||||
@click="sendInvite"
|
||||
/>
|
||||
@@ -36,6 +36,9 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useToast } from '~/composables/toast';
|
||||
import { useI18n } from '~/composables/i18n';
|
||||
|
||||
const t = useI18n();
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
@@ -59,7 +62,7 @@ const email = ref('');
|
||||
|
||||
const sendInvite = () => {
|
||||
if (email.value.trim() === '') {
|
||||
toast.error('Please enter a valid email address');
|
||||
toast.error(`${t('users.valid_email')}`);
|
||||
return;
|
||||
}
|
||||
emit('send-invite', email.value);
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
<table class="w-full">
|
||||
<thead>
|
||||
<tr class="text-secondary border-b border-dividerDark text-sm text-left">
|
||||
<th class="px-3 pb-3">User ID</th>
|
||||
<th class="px-3 pb-3">Name</th>
|
||||
<th class="px-3 pb-3">Email</th>
|
||||
<th class="px-3 pb-3">Date</th>
|
||||
<th class="px-3 pb-3">{{ t('users.id') }}</th>
|
||||
<th class="px-3 pb-3">{{ t('users.name') }}</th>
|
||||
<th class="px-3 pb-3">{{ t('users.email') }}</th>
|
||||
<th class="px-3 pb-3">{{ t('users.date') }}</th>
|
||||
<th class="px-3 pb-3"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -36,16 +36,16 @@
|
||||
v-if="user.isAdmin"
|
||||
class="text-xs font-medium px-3 py-0.5 rounded-full bg-green-900 text-green-300"
|
||||
>
|
||||
Admin
|
||||
{{ t('users.admin') }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-else class="flex items-center space-x-3">
|
||||
<span> (Unnamed user) </span>
|
||||
<span> {{ t('users.unnamed') }} </span>
|
||||
<span
|
||||
v-if="user.isAdmin"
|
||||
class="text-xs font-medium px-3 py-0.5 rounded-full bg-green-900 text-green-300"
|
||||
>
|
||||
Admin
|
||||
{{ t('users.admin') }}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
@@ -138,6 +138,9 @@ import IconUserCheck from '~icons/lucide/user-check';
|
||||
import IconMoreHorizontal from '~icons/lucide/more-horizontal';
|
||||
import { UsersListQuery } from '~/helpers/backend/graphql';
|
||||
import { TippyComponent } from 'vue-tippy';
|
||||
import { useI18n } from '~/composables/i18n';
|
||||
|
||||
const t = useI18n();
|
||||
|
||||
defineProps<{
|
||||
usersList: UsersListQuery['admin']['allUsers'];
|
||||
|
||||
Reference in New Issue
Block a user