From 6d56db384fbefd9ab8fcb9b5ab69021b6308ef71 Mon Sep 17 00:00:00 2001 From: Sin-Kang Date: Wed, 3 Jun 2026 22:47:55 +0900 Subject: [PATCH] feat: skeleton loading for list tables Replace the default spinner overlay on the list screens with a table-shaped skeleton (header strip + placeholder rows) on first load, so the page hints at its structure instead of flashing an empty area. Matches the Skeleton already used on the dashboard. - new reusable (PrimeVue Skeleton) - shown via v-if="loading && !items.length"; the real table is v-else, so background refreshes keep showing data (no skeleton flash on reload) - applied to Users, Roles, Permissions, Groups, Tenants, Policies, Audit Logs (DataTable) and Menus (TreeTable) - common.loading i18n key (en/ko) for the aria-label Verification: npm run build (vue-tsc + Vite) green. --- src/components/TableSkeleton.vue | 40 ++++++++++++++++++++++++++++++++ src/i18n/locales/en.ts | 1 + src/i18n/locales/ko.ts | 1 + src/views/AuditLogsView.vue | 3 +++ src/views/GroupsView.vue | 3 +++ src/views/MenusView.vue | 4 +++- src/views/PermissionsView.vue | 3 +++ src/views/PoliciesView.vue | 3 +++ src/views/RolesView.vue | 3 +++ src/views/TenantsView.vue | 3 +++ src/views/UsersView.vue | 3 +++ 11 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 src/components/TableSkeleton.vue diff --git a/src/components/TableSkeleton.vue b/src/components/TableSkeleton.vue new file mode 100644 index 0000000..db5e5f3 --- /dev/null +++ b/src/components/TableSkeleton.vue @@ -0,0 +1,40 @@ + + + diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index d024662..398abf0 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -157,6 +157,7 @@ export default { close: 'Close', search: 'Search', noResults: 'No results', + loading: 'Loading…', tenantId: 'Tenant ID', code: 'Code', name: 'Name', diff --git a/src/i18n/locales/ko.ts b/src/i18n/locales/ko.ts index e97ba1d..abb2c44 100644 --- a/src/i18n/locales/ko.ts +++ b/src/i18n/locales/ko.ts @@ -157,6 +157,7 @@ export default { close: '닫기', search: '검색', noResults: '결과 없음', + loading: '불러오는 중…', tenantId: '테넌트 ID', code: '코드', name: '이름', diff --git a/src/views/AuditLogsView.vue b/src/views/AuditLogsView.vue index 63819f0..9cb2695 100644 --- a/src/views/AuditLogsView.vue +++ b/src/views/AuditLogsView.vue @@ -3,6 +3,7 @@ import { computed, onMounted, ref } from 'vue' import { useI18n } from 'vue-i18n' import DataTable, { type DataTablePageEvent, type DataTableRowClickEvent } from 'primevue/datatable' import Column from 'primevue/column' +import TableSkeleton from '@/components/TableSkeleton.vue' import Button from 'primevue/button' import Dialog from 'primevue/dialog' import InputText from 'primevue/inputtext' @@ -132,7 +133,9 @@ onMounted(reload) + + { - + + diff --git a/src/views/PermissionsView.vue b/src/views/PermissionsView.vue index 839c5fa..ce50537 100644 --- a/src/views/PermissionsView.vue +++ b/src/views/PermissionsView.vue @@ -4,6 +4,7 @@ import { useI18n } from 'vue-i18n' import DataTable from 'primevue/datatable' import Column from 'primevue/column' import EmptyState from '@/components/EmptyState.vue' +import TableSkeleton from '@/components/TableSkeleton.vue' import Button from 'primevue/button' import Dialog from 'primevue/dialog' import InputText from 'primevue/inputtext' @@ -147,7 +148,9 @@ onMounted(reload) + + + + +