Browse Source

fix(sh-admin): resolve record retrieval limits in pending invites section by implementing pagination (#4624)

Joel Jacob Stephen 2 months ago
parent
commit
a3912d3ed2

+ 1 - 0
packages/hoppscotch-sh-admin/src/components.d.ts

@@ -45,6 +45,7 @@ declare module 'vue' {
     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']
+    SettingsHistoryConfiguration: typeof import('./components/settings/HistoryConfiguration.vue')['default']
     SettingsReset: typeof import('./components/settings/Reset.vue')['default']
     SettingsReset: typeof import('./components/settings/Reset.vue')['default']
     SettingsServerRestart: typeof import('./components/settings/ServerRestart.vue')['default']
     SettingsServerRestart: typeof import('./components/settings/ServerRestart.vue')['default']
     SettingsSmtpConfiguration: typeof import('./components/settings/SmtpConfiguration.vue')['default']
     SettingsSmtpConfiguration: typeof import('./components/settings/SmtpConfiguration.vue')['default']

+ 2 - 2
packages/hoppscotch-sh-admin/src/helpers/backend/gql/queries/InvitedUsers.graphql

@@ -1,6 +1,6 @@
-query InvitedUsers {
+query InvitedUsers($skip: Int, $take: Int) {
   infra {
   infra {
-    invitedUsers {
+    invitedUsers(skip: $skip, take: $take) {
       adminUid
       adminUid
       adminEmail
       adminEmail
       inviteeEmail
       inviteeEmail

+ 56 - 17
packages/hoppscotch-sh-admin/src/pages/users/invited.vue

@@ -87,6 +87,15 @@
           </template>
           </template>
         </HoppSmartTable>
         </HoppSmartTable>
 
 
+        <div
+          v-if="hasNextPage"
+          class="flex items-center w-28 px-3 py-2 mt-5 mx-auto font-semibold text-secondaryDark bg-divider hover:bg-dividerDark rounded-3xl cursor-pointer"
+          @click="fetchNextInvites"
+        >
+          <span>{{ t('users.show_more') }}</span>
+          <icon-lucide-chevron-down class="ml-2" />
+        </div>
+
         <div
         <div
           v-if="selectedRows.length"
           v-if="selectedRows.length"
           class="fixed m-2 bottom-0 left-40 right-0 w-min mx-auto shadow-2xl"
           class="fixed m-2 bottom-0 left-40 right-0 w-min mx-auto shadow-2xl"
@@ -124,13 +133,14 @@
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import { useMutation, useQuery } from '@urql/vue';
+import { useMutation } from '@urql/vue';
 import { breakpointsTailwind, useBreakpoints } from '@vueuse/core';
 import { breakpointsTailwind, useBreakpoints } from '@vueuse/core';
 import { format } from 'date-fns';
 import { format } from 'date-fns';
-import { computed, ref } from 'vue';
+import { computed, ref, watch } from 'vue';
 import { useRouter } from 'vue-router';
 import { useRouter } from 'vue-router';
 import { useI18n } from '~/composables/i18n';
 import { useI18n } from '~/composables/i18n';
 import { useToast } from '~/composables/toast';
 import { useToast } from '~/composables/toast';
+import { usePagedQuery } from '~/composables/usePagedQuery';
 import { copyToClipboard } from '~/helpers/utils/clipboard';
 import { copyToClipboard } from '~/helpers/utils/clipboard';
 import IconTrash from '~icons/lucide/trash';
 import IconTrash from '~icons/lucide/trash';
 import {
 import {
@@ -146,16 +156,13 @@ const router = useRouter();
 const breakpoints = useBreakpoints(breakpointsTailwind);
 const breakpoints = useBreakpoints(breakpointsTailwind);
 const lgAndLarger = breakpoints.greater('lg');
 const lgAndLarger = breakpoints.greater('lg');
 
 
+const invitesPerPage = 10;
+const page = ref(1);
+
 // Get Proper Date Formats
 // Get Proper Date Formats
 const getCreatedDate = (date: string) => format(new Date(date), 'dd-MMMM-yyyy');
 const getCreatedDate = (date: string) => format(new Date(date), 'dd-MMMM-yyyy');
 const getCreatedTime = (date: string) => format(new Date(date), 'hh:mm a');
 const getCreatedTime = (date: string) => format(new Date(date), 'hh:mm a');
 
 
-// Get Invited Users
-const { fetching, error, data } = useQuery({
-  query: InvitedUsersDocument,
-  variables: {},
-});
-
 // Table Headings
 // Table Headings
 const headings = [
 const headings = [
   { key: 'inviteeEmail', label: t('users.invitee_email') },
   { key: 'inviteeEmail', label: t('users.invitee_email') },
@@ -164,17 +171,49 @@ const headings = [
   { key: 'action', label: 'Action' },
   { key: 'action', label: 'Action' },
 ];
 ];
 
 
-// Selected Rows
-const selectedRows = ref<InvitedUsersQuery['infra']['invitedUsers']>([]);
+// Fetch Invites
+const {
+  fetching,
+  error,
+  refetch,
+  list: invitesList,
+} = usePagedQuery(
+  InvitedUsersDocument,
+  (x) => x.infra.invitedUsers,
+  invitesPerPage,
+  { take: invitesPerPage, skip: (page.value - 1) * invitesPerPage }
+);
 
 
-// Invited Users
-const pendingInvites = computed({
-  get: () => data.value?.infra.invitedUsers,
-  set: (value) => {
-    if (!value) return;
-    data.value!.infra.invitedUsers = value;
+const pendingInvites = ref<InvitedUsersQuery['infra']['invitedUsers']>([]);
+
+// Populate pending invites
+watch(
+  invitesList,
+  (newInvitesList) => {
+    if (!newInvitesList) return;
+
+    const newInvites = newInvitesList.filter(
+      (newInvite) =>
+        !pendingInvites.value.some(
+          (existingInvite) =>
+            existingInvite.inviteeEmail === newInvite.inviteeEmail
+        )
+    );
+    pendingInvites.value.push(...newInvites);
   },
   },
-});
+  { immediate: true, deep: true }
+);
+
+// Pagination
+const hasNextPage = computed(() => invitesList.value.length === invitesPerPage);
+
+const fetchNextInvites = () => {
+  page.value += 1;
+  refetch({ take: invitesPerPage, skip: (page.value - 1) * invitesPerPage });
+};
+
+// Selected Rows
+const selectedRows = ref<InvitedUsersQuery['infra']['invitedUsers']>([]);
 
 
 // Delete Invite
 // Delete Invite
 const confirmDeletion = ref(false);
 const confirmDeletion = ref(false);