refactor: consolidated admin dashboard improvements (#3790)
Co-authored-by: jamesgeorge007 <jamesgeorge998001@gmail.com>
This commit is contained in:
committed by
GitHub
parent
aab76f1358
commit
3d6adcc39d
@@ -16,15 +16,15 @@
|
||||
{{ t('configs.load_error') }}
|
||||
</div>
|
||||
|
||||
<div v-else class="flex flex-col py-8">
|
||||
<HoppSmartTabs v-model="selectedOptionTab" render-inactive-tabs>
|
||||
<HoppSmartTab :id="'config'" :label="t('configs.title')">
|
||||
<SettingsConfigurations
|
||||
v-model:config="workingConfigs"
|
||||
class="py-8 px-4"
|
||||
/>
|
||||
</HoppSmartTab>
|
||||
</HoppSmartTabs>
|
||||
<div v-else-if="workingConfigs" class="flex flex-col py-8">
|
||||
<HoppSmartTabs v-model="selectedOptionTab" render-inactive-tabs>
|
||||
<HoppSmartTab :id="'config'" :label="t('configs.title')">
|
||||
<SettingsConfigurations
|
||||
v-model:config="workingConfigs"
|
||||
class="py-8 px-4"
|
||||
/>
|
||||
</HoppSmartTab>
|
||||
</HoppSmartTabs>
|
||||
</div>
|
||||
|
||||
<div v-if="isConfigUpdated" class="fixed bottom-0 right-0 m-10">
|
||||
@@ -43,7 +43,7 @@
|
||||
:show="showSaveChangesModal"
|
||||
:title="t('configs.confirm_changes')"
|
||||
@hide-modal="showSaveChangesModal = false"
|
||||
@resolve="initiateServerRestart = true"
|
||||
@resolve="restartServer"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -51,9 +51,11 @@
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useI18n } from '~/composables/i18n';
|
||||
import { useToast } from '~/composables/toast';
|
||||
import { useConfigHandler } from '~/composables/useConfigHandler';
|
||||
|
||||
const t = useI18n();
|
||||
const toast = useToast();
|
||||
|
||||
const showSaveChangesModal = ref(false);
|
||||
const initiateServerRestart = ref(false);
|
||||
@@ -70,6 +72,7 @@ const {
|
||||
infraConfigsError,
|
||||
fetchingAllowedAuthProviders,
|
||||
allowedAuthProvidersError,
|
||||
AreAnyConfigFieldsEmpty,
|
||||
} = useConfigHandler();
|
||||
|
||||
// Check if the configs have been updated
|
||||
@@ -78,4 +81,17 @@ const isConfigUpdated = computed(() =>
|
||||
? !isEqual(currentConfigs.value, workingConfigs.value)
|
||||
: false
|
||||
);
|
||||
|
||||
// Check if any of the fields in workingConfigs are empty
|
||||
const areAnyFieldsEmpty = computed(() =>
|
||||
workingConfigs.value ? AreAnyConfigFieldsEmpty(workingConfigs.value) : false
|
||||
);
|
||||
|
||||
const restartServer = () => {
|
||||
if (areAnyFieldsEmpty.value) {
|
||||
return toast.error(t('configs.input_empty'));
|
||||
}
|
||||
initiateServerRestart.value = true;
|
||||
showSaveChangesModal.value = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
<HoppSmartSpinner />
|
||||
</div>
|
||||
|
||||
<div v-if="team" class="flex flex-col">
|
||||
<div v-else-if="error">{{ t('teams.load_info_error') }}</div>
|
||||
|
||||
<div v-else-if="team" class="flex flex-col">
|
||||
<div class="flex items-center space-x-4">
|
||||
<button
|
||||
class="p-2 rounded-3xl bg-divider hover:bg-dividerDark transition flex justify-center items-center"
|
||||
@@ -27,19 +29,16 @@
|
||||
<HoppSmartTabs v-model="selectedOptionTab" render-inactive-tabs>
|
||||
<HoppSmartTab :id="'details'" :label="t('teams.details')">
|
||||
<TeamsDetails
|
||||
:team="team"
|
||||
:teamName="teamName"
|
||||
v-model:showRenameInput="showRenameInput"
|
||||
@rename-team="renameTeamName"
|
||||
v-model:team="team"
|
||||
@delete-team="deleteTeam"
|
||||
class="py-8 px-4"
|
||||
/>
|
||||
</HoppSmartTab>
|
||||
<HoppSmartTab :id="'members'" :label="t('teams.team_members')">
|
||||
<TeamsMembers @update-team="updateTeam()" class="py-8 px-4" />
|
||||
<TeamsMembers v-model:team="team" class="py-8 px-4" />
|
||||
</HoppSmartTab>
|
||||
<HoppSmartTab :id="'invites'" :label="t('teams.invites')">
|
||||
<TeamsPendingInvites :editingTeamID="team.id" />
|
||||
<TeamsPendingInvites v-model:team="team" />
|
||||
</HoppSmartTab>
|
||||
</HoppSmartTabs>
|
||||
|
||||
@@ -55,24 +54,24 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useClientHandle, useMutation } from '@urql/vue';
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
import { useMutation } from '@urql/vue';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useI18n } from '~/composables/i18n';
|
||||
import { useToast } from '~/composables/toast';
|
||||
import { useClientHandler } from '~/composables/useClientHandler';
|
||||
import {
|
||||
RemoveTeamDocument,
|
||||
RenameTeamDocument,
|
||||
TeamInfoDocument,
|
||||
TeamMemberRole,
|
||||
TeamInfoQuery,
|
||||
} from '../../helpers/backend/graphql';
|
||||
import { HoppSmartTabs } from '@hoppscotch/ui';
|
||||
import { useI18n } from '~/composables/i18n';
|
||||
|
||||
const t = useI18n();
|
||||
|
||||
const toast = useToast();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
// Tabs
|
||||
type OptionTabs = 'details' | 'members' | 'invites';
|
||||
|
||||
const selectedOptionTab = ref<OptionTabs>('details');
|
||||
@@ -90,59 +89,24 @@ const currentTabName = computed(() => {
|
||||
}
|
||||
});
|
||||
|
||||
// Get the details of the team
|
||||
// Get Team Info
|
||||
const {
|
||||
fetching,
|
||||
error,
|
||||
data: teamInfo,
|
||||
fetchData: getTeamInfo,
|
||||
} = useClientHandler(TeamInfoDocument, {
|
||||
teamID: route.params.id.toString(),
|
||||
});
|
||||
|
||||
const team = ref<TeamInfoQuery['infra']['teamInfo'] | undefined>();
|
||||
const teamName = ref('');
|
||||
const route = useRoute();
|
||||
const fetching = ref(true);
|
||||
const { client } = useClientHandle();
|
||||
|
||||
const getTeamInfo = async () => {
|
||||
fetching.value = true;
|
||||
const result = await client
|
||||
.query(TeamInfoDocument, { teamID: route.params.id.toString() })
|
||||
.toPromise();
|
||||
if (result.error) {
|
||||
return toast.error(`${t('team.load_info_error')}`);
|
||||
}
|
||||
if (result.data?.infra.teamInfo) {
|
||||
team.value = result.data.infra.teamInfo;
|
||||
teamName.value = team.value.name;
|
||||
}
|
||||
fetching.value = false;
|
||||
};
|
||||
|
||||
onMounted(async () => await getTeamInfo());
|
||||
|
||||
const updateTeam = async () => await getTeamInfo();
|
||||
|
||||
// Rename the team name
|
||||
const showRenameInput = ref(false);
|
||||
const teamRename = useMutation(RenameTeamDocument);
|
||||
|
||||
const renameTeamName = async (teamName: string) => {
|
||||
if (!team.value) return;
|
||||
|
||||
if (team.value.name === teamName) {
|
||||
showRenameInput.value = false;
|
||||
return;
|
||||
}
|
||||
const variables = { uid: team.value.id, name: teamName };
|
||||
await teamRename.executeMutation(variables).then((result) => {
|
||||
if (result.error) {
|
||||
toast.error(`${t('state.rename_team_failure')}`);
|
||||
} else {
|
||||
showRenameInput.value = false;
|
||||
if (team.value) {
|
||||
team.value.name = teamName;
|
||||
toast.success(`${t('state.rename_team_success')}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
onMounted(async () => {
|
||||
await getTeamInfo();
|
||||
team.value = teamInfo.value?.infra.teamInfo;
|
||||
});
|
||||
|
||||
// Delete team from the infra
|
||||
const router = useRouter();
|
||||
const confirmDeletion = ref(false);
|
||||
const teamDeletion = useMutation(RemoveTeamDocument);
|
||||
const deleteTeamUID = ref<string | null>(null);
|
||||
@@ -155,42 +119,18 @@ const deleteTeam = (id: string) => {
|
||||
const deleteTeamMutation = async (id: string | null) => {
|
||||
if (!id) {
|
||||
confirmDeletion.value = false;
|
||||
toast.error(`${t('state.delete_team_failure')}`);
|
||||
toast.error(t('state.delete_team_failure'));
|
||||
return;
|
||||
}
|
||||
const variables = { uid: id };
|
||||
await teamDeletion.executeMutation(variables).then((result) => {
|
||||
if (result.error) {
|
||||
toast.error(`${t('state.delete_team_failure')}`);
|
||||
} else {
|
||||
toast.success(`${t('state.delete_team_success')}`);
|
||||
}
|
||||
});
|
||||
const result = await teamDeletion.executeMutation(variables);
|
||||
if (result.error) {
|
||||
toast.error(t('state.delete_team_failure'));
|
||||
} else {
|
||||
toast.success(t('state.delete_team_success'));
|
||||
}
|
||||
confirmDeletion.value = false;
|
||||
deleteTeamUID.value = null;
|
||||
router.push('/teams');
|
||||
};
|
||||
|
||||
// Update Roles of Members
|
||||
const roleUpdates = ref<
|
||||
{
|
||||
userID: string;
|
||||
role: TeamMemberRole;
|
||||
}[]
|
||||
>([]);
|
||||
|
||||
watch(
|
||||
() => team.value,
|
||||
(teamDetails) => {
|
||||
const members = teamDetails?.teamMembers ?? [];
|
||||
|
||||
// Remove deleted members
|
||||
roleUpdates.value = roleUpdates.value.filter(
|
||||
(update) =>
|
||||
members.findIndex(
|
||||
(y: { user: { uid: string } }) => y.user.uid === update.userID
|
||||
) !== -1
|
||||
);
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
@@ -122,7 +122,15 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useMutation, useQuery } from '@urql/vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useI18n } from '~/composables/i18n';
|
||||
import { useToast } from '~/composables/toast';
|
||||
import { usePagedQuery } from '~/composables/usePagedQuery';
|
||||
import IconMoreHorizontal from '~icons/lucide/more-horizontal';
|
||||
import IconAddUsers from '~icons/lucide/plus';
|
||||
import IconTrash from '~icons/lucide/trash';
|
||||
import {
|
||||
CreateTeamDocument,
|
||||
MetricsDocument,
|
||||
@@ -130,18 +138,10 @@ import {
|
||||
TeamListDocument,
|
||||
UsersListDocument,
|
||||
} from '../../helpers/backend/graphql';
|
||||
import { usePagedQuery } from '~/composables/usePagedQuery';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { useMutation, useQuery } from '@urql/vue';
|
||||
import { useToast } from '~/composables/toast';
|
||||
import IconAddUsers from '~icons/lucide/plus';
|
||||
import IconTrash from '~icons/lucide/trash';
|
||||
import IconMoreHorizontal from '~icons/lucide/more-horizontal';
|
||||
import { useI18n } from '~/composables/i18n';
|
||||
|
||||
const t = useI18n();
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
// Get Users List
|
||||
const { data } = useQuery({ query: MetricsDocument });
|
||||
const usersPerPage = computed(() => data.value?.infra.usersCount || 10000);
|
||||
@@ -174,55 +174,50 @@ const {
|
||||
);
|
||||
|
||||
// Create Team
|
||||
const createTeamMutation = useMutation(CreateTeamDocument);
|
||||
const showCreateTeamModal = ref(false);
|
||||
const createTeamLoading = ref(false);
|
||||
const createTeamMutation = useMutation(CreateTeamDocument);
|
||||
|
||||
const createTeam = async (newTeamName: string, ownerEmail: string) => {
|
||||
if (newTeamName.length < 6) {
|
||||
toast.error(`${t('state.team_name_long')}`);
|
||||
toast.error(t('state.team_name_too_short'));
|
||||
return;
|
||||
}
|
||||
if (ownerEmail.length === 0) {
|
||||
toast.error(`${t('state.enter_team_email')}`);
|
||||
toast.error(t('state.enter_team_email'));
|
||||
return;
|
||||
}
|
||||
|
||||
createTeamLoading.value = true;
|
||||
const userUid =
|
||||
usersList.value.find((user) => user.email === ownerEmail)?.uid || '';
|
||||
|
||||
const variables = { name: newTeamName.trim(), userUid: userUid };
|
||||
await createTeamMutation.executeMutation(variables).then((result) => {
|
||||
if (result.error) {
|
||||
if (result.error.toString() == '[GraphQL] user/not_found') {
|
||||
toast.error(`${t('state.user_not_found')}`);
|
||||
} else {
|
||||
toast.error(`${t('state.create_team_failure')}`);
|
||||
}
|
||||
createTeamLoading.value = false;
|
||||
|
||||
const result = await createTeamMutation.executeMutation(variables);
|
||||
if (result.error) {
|
||||
if (result.error.toString() == '[GraphQL] user/not_found') {
|
||||
toast.error(t('state.user_not_found'));
|
||||
} else {
|
||||
toast.success(`${t('state.create_team_success')}`);
|
||||
showCreateTeamModal.value = false;
|
||||
createTeamLoading.value = false;
|
||||
refetch();
|
||||
toast.error(t('state.create_team_failure'));
|
||||
}
|
||||
});
|
||||
createTeamLoading.value = false;
|
||||
} else {
|
||||
toast.success(t('state.create_team_success'));
|
||||
showCreateTeamModal.value = false;
|
||||
createTeamLoading.value = false;
|
||||
refetch();
|
||||
}
|
||||
};
|
||||
|
||||
// Go To Individual Team Details Page
|
||||
const router = useRouter();
|
||||
const goToTeamDetails = (teamId: string) => router.push('/teams/' + teamId);
|
||||
|
||||
// Reload Teams Page when routed back to the teams page
|
||||
const route = useRoute();
|
||||
watch(
|
||||
() => route.params.id,
|
||||
() => window.location.reload()
|
||||
);
|
||||
|
||||
// Team Deletion
|
||||
const teamDeletion = useMutation(RemoveTeamDocument);
|
||||
const confirmDeletion = ref(false);
|
||||
const deleteTeamID = ref<string | null>(null);
|
||||
const teamDeletion = useMutation(RemoveTeamDocument);
|
||||
|
||||
const deleteTeam = (id: string) => {
|
||||
confirmDeletion.value = true;
|
||||
@@ -232,20 +227,19 @@ const deleteTeam = (id: string) => {
|
||||
const deleteTeamMutation = async (id: string | null) => {
|
||||
if (!id) {
|
||||
confirmDeletion.value = false;
|
||||
toast.error(`${t('state.delete_team_failure')}`);
|
||||
toast.error(t('state.delete_team_failure'));
|
||||
return;
|
||||
}
|
||||
const variables = { uid: id };
|
||||
await teamDeletion.executeMutation(variables).then((result) => {
|
||||
if (result.error) {
|
||||
toast.error(`${t('state.delete_team_failure')}`);
|
||||
} else {
|
||||
teamsList.value = teamsList.value.filter((team) => team.id !== id);
|
||||
toast.success(`${t('state.delete_team_success')}`);
|
||||
}
|
||||
});
|
||||
const result = await teamDeletion.executeMutation(variables);
|
||||
if (result.error) {
|
||||
toast.error(t('state.delete_team_failure'));
|
||||
} else {
|
||||
teamsList.value = teamsList.value.filter((team) => team.id !== id);
|
||||
toast.success(t('state.delete_team_success'));
|
||||
}
|
||||
|
||||
confirmDeletion.value = false;
|
||||
deleteTeamID.value = null;
|
||||
router.push('/teams');
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<div v-if="fetching" class="flex justify-center"><HoppSmartSpinner /></div>
|
||||
<div v-else class="flex flex-col space-y-4">
|
||||
<div v-else-if="error">{{ t('users.load_info_error') }}</div>
|
||||
<div v-else-if="user" class="flex flex-col space-y-4">
|
||||
<div class="flex gap-x-3">
|
||||
<button
|
||||
class="p-2 mb-2 rounded-3xl bg-divider"
|
||||
@@ -32,7 +33,7 @@
|
||||
/>
|
||||
</HoppSmartTab>
|
||||
<HoppSmartTab :id="'requests'" :label="t('shared_requests.title')">
|
||||
<UsersSharedRequests :email="user.email" class="py-8 px-4 mt-10" />
|
||||
<UsersSharedRequests :email="user.email" />
|
||||
</HoppSmartTab>
|
||||
</HoppSmartTabs>
|
||||
</div>
|
||||
@@ -59,21 +60,20 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, computed } from 'vue';
|
||||
import { useMutation } from '@urql/vue';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useI18n } from '~/composables/i18n';
|
||||
import { useToast } from '~/composables/toast';
|
||||
import { useClientHandler } from '~/composables/useClientHandler';
|
||||
import {
|
||||
MakeUserAdminDocument,
|
||||
UserInfoDocument,
|
||||
RemoveUserByAdminDocument,
|
||||
RemoveUserAsAdminDocument,
|
||||
RemoveUserByAdminDocument,
|
||||
UserInfoDocument,
|
||||
} from '~/helpers/backend/graphql';
|
||||
import { useClientHandle } from '@urql/vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useToast } from '~/composables/toast';
|
||||
import { useI18n } from '~/composables/i18n';
|
||||
|
||||
const t = useI18n();
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
// Tabs
|
||||
@@ -91,23 +91,26 @@ const currentTabName = computed(() => {
|
||||
}
|
||||
});
|
||||
|
||||
// Get User Info
|
||||
const user = ref();
|
||||
const { client } = useClientHandle();
|
||||
const fetching = ref(true);
|
||||
const route = useRoute();
|
||||
|
||||
onMounted(async () => {
|
||||
fetching.value = true;
|
||||
const result = await client
|
||||
.query(UserInfoDocument, { uid: route.params.id.toString() })
|
||||
.toPromise();
|
||||
|
||||
if (result.error) {
|
||||
toast.error(`${t('users.load_info_error')}`);
|
||||
const { fetching, error, data, fetchData } = useClientHandler(
|
||||
UserInfoDocument,
|
||||
{
|
||||
uid: route.params.id.toString(),
|
||||
}
|
||||
user.value = result.data?.infra.userInfo ?? {};
|
||||
fetching.value = false;
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
await fetchData();
|
||||
});
|
||||
|
||||
const user = computed({
|
||||
get: () => data.value?.infra.userInfo,
|
||||
set: (value) => {
|
||||
if (value) {
|
||||
data.value!.infra.userInfo = value;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// User Deletion
|
||||
@@ -124,17 +127,18 @@ const deleteUser = (id: string) => {
|
||||
const deleteUserMutation = async (id: string | null) => {
|
||||
if (!id) {
|
||||
confirmDeletion.value = false;
|
||||
toast.error(`${t('state.delete_user_failure')}`);
|
||||
toast.error(t('state.delete_user_failure'));
|
||||
return;
|
||||
}
|
||||
const variables = { uid: id };
|
||||
await userDeletion.executeMutation(variables).then((result) => {
|
||||
if (result.error) {
|
||||
toast.error(`${t('state.delete_user_failure')}`);
|
||||
} else {
|
||||
toast.success(`${t('state.delete_user_success')}`);
|
||||
}
|
||||
});
|
||||
const result = await userDeletion.executeMutation(variables);
|
||||
|
||||
if (result.error) {
|
||||
toast.error(t('state.delete_user_failure'));
|
||||
} else {
|
||||
toast.success(t('state.delete_user_success'));
|
||||
}
|
||||
|
||||
confirmDeletion.value = false;
|
||||
deleteUserUID.value = null;
|
||||
router.push('/users');
|
||||
@@ -153,18 +157,17 @@ const makeUserAdmin = (id: string) => {
|
||||
const makeUserAdminMutation = async (id: string | null) => {
|
||||
if (!id) {
|
||||
confirmUserToAdmin.value = false;
|
||||
toast.error(`${t('state.admin_failure')}`);
|
||||
toast.error(t('state.admin_failure'));
|
||||
return;
|
||||
}
|
||||
const variables = { uid: id };
|
||||
await userToAdmin.executeMutation(variables).then((result) => {
|
||||
if (result.error) {
|
||||
toast.error(`${t('state.admin_failure')}`);
|
||||
} else {
|
||||
user.value.isAdmin = true;
|
||||
toast.success(`${t('state.admin_success')}`);
|
||||
}
|
||||
});
|
||||
const result = await userToAdmin.executeMutation(variables);
|
||||
if (result.error) {
|
||||
toast.error(t('state.admin_failure'));
|
||||
} else {
|
||||
user.value!.isAdmin = true;
|
||||
toast.success(t('state.admin_success'));
|
||||
}
|
||||
confirmUserToAdmin.value = false;
|
||||
userToAdminUID.value = null;
|
||||
};
|
||||
@@ -182,18 +185,17 @@ const makeAdminToUser = (id: string) => {
|
||||
const makeAdminToUserMutation = async (id: string | null) => {
|
||||
if (!id) {
|
||||
confirmAdminToUser.value = false;
|
||||
toast.error(`${t('state.remove_admin_failure')}`);
|
||||
toast.error(t('state.remove_admin_failure'));
|
||||
return;
|
||||
}
|
||||
const variables = { uid: id };
|
||||
await adminToUser.executeMutation(variables).then((result) => {
|
||||
if (result.error) {
|
||||
toast.error(`${t('state.remove_admin_failure')}`);
|
||||
} else {
|
||||
user.value.isAdmin = false;
|
||||
toast.error(`${t('state.remove_admin_success')}`);
|
||||
}
|
||||
});
|
||||
const result = await adminToUser.executeMutation(variables);
|
||||
if (result.error) {
|
||||
toast.error(t('state.remove_admin_failure'));
|
||||
} else {
|
||||
user.value!.isAdmin = false;
|
||||
toast.error(t('state.remove_admin_success'));
|
||||
}
|
||||
confirmAdminToUser.value = false;
|
||||
adminToUserUID.value = null;
|
||||
};
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
@click="showInviteUserModal = true"
|
||||
:icon="IconAddUser"
|
||||
/>
|
||||
|
||||
<div class="flex">
|
||||
<HoppButtonSecondary
|
||||
outline
|
||||
@@ -173,35 +172,33 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { format } from 'date-fns';
|
||||
import { ref, watch } from 'vue';
|
||||
import { useMutation } from '@urql/vue';
|
||||
import { format } from 'date-fns';
|
||||
import { ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useI18n } from '~/composables/i18n';
|
||||
import { useToast } from '~/composables/toast';
|
||||
import { usePagedQuery } from '~/composables/usePagedQuery';
|
||||
import IconMoreHorizontal from '~icons/lucide/more-horizontal';
|
||||
import IconTrash from '~icons/lucide/trash';
|
||||
import IconUserCheck from '~icons/lucide/user-check';
|
||||
import IconUserMinus from '~icons/lucide/user-minus';
|
||||
import IconAddUser from '~icons/lucide/user-plus';
|
||||
import {
|
||||
InviteNewUserDocument,
|
||||
MakeUserAdminDocument,
|
||||
RemoveUserByAdminDocument,
|
||||
RemoveUserAsAdminDocument,
|
||||
RemoveUserByAdminDocument,
|
||||
UsersListDocument,
|
||||
} from '../../helpers/backend/graphql';
|
||||
import { usePagedQuery } from '~/composables/usePagedQuery';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useToast } from '~/composables/toast';
|
||||
import { HoppButtonSecondary } from '@hoppscotch/ui';
|
||||
import IconAddUser from '~icons/lucide/user-plus';
|
||||
import IconTrash from '~icons/lucide/trash';
|
||||
import IconUserMinus from '~icons/lucide/user-minus';
|
||||
import IconUserCheck from '~icons/lucide/user-check';
|
||||
import IconMoreHorizontal from '~icons/lucide/more-horizontal';
|
||||
import { useI18n } from '~/composables/i18n';
|
||||
} from '~/helpers/backend/graphql';
|
||||
|
||||
// Get Proper Date Formats
|
||||
const t = useI18n();
|
||||
const toast = useToast();
|
||||
|
||||
const getCreatedDate = (date: string) => format(new Date(date), 'dd-MM-yyyy');
|
||||
const getCreatedTime = (date: string) => format(new Date(date), 'hh:mm a');
|
||||
|
||||
const t = useI18n();
|
||||
|
||||
const toast = useToast();
|
||||
|
||||
// Get Paginated Results of all the users in the infra
|
||||
const usersPerPage = 20;
|
||||
const {
|
||||
@@ -224,30 +221,23 @@ const showInviteUserModal = ref(false);
|
||||
|
||||
const sendInvite = async (email: string) => {
|
||||
if (!email.trim()) {
|
||||
toast.error(`${t('state.invalid_email')}`);
|
||||
toast.error(t('state.invalid_email'));
|
||||
return;
|
||||
}
|
||||
const variables = { inviteeEmail: email.trim() };
|
||||
await sendInvitation.executeMutation(variables).then((result) => {
|
||||
if (result.error) {
|
||||
toast.error(`${t('state.email_failure')}`);
|
||||
} else {
|
||||
toast.success(`${t('state.email_success')}`);
|
||||
showInviteUserModal.value = false;
|
||||
}
|
||||
});
|
||||
const result = await sendInvitation.executeMutation(variables);
|
||||
if (result.error) {
|
||||
toast.error(t('state.email_failure'));
|
||||
} else {
|
||||
toast.success(t('state.email_success'));
|
||||
showInviteUserModal.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Go to Individual User Details Page
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const goToUserDetails = (uid: string) => router.push('/users/' + uid);
|
||||
|
||||
watch(
|
||||
() => route.params.id,
|
||||
() => window.location.reload()
|
||||
);
|
||||
|
||||
// User Deletion
|
||||
const userDeletion = useMutation(RemoveUserByAdminDocument);
|
||||
const confirmDeletion = ref(false);
|
||||
@@ -256,18 +246,17 @@ const deleteUserUID = ref<string | null>(null);
|
||||
const deleteUserMutation = async (id: string | null) => {
|
||||
if (!id) {
|
||||
confirmDeletion.value = false;
|
||||
toast.error(`${t('state.delete_user_failure')}`);
|
||||
toast.error(t('state.delete_user_failure'));
|
||||
return;
|
||||
}
|
||||
const variables = { uid: id };
|
||||
await userDeletion.executeMutation(variables).then((result) => {
|
||||
if (result.error) {
|
||||
toast.error(`${t('state.delete_user_failure')}`);
|
||||
} else {
|
||||
toast.success(`${t('state.delete_user_success')}`);
|
||||
usersList.value = usersList.value.filter((user) => user.uid !== id);
|
||||
}
|
||||
});
|
||||
const result = await userDeletion.executeMutation(variables);
|
||||
if (result.error) {
|
||||
toast.error(t('state.delete_user_failure'));
|
||||
} else {
|
||||
toast.success(t('state.delete_user_success'));
|
||||
usersList.value = usersList.value.filter((user) => user.uid !== id);
|
||||
}
|
||||
confirmDeletion.value = false;
|
||||
deleteUserUID.value = null;
|
||||
};
|
||||
@@ -285,23 +274,20 @@ const makeUserAdmin = (id: string) => {
|
||||
const makeUserAdminMutation = async (id: string | null) => {
|
||||
if (!id) {
|
||||
confirmUserToAdmin.value = false;
|
||||
toast.error(`${t('state.admin_failure')}`);
|
||||
toast.error(t('state.admin_failure'));
|
||||
return;
|
||||
}
|
||||
const variables = { uid: id };
|
||||
await userToAdmin.executeMutation(variables).then((result) => {
|
||||
if (result.error) {
|
||||
toast.error(`${t('state.admin_failure')}`);
|
||||
} else {
|
||||
toast.success(`${t('state.admin_success')}`);
|
||||
usersList.value = usersList.value.map((user) => {
|
||||
if (user.uid === id) {
|
||||
user.isAdmin = true;
|
||||
}
|
||||
return user;
|
||||
});
|
||||
}
|
||||
});
|
||||
const result = await userToAdmin.executeMutation(variables);
|
||||
if (result.error) {
|
||||
toast.error(t('state.admin_failure'));
|
||||
} else {
|
||||
toast.success(t('state.admin_success'));
|
||||
usersList.value = usersList.value.map((user) => ({
|
||||
...user,
|
||||
isAdmin: user.uid === id ? true : user.isAdmin,
|
||||
}));
|
||||
}
|
||||
confirmUserToAdmin.value = false;
|
||||
userToAdminUID.value = null;
|
||||
};
|
||||
@@ -324,23 +310,20 @@ const deleteUser = (id: string) => {
|
||||
const makeAdminToUserMutation = async (id: string | null) => {
|
||||
if (!id) {
|
||||
confirmAdminToUser.value = false;
|
||||
toast.error(`${t('state.remove_admin_failure')}`);
|
||||
toast.error(t('state.remove_admin_failure'));
|
||||
return;
|
||||
}
|
||||
const variables = { uid: id };
|
||||
await adminToUser.executeMutation(variables).then((result) => {
|
||||
if (result.error) {
|
||||
toast.error(`${t('state.remove_admin_failure')}`);
|
||||
} else {
|
||||
toast.success(`${t('state.remove_admin_success')}`);
|
||||
usersList.value = usersList.value.map((user) => {
|
||||
if (user.uid === id) {
|
||||
user.isAdmin = false;
|
||||
}
|
||||
return user;
|
||||
});
|
||||
}
|
||||
});
|
||||
const result = await adminToUser.executeMutation(variables);
|
||||
if (result.error) {
|
||||
toast.error(t('state.remove_admin_failure'));
|
||||
} else {
|
||||
toast.success(t('state.remove_admin_success'));
|
||||
usersList.value = usersList.value.map((user) => ({
|
||||
...user,
|
||||
isAdmin: user.uid === id ? false : user.isAdmin,
|
||||
}));
|
||||
}
|
||||
confirmAdminToUser.value = false;
|
||||
adminToUserUID.value = null;
|
||||
};
|
||||
|
||||
@@ -46,16 +46,14 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { useQuery } from '@urql/vue';
|
||||
import { InvitedUsersDocument } from '../../helpers/backend/graphql';
|
||||
import { format } from 'date-fns';
|
||||
import { HoppSmartSpinner } from '@hoppscotch/ui';
|
||||
import { computed } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useI18n } from '~/composables/i18n';
|
||||
import { InvitedUsersDocument } from '~/helpers/backend/graphql';
|
||||
|
||||
const t = useI18n();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
// Get Proper Date Formats
|
||||
|
||||
Reference in New Issue
Block a user