add loading state support to DataTable component across various views

- UI/UX improvements for datatable
This commit is contained in:
Abhinav Raut
2026-01-11 18:13:11 +05:30
parent 74fd95c56d
commit 7bc3387d85
16 changed files with 76 additions and 33 deletions
+59 -16
View File
@@ -1,41 +1,80 @@
<template>
<div class="w-full">
<div class="rounded border shadow">
<div class="overflow-hidden rounded-lg border border-border bg-card shadow-sm">
<Table>
<TableHeader>
<TableRow v-for="headerGroup in table.getHeaderGroups()" :key="headerGroup.id">
<TableRow
v-for="headerGroup in table.getHeaderGroups()"
:key="headerGroup.id"
class="border-b border-border bg-muted/40 hover:bg-muted/40"
>
<TableHead
v-for="header in headerGroup.headers"
:key="header.id"
class="font-semibold"
:class="{ 'cursor-pointer select-none': header.column.getCanSort() }"
class="h-11 px-4 text-center text-sm font-medium text-muted-foreground"
:class="{
'cursor-pointer select-none transition-colors hover:text-foreground':
header.column.getCanSort()
}"
@click="header.column.getToggleSortingHandler()?.($event)"
>
<FlexRender
v-if="!header.isPlaceholder"
:render="header.column.columnDef.header"
:props="header.getContext()"
/>
<div class="flex items-center justify-center gap-2">
<FlexRender
v-if="!header.isPlaceholder"
:render="header.column.columnDef.header"
:props="header.getContext()"
/>
<template v-if="header.column.getCanSort()">
<ChevronUp v-if="header.column.getIsSorted() === 'asc'" class="h-3.5 w-3.5" />
<ChevronDown
v-else-if="header.column.getIsSorted() === 'desc'"
class="h-3.5 w-3.5"
/>
<ArrowUpDown
v-else
class="h-3.5 w-3.5 opacity-0 transition-opacity group-hover:opacity-50"
/>
</template>
</div>
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<template v-if="table.getRowModel().rows?.length">
<TableRow
v-for="row in table.getRowModel().rows"
:key="row.id"
:data-state="row.getIsSelected() ? 'selected' : undefined"
class="hover:bg-muted/50"
class="border-b border-border/50 transition-colors last:border-0 hover:bg-muted/30 data-[state=selected]:bg-muted"
>
<TableCell v-for="cell in row.getVisibleCells()" :key="cell.id">
<TableCell
v-for="cell in row.getVisibleCells()"
:key="cell.id"
class="px-4 py-3 text-center text-sm"
>
<FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
</TableCell>
</TableRow>
</template>
<template v-else-if="loading">
<TableRow class="hover:bg-transparent">
<TableCell :colspan="columns.length" class="h-32">
<div class="flex items-center justify-center">
<p class="text-sm text-muted-foreground">{{ t('globals.terms.loading') }}</p>
</div>
</TableCell>
</TableRow>
</template>
<template v-else>
<TableRow>
<TableCell :colspan="columns.length" class="h-24 text-center">
<div class="text-muted-foreground">{{ emptyText }}</div>
<TableRow class="hover:bg-transparent">
<TableCell :colspan="columns.length" class="h-32">
<div class="flex flex-col items-center justify-center gap-2 text-center">
<Ghost class="h-8 w-8 text-muted-foreground/50" />
<p class="text-sm font-medium text-muted-foreground">{{ emptyText }}</p>
</div>
</TableCell>
</TableRow>
</template>
@@ -49,7 +88,7 @@
import { FlexRender, getCoreRowModel, getSortedRowModel, useVueTable } from '@tanstack/vue-table'
import { useI18n } from 'vue-i18n'
import { computed, ref } from 'vue'
import { ArrowUpDown, ChevronDown, ChevronUp, Ghost } from 'lucide-vue-next'
import {
Table,
TableBody,
@@ -60,16 +99,20 @@ import {
} from '@/components/ui/table'
const { t } = useI18n()
const props = defineProps({
columns: Array,
data: Array,
emptyText: {
type: String,
default: ''
},
loading: {
type: Boolean,
default: false
}
})
// Set the default value for emptyText if it's empty
const emptyText = computed(
() =>
props.emptyText ||
@@ -1,6 +1,6 @@
<template>
<div class="flex justify-between h-full">
<div class="w-8/12">
<div class="w-8/12 relative">
<slot name="content" />
</div>
<div class="rounded w-3/12 p-2 space-y-2 self-start">
@@ -17,7 +17,7 @@
</router-link>
</div>
<div>
<DataTable :columns="createColumns(t)" :data="data" />
<DataTable :columns="createColumns(t)" :data="data" :loading="isLoading" />
</div>
</div>
</template>
@@ -16,7 +16,7 @@
</div>
<div>
<Spinner v-if="isLoading" />
<DataTable :columns="createColumns(t)" :data="businessHours" />
<DataTable :columns="createColumns(t)" :data="businessHours" :loading="isLoading" />
</div>
</div>
</template>
@@ -58,10 +58,10 @@
</TabsTrigger>
</TabsList>
<TabsContent value="contact">
<DataTable :columns="createColumns(t)" :data="customAttributes" />
<DataTable :columns="createColumns(t)" :data="customAttributes" :loading="isLoading" />
</TabsContent>
<TabsContent value="conversation">
<DataTable :columns="createColumns(t)" :data="customAttributes" />
<DataTable :columns="createColumns(t)" :data="customAttributes" :loading="isLoading" />
</TabsContent>
</Tabs>
</div>
+1 -1
View File
@@ -14,7 +14,7 @@
</router-link>
</div>
<div>
<DataTable :columns="columns" :data="data" />
<DataTable :columns="columns" :data="data" :loading="isLoading" />
</div>
</div>
</template>
@@ -13,7 +13,7 @@
</router-link>
</div>
<div>
<DataTable :columns="createColumns(t)" :data="macros" />
<DataTable :columns="createColumns(t)" :data="macros" :loading="formLoading" />
</div>
</div>
</template>
+1 -1
View File
@@ -14,7 +14,7 @@
</div>
</div>
<div>
<DataTable :columns="createColumns(t)" :data="oidc" />
<DataTable :columns="createColumns(t)" :data="oidc" :loading="isLoading" />
</div>
</div>
</template>
+1 -1
View File
@@ -13,7 +13,7 @@
</router-link>
</div>
<div>
<DataTable :columns="createColumns(t)" :data="roles" />
<DataTable :columns="createColumns(t)" :data="roles" :loading="isLoading" />
</div>
</div>
</template>
@@ -13,7 +13,7 @@
</router-link>
</div>
<div>
<DataTable :columns="createColumns(t)" :data="sharedViews" />
<DataTable :columns="createColumns(t)" :data="sharedViews" :loading="formLoading" />
</div>
</div>
</template>
+1 -1
View File
@@ -16,7 +16,7 @@
</div>
<div>
<Spinner v-if="isLoading"></Spinner>
<DataTable :columns="createColumns(t)" :data="slas" />
<DataTable :columns="createColumns(t)" :data="slas" :loading="isLoading" />
</div>
</div>
</template>
@@ -43,7 +43,7 @@
</div>
</div>
<div>
<DataTable :columns="createColumns(t)" :data="statuses" />
<DataTable :columns="createColumns(t)" :data="statuses" :loading="isLoading" />
</div>
</div>
</template>
+1 -1
View File
@@ -39,7 +39,7 @@
</div>
</div>
<div>
<DataTable :columns="createColumns(t)" :data="tags" />
<DataTable :columns="createColumns(t)" :data="tags" :loading="isLoading" />
</div>
</div>
</template>
+1 -1
View File
@@ -7,7 +7,7 @@
</router-link>
</div>
<div>
<DataTable :columns="columns" :data="data" />
<DataTable :columns="columns" :data="data" :loading="isLoading" />
</div>
</div>
</template>
@@ -31,10 +31,10 @@
</TabsTrigger>
</TabsList>
<TabsContent value="email_outgoing">
<DataTable :columns="createOutgoingEmailTableColumns(t)" :data="templates" />
<DataTable :columns="createOutgoingEmailTableColumns(t)" :data="templates" :loading="isLoading" />
</TabsContent>
<TabsContent value="email_notification">
<DataTable :columns="createEmailNotificationTableColumns(t)" :data="templates" />
<DataTable :columns="createEmailNotificationTableColumns(t)" :data="templates" :loading="isLoading" />
</TabsContent>
</Tabs>
</div>
@@ -14,7 +14,7 @@
</div>
</div>
<div>
<DataTable :columns="createColumns(t)" :data="webhooks" />
<DataTable :columns="createColumns(t)" :data="webhooks" :loading="isLoading" />
</div>
</div>
</template>