@7365admin1/layer-common 3.2.1-staging.75 → 3.2.2-staging.100

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/.changeset/notification-preferences.md +17 -0
  2. package/.changeset/pm-sidebar-grouping-and-list-scaffold.md +27 -0
  3. package/CHANGELOG.md +6 -0
  4. package/assets/css/primitives.css +680 -0
  5. package/assets/css/screens.css +541 -0
  6. package/assets/css/shell.css +258 -0
  7. package/assets/css/tokens.css +220 -0
  8. package/components/AccessCardQrTagging.vue +1 -1
  9. package/components/AccessManagement.vue +53 -72
  10. package/components/AppButton.vue +52 -0
  11. package/components/AppCard.vue +20 -0
  12. package/components/AppDateField.vue +35 -0
  13. package/components/AppField.vue +46 -0
  14. package/components/AppKpiTile.vue +65 -0
  15. package/components/AppSegmented.vue +38 -0
  16. package/components/AppSelect.vue +203 -0
  17. package/components/AreaChecklistHistoryMain.vue +6 -2
  18. package/components/AreaMain.vue +9 -17
  19. package/components/AttendanceMain.vue +4 -3
  20. package/components/Avatar/Main.vue +54 -2
  21. package/components/BillingMain.vue +26 -30
  22. package/components/BuildingManagement/buildings.vue +27 -38
  23. package/components/BuildingManagement/units.vue +26 -42
  24. package/components/BulletinBoardManagement.vue +23 -10
  25. package/components/CardToolbar.vue +37 -0
  26. package/components/CleaningScheduleMain.vue +29 -46
  27. package/components/ClientMain.vue +90 -78
  28. package/components/DashboardEmptyState.vue +11 -1
  29. package/components/DashboardMain.vue +285 -207
  30. package/components/DocumentManagement.vue +129 -93
  31. package/components/EntryPassMain.vue +49 -27
  32. package/components/EquipmentItemMain.vue +4 -6
  33. package/components/EquipmentManagementMain.vue +9 -7
  34. package/components/FeedbackMain.vue +35 -49
  35. package/components/FormDialog.vue +60 -21
  36. package/components/HidIntercomManagement.vue +40 -24
  37. package/components/HidQrCodeConfiguration.vue +24 -2
  38. package/components/HidUserEnrollment.vue +68 -31
  39. package/components/InvitationMain.vue +46 -61
  40. package/components/Layout/Header.vue +289 -37
  41. package/components/Layout/NavigationDrawer.vue +299 -30
  42. package/components/ManageChecklistMain.vue +17 -17
  43. package/components/MemberMain.vue +79 -60
  44. package/components/MyAttendanceMain.vue +3 -5
  45. package/components/Nfc/NFCPatrolRouteMain.vue +64 -96
  46. package/components/NotificationSettings.vue +361 -0
  47. package/components/OnlineFormsConfiguration.vue +1 -1
  48. package/components/PageHeader.vue +43 -0
  49. package/components/PassInformation.vue +104 -45
  50. package/components/RolePermissionMain.vue +184 -91
  51. package/components/ScanVisitorQRCode.vue +35 -7
  52. package/components/ScheduleAreaMain.vue +15 -49
  53. package/components/ScheduleTaskMain.vue +23 -26
  54. package/components/ScheduleTastTicketMain.vue +18 -43
  55. package/components/ServiceProviderMain.vue +113 -168
  56. package/components/SiteSettings.vue +21 -8
  57. package/components/SpecificAttr.vue +10 -2
  58. package/components/StatusChip.vue +93 -0
  59. package/components/SwitchContext.vue +33 -1
  60. package/components/TableHygiene.vue +29 -46
  61. package/components/TableList.vue +1 -1
  62. package/components/TableListSecondary.vue +1 -1
  63. package/components/TableMain.vue +161 -77
  64. package/components/TableV3.vue +61 -66
  65. package/components/UnitMain.vue +9 -17
  66. package/components/VehicleManagement.vue +20 -9
  67. package/components/VisitorForm.vue +100 -7
  68. package/components/VisitorManagement.vue +219 -68
  69. package/components/WorkOrder/Main.vue +42 -51
  70. package/composables/useHidAmico.ts +26 -2
  71. package/composables/useNotificationPreference.ts +87 -0
  72. package/composables/useOrg.ts +1 -1
  73. package/composables/useRole.ts +22 -0
  74. package/composables/useSipWebPhone.ts +28 -3
  75. package/composables/useThemePreference.ts +63 -0
  76. package/composables/useVisitor.ts +6 -1
  77. package/nuxt.config.ts +31 -0
  78. package/package.json +3 -2
  79. package/pages/notification-settings.vue +19 -0
  80. package/plugins/vuetify.ts +32 -1
  81. package/types/org.d.ts +2 -0
  82. package/utils/nav-sections.test.ts +190 -0
  83. package/utils/nav-sections.ts +151 -0
  84. package/utils/status.test.ts +77 -0
  85. package/utils/status.ts +90 -0
  86. package/utils/theme.test.ts +325 -7
  87. package/utils/theme.ts +268 -137
@@ -1,77 +1,60 @@
1
1
  <template>
2
2
  <v-row no-gutters class="pa-0">
3
3
  <v-col cols="12" class="mb-2">
4
- <v-row no-gutters align="center">
5
- <v-col cols="12" md="6" class="d-flex align-center">
6
- <v-btn
7
- class="text-none me-2"
8
- rounded="pill"
9
- variant="tonal"
10
- size="large"
11
- @click="setDocument()"
4
+ <PageHeader title="Documents">
5
+ <template #actions>
6
+ <AppButton
7
+ v-if="canCreate && canCreateFolder"
8
+ variant="ghost"
9
+ icon="mdi-folder-plus-outline"
10
+ @click="openCreateFolderDialog()"
11
+ >
12
+ Create Folder
13
+ </AppButton>
14
+
15
+ <AppButton
12
16
  v-if="canCreate && canCreateDocument"
17
+ icon="mdi-plus"
18
+ @click="setDocument()"
13
19
  >
14
20
  Add Document
15
- </v-btn>
21
+ </AppButton>
22
+ </template>
23
+ </PageHeader>
24
+
25
+ <div class="doc-filters">
26
+ <AppField
27
+ v-model="search"
28
+ search
29
+ icon="mdi-magnify"
30
+ placeholder="Search"
31
+ klass="doc-filters__search"
32
+ />
16
33
 
17
- <v-btn
18
- class="text-none"
19
- rounded="pill"
20
- variant="tonal"
21
- size="large"
22
- @click="openCreateFolderDialog()"
23
- v-if="canCreate && canCreateFolder"
24
- >
25
- Create Folder
26
- </v-btn>
27
- </v-col>
28
-
29
- <v-col
30
- cols="12"
31
- md="6"
32
- class="d-flex justify-end align-center mt-4 mt-md-0"
33
- >
34
- <v-text-field
35
- v-model="search"
36
- placeholder="Search"
37
- density="comfortable"
38
- variant="outlined"
39
- hide-details
40
- rounded="lg"
41
- class="me-3"
42
- style="max-width: 220px"
43
- />
44
-
45
- <v-menu v-model="filterMenu" offset-y>
46
- <template #activator="{ props }">
47
- <v-btn
48
- v-bind="props"
49
- variant="outlined"
50
- rounded="lg"
51
- class="filtertype-btn text-none"
52
- style="max-width: 220px"
53
- >
54
- {{ selectedFileType || "Filter by File Type" }}
55
- </v-btn>
56
- </template>
34
+ <v-menu v-model="filterMenu" offset-y>
35
+ <template #activator="{ props }">
36
+ <button v-bind="props" type="button" class="doc-filters__type">
37
+ <span>{{ selectedFileType || "Filter by File Type" }}</span>
38
+ <v-icon size="18">mdi-chevron-down</v-icon>
39
+ </button>
40
+ </template>
57
41
 
58
- <v-card class="filtertype-card">
59
- <v-list class="pa-0">
60
- <v-list-item
61
- v-for="(type, idx) in fileTypes"
62
- :key="idx"
63
- @click="selectFileType(type)"
64
- class="filtertype-item"
65
- >
66
- <v-list-item-title class="text-subtitle-2">
67
- {{ type }}
68
- </v-list-item-title>
69
- </v-list-item>
70
- </v-list>
71
- </v-card>
72
- </v-menu>
73
- </v-col>
74
- </v-row>
42
+ <v-card class="filtertype-card">
43
+ <v-list class="pa-0">
44
+ <v-list-item
45
+ v-for="(type, idx) in fileTypes"
46
+ :key="idx"
47
+ @click="selectFileType(type)"
48
+ class="filtertype-item"
49
+ >
50
+ <v-list-item-title class="text-subtitle-2">
51
+ {{ type }}
52
+ </v-list-item-title>
53
+ </v-list-item>
54
+ </v-list>
55
+ </v-card>
56
+ </v-menu>
57
+ </div>
75
58
  </v-col>
76
59
 
77
60
  <v-col cols="12" class="mb-2" v-if="isInsideFolder">
@@ -82,7 +65,7 @@
82
65
  variant="text"
83
66
  @click="goBackFolder"
84
67
  />
85
- <v-icon size="18" color="grey-darken-2">mdi-folder</v-icon>
68
+ <v-icon size="18" style="color: var(--muted)">mdi-folder</v-icon>
86
69
  <span class="text-body-2 font-weight-medium">{{
87
70
  currentFolderPath
88
71
  }}</span>
@@ -114,18 +97,18 @@
114
97
  />
115
98
 
116
99
  <span
117
- class="breadcrumb-segment text-body-2 text-grey-darken-1 cursor-pointer"
100
+ class="breadcrumb-segment text-body-2 breadcrumb-segment--link cursor-pointer"
118
101
  @click="navigateToRoot"
119
102
  >Home</span
120
103
  >
121
104
  <template v-for="(folder, idx) in folderStack" :key="folder.id">
122
- <v-icon size="14" color="grey-darken-1">mdi-chevron-right</v-icon>
105
+ <v-icon size="14" style="color: var(--muted)">mdi-chevron-right</v-icon>
123
106
  <span
124
107
  :class="[
125
108
  'breadcrumb-segment text-body-2 font-weight-medium',
126
109
  idx === folderStack.length - 1
127
- ? 'text-black'
128
- : 'text-grey-darken-1 cursor-pointer',
110
+ ? 'breadcrumb-segment--current'
111
+ : 'breadcrumb-segment--link cursor-pointer',
129
112
  ]"
130
113
  @click="
131
114
  idx < folderStack.length - 1 && navigateToStackIndex(idx)
@@ -1197,36 +1180,87 @@ function selectFileType(type: string) {
1197
1180
  </script>
1198
1181
 
1199
1182
  <style scoped>
1183
+ /*
1184
+ * EVERY COLOUR IN THIS BLOCK WAS A LITERAL. `#fff` menus, `#fafafa` bars,
1185
+ * `#e0e0e0` rules and `#38475a` ink do not follow the theme, so in dark mode
1186
+ * this screen drew white slabs on the dark page. All of them are tokens now;
1187
+ * nothing about the layout, the flow or the actions changed.
1188
+ */
1189
+
1190
+ /* The breadcrumb was `text-grey-darken-1` and `text-black` - the same two
1191
+ greys on the dark page, i.e. black lettering on a dark card. */
1192
+ .breadcrumb-segment--link {
1193
+ color: var(--muted);
1194
+ }
1195
+
1196
+ .breadcrumb-segment--current {
1197
+ color: var(--text);
1198
+ }
1199
+
1200
1200
  .table-download-btn {
1201
1201
  min-width: 106px;
1202
1202
  height: 30px;
1203
- border-radius: 4px;
1204
- background-color: #e3e3e3;
1205
- color: #38475a;
1203
+ border-radius: var(--r-tag);
1204
+ background-color: var(--hover);
1205
+ color: var(--text2);
1206
1206
  font-size: 12px;
1207
- font-weight: 500;
1207
+ font-weight: 600;
1208
1208
  box-shadow: none;
1209
1209
  }
1210
1210
 
1211
1211
  .table-download-btn:hover {
1212
- background-color: #d8d8d8;
1212
+ background-color: var(--thead);
1213
+ }
1214
+
1215
+ /* The filter row under the title: the design puts search and filters on one
1216
+ line, right-aligned, rather than in a second full-width toolbar. */
1217
+ .doc-filters {
1218
+ display: flex;
1219
+ align-items: center;
1220
+ justify-content: flex-end;
1221
+ flex-wrap: wrap;
1222
+ gap: 8px;
1223
+ margin-top: 12px;
1213
1224
  }
1214
1225
 
1215
- .filtertype-btn {
1216
- justify-content: flex-start;
1226
+ .doc-filters :deep(.doc-filters__search) {
1227
+ flex: 0 0 auto;
1217
1228
  width: 220px;
1229
+ min-width: 0;
1230
+ }
1231
+
1232
+ .doc-filters__type {
1233
+ display: flex;
1234
+ align-items: center;
1235
+ justify-content: space-between;
1236
+ gap: 8px;
1237
+ width: 220px;
1238
+ height: 38px;
1239
+ padding: 0 12px;
1240
+ border: 1px solid var(--border);
1241
+ border-radius: var(--r-inner);
1242
+ background: var(--card);
1243
+ color: var(--text);
1244
+ font-size: 13px;
1245
+ font-weight: 600;
1246
+ text-align: left;
1247
+ }
1248
+
1249
+ .doc-filters__type:hover {
1250
+ background: var(--hover);
1218
1251
  }
1219
1252
 
1220
1253
  .filtertype-card {
1221
- background: #fff;
1222
- border-radius: 8px;
1254
+ background: var(--card);
1255
+ border: 1px solid var(--border);
1256
+ border-radius: var(--r-inner);
1223
1257
  min-width: 160px;
1224
- box-shadow: 0 6px 18px rgba(0, 0, 0, 0.25);
1258
+ box-shadow: var(--shadow-menu);
1225
1259
  overflow: hidden;
1226
1260
  }
1227
1261
 
1228
1262
  .filtertype-item {
1229
- border-bottom: 1px solid #f0f0f0;
1263
+ border-bottom: 1px solid var(--border);
1230
1264
  padding: 10px 16px;
1231
1265
  }
1232
1266
 
@@ -1235,27 +1269,29 @@ function selectFileType(type: string) {
1235
1269
  }
1236
1270
 
1237
1271
  .folder-path-bar {
1238
- border: 1px solid #e0e0e0;
1239
- border-radius: 8px;
1240
- background: #fafafa;
1272
+ border: 1px solid var(--border);
1273
+ border-radius: var(--r-inner);
1274
+ background: var(--thead);
1275
+ color: var(--text);
1241
1276
  }
1242
1277
 
1243
1278
  .folder-action-btn {
1244
- color: #38475a;
1279
+ color: var(--text2);
1245
1280
  }
1246
1281
 
1247
1282
  .folder-action-list {
1248
1283
  min-width: 180px;
1249
- border-radius: 8px;
1250
- background: #fff;
1251
- box-shadow: 0 10px 28px rgba(15, 23, 42, 0.22);
1284
+ border-radius: var(--r-inner);
1285
+ background: var(--card);
1286
+ border: 1px solid var(--border);
1287
+ box-shadow: var(--shadow-menu);
1252
1288
  overflow: hidden;
1253
1289
  }
1254
1290
 
1255
1291
  .folder-action-item {
1256
1292
  min-height: 40px;
1257
- border-bottom: 1px solid #eef0f2;
1258
- color: #38475a;
1293
+ border-bottom: 1px solid var(--border);
1294
+ color: var(--text2);
1259
1295
  }
1260
1296
 
1261
1297
  .folder-action-item:last-child {
@@ -1263,11 +1299,11 @@ function selectFileType(type: string) {
1263
1299
  }
1264
1300
 
1265
1301
  .folder-dialog-card {
1266
- border-radius: 6px;
1302
+ border-radius: var(--r-inner);
1267
1303
  overflow: hidden;
1268
1304
  }
1269
1305
 
1270
1306
  .folder-dialog-toolbar {
1271
- border-bottom: 1px solid #eef0f2;
1307
+ border-bottom: 1px solid var(--border);
1272
1308
  }
1273
1309
  </style>
@@ -5,28 +5,41 @@
5
5
  :height="'200px'"
6
6
  v-if="loading"
7
7
  />
8
- <v-row no-gutters v-if="!loading">
9
- <v-col cols="12" elevation="3" class="border">
10
- <v-tabs v-model="activeTab" color="primary" class="px-2">
11
- <v-tab value="general">
12
- <v-icon start>mdi-cog-outline</v-icon>
13
- General
14
- </v-tab>
15
- <v-tab value="qrcode">
16
- <v-icon start>mdi-qrcode</v-icon>
17
- QR Code
18
- </v-tab>
19
- <v-tab value="printer">
20
- <v-icon start>mdi-printer</v-icon>
21
- Printer
22
- </v-tab>
23
- <v-tab value="entrypass">
24
- <v-icon start>mdi-link-variant</v-icon>
25
- Entrypass
26
- </v-tab>
27
- </v-tabs>
28
-
29
- <v-divider />
8
+ <div v-if="!loading">
9
+ <!--
10
+ THE DESIGN'S CARD + TAB ROW - AND DELIBERATELY NO `PageHeader`.
11
+
12
+ This screen owns no table; it is a settings screen of switches and fields,
13
+ so it takes `AppCard` + `CardToolbar`'s tabs row and no pager, count or
14
+ refresh, because it has never had any.
15
+
16
+ It gets NO page title, unlike the other screens in this pass: its only
17
+ caller mounts it INSIDE a `v-expansion-panel` on property-management's
18
+ Settings page whose panel header already reads "Entry Pass". A
19
+ `PageHeader` here would print that title twice.
20
+
21
+ The same four tabs switch the same `v-window` on the same `activeTab`,
22
+ keeping their icons; every switch, field, button and save handler is
23
+ untouched.
24
+ -->
25
+ <AppCard class="screen-card">
26
+ <CardToolbar>
27
+ <template #tabs>
28
+ <button
29
+ v-for="tab in entryPassTabs"
30
+ :key="tab.value"
31
+ type="button"
32
+ role="tab"
33
+ class="app-tab"
34
+ :class="{ 'app-tab--active': activeTab === tab.value }"
35
+ :aria-selected="activeTab === tab.value"
36
+ @click="activeTab = tab.value"
37
+ >
38
+ <v-icon start size="16">{{ tab.icon }}</v-icon>
39
+ {{ tab.label }}
40
+ </button>
41
+ </template>
42
+ </CardToolbar>
30
43
 
31
44
  <v-window v-model="activeTab">
32
45
  <!-- ─── Tab 1: General ─────────────────────────────────── -->
@@ -38,7 +51,7 @@
38
51
  <div class="d-flex mt-3 pb-5 pr-5">
39
52
  <p class="mr-6 font-weight-medium flex-grow-1">
40
53
  NFC / QR Code Pass
41
- <span class="d-block text-caption text-grey-darken-1 mt-1">
54
+ <span class="d-block text-caption screen-hint mt-1">
42
55
  NFC Card Pass uses Near Field Communication (NFC) technology
43
56
  to enable secure and convenient entry within ~4 cm proximity.
44
57
  </span>
@@ -59,7 +72,7 @@
59
72
  <div class="mt-5 pb-5">
60
73
  <p class="font-weight-medium mb-1">
61
74
  Access Door / Lift
62
- <span class="d-block text-caption text-grey-darken-1 mt-1">
75
+ <span class="d-block text-caption screen-hint mt-1">
63
76
  Choose the access method(s) for door and lift control.
64
77
  At least one method must be enabled.
65
78
  </span>
@@ -190,7 +203,7 @@
190
203
  <div class="d-flex mt-3 pb-5">
191
204
  <p class="font-weight-medium flex-grow-1">
192
205
  Upload Access Cards Template
193
- <span class="d-block text-caption text-grey-darken-1 mt-1">
206
+ <span class="d-block text-caption screen-hint mt-1">
194
207
  <a
195
208
  v-if="currentEntryPassSettings.template?.id"
196
209
  href="#"
@@ -379,7 +392,7 @@
379
392
  </v-row>
380
393
  </v-window-item>
381
394
  </v-window>
382
- </v-col>
395
+ </AppCard>
383
396
 
384
397
  <Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
385
398
 
@@ -388,7 +401,7 @@
388
401
  :header="currentEntryPassSettings.qrCodeTemplateHeader"
389
402
  :sub-text="currentEntryPassSettings.qrCodeTemplateHeaderSubText"
390
403
  />
391
- </v-row>
404
+ </div>
392
405
  </template>
393
406
 
394
407
  <script lang="ts" setup>
@@ -400,6 +413,15 @@ const prop = defineProps({
400
413
 
401
414
  const activeTab = ref("general");
402
415
 
416
+ /** The same four tabs, in the same order, moved out of the template so the
417
+ design's tab row can `v-for` them. Values unchanged - they key `v-window`. */
418
+ const entryPassTabs = [
419
+ { value: "general", label: "General", icon: "mdi-cog-outline" },
420
+ { value: "qrcode", label: "QR Code", icon: "mdi-qrcode" },
421
+ { value: "printer", label: "Printer", icon: "mdi-printer" },
422
+ { value: "entrypass", label: "Entrypass", icon: "mdi-link-variant" },
423
+ ];
424
+
403
425
  const currentEntryPassSettings = ref<Record<string, any>>({
404
426
  _id: "",
405
427
  site: "",
@@ -2,7 +2,7 @@
2
2
  <v-row no-gutters align="center" justify="center">
3
3
  <v-col cols="12" lg="12">
4
4
  <TableMain
5
- :title="`${siteName} Units`"
5
+ :title="`${siteName} Equipment Items`"
6
6
  :items="items"
7
7
  :headers="headers"
8
8
  :loading="loading"
@@ -20,10 +20,8 @@
20
20
  <v-row no-gutters align="center" class="w-100">
21
21
  <v-col cols="auto" v-if="canCreateEquipmentItem">
22
22
  <v-btn
23
- class="text-none"
24
- rounded="pill"
25
- variant="tonal"
26
- size="large"
23
+ class="text-none screen-btn-primary"
24
+ variant="flat"
27
25
  @click="openEquipmentItemDialog()"
28
26
  >
29
27
  Checkout Item
@@ -32,7 +30,7 @@
32
30
 
33
31
  <v-spacer />
34
32
 
35
- <v-col cols="auto">
33
+ <v-col cols="auto" class="filter-field">
36
34
  <v-text-field
37
35
  v-model="searchInput"
38
36
  density="compact"
@@ -2,7 +2,7 @@
2
2
  <v-row no-gutters align="center" justify="center">
3
3
  <v-col cols="12" lg="12">
4
4
  <TableMain
5
- :title="`${siteName} Units`"
5
+ :title="`${siteName} Equipment`"
6
6
  :items="items"
7
7
  :headers="headers"
8
8
  :loading="loading"
@@ -20,10 +20,8 @@
20
20
  <v-row no-gutters align="center" class="w-100">
21
21
  <v-col cols="auto" v-if="canAddEquipment">
22
22
  <v-btn
23
- class="text-none"
24
- rounded="pill"
25
- variant="tonal"
26
- size="large"
23
+ class="text-none screen-btn-primary"
24
+ variant="flat"
27
25
  @click="onCreateUnit()"
28
26
  >
29
27
  Add Equipment
@@ -32,7 +30,7 @@
32
30
 
33
31
  <v-spacer />
34
32
 
35
- <v-col cols="auto">
33
+ <v-col cols="auto" class="filter-field">
36
34
  <v-text-field
37
35
  v-model="searchInput"
38
36
  density="compact"
@@ -60,7 +58,11 @@
60
58
  </template>
61
59
 
62
60
  <template #item.status="{ value }">
63
- <v-chip class="text-capitalize">{{ value || "N/A" }}</v-chip>
61
+ <StatusChip
62
+ class="text-capitalize"
63
+ :status="value"
64
+ :label="value || 'N/A'"
65
+ />
64
66
  </template>
65
67
 
66
68
  <template #item.block="{ value }">
@@ -1,7 +1,17 @@
1
1
  <template>
2
- <v-row no-gutters>
2
+ <div>
3
+ <!-- The design's title row: the page's name at 22px and its primary
4
+ action on the right of that same line. Placement only - the same
5
+ button opens the same dialog under the same permission. -->
6
+ <PageHeader title="Feedbacks">
7
+ <template v-if="canCreateFeedback" #actions>
8
+ <AppButton @click="showCreateDialog = true">Create Feedback</AppButton>
9
+ </template>
10
+ </PageHeader>
11
+
12
+ <v-row no-gutters>
3
13
  <v-col cols="12" class="mb-2">
4
- <v-row no-gutters align="center">
14
+ <v-row no-gutters align="center" class="filter-bar">
5
15
  <v-text-field
6
16
  v-model="searchText"
7
17
  placeholder="Search feedback..."
@@ -50,51 +60,32 @@
50
60
  <v-date-picker v-model="endDate" width="320" :locale="locale" />
51
61
  </v-card>
52
62
  </v-dialog>
53
- <v-btn
54
- v-if="canCreateFeedback"
55
- text="create feedback"
56
- class="text-none text-capitalize ml-2"
57
- rounded="pill"
58
- variant="tonal"
59
- size="large"
60
- @click="showCreateDialog = true"
61
- />
62
63
  </v-row>
63
64
  </v-col>
64
65
 
65
66
  <v-col cols="12">
66
- <v-card
67
- width="100%"
68
- variant="outlined"
69
- border="thin"
70
- rounded="lg"
71
- :loading="loading"
72
- >
73
- <v-toolbar density="compact" color="grey-lighten-4">
74
- <template #prepend>
75
- <v-btn
76
- fab
77
- icon
78
- density="comfortable"
67
+ <AppCard class="table-card">
68
+ <!-- One row inside the card - refresh, the range and the pager - in
69
+ place of a `v-toolbar` that stacked them above the table. Same
70
+ three controls, same handlers. -->
71
+ <CardToolbar :count="pageRange">
72
+ <template #left>
73
+ <AppButton
74
+ variant="icon"
75
+ icon="mdi-refresh"
76
+ aria-label="Refresh"
79
77
  @click="(page = 1), refreshFeedbacks()"
80
- >
81
- <v-icon icon="mdi-refresh" />
82
- </v-btn>
78
+ />
83
79
  </template>
84
80
 
85
- <template #append>
86
- <v-row no-gutters justify="end" align="center">
87
- <span class="mr-2 text-caption text-fontgray">
88
- {{ pageRange }}
89
- </span>
90
- <local-pagination
91
- v-model="page"
92
- :length="pages"
93
- @update:value="updatePage"
94
- />
95
- </v-row>
81
+ <template #right>
82
+ <local-pagination
83
+ v-model="page"
84
+ :length="pages"
85
+ @update:value="updatePage"
86
+ />
96
87
  </template>
97
- </v-toolbar>
88
+ </CardToolbar>
98
89
 
99
90
  <v-data-table
100
91
  :headers="headers"
@@ -104,6 +95,7 @@
104
95
  fixed-header
105
96
  hide-default-footer
106
97
  :hide-default-header="false"
98
+ :loading="loading"
107
99
  @click:row="tableRowClickHandler"
108
100
  style="max-height: calc(100vh - (200px))"
109
101
  >
@@ -139,13 +131,7 @@
139
131
  </template>
140
132
  <template #item.status="{ value }">
141
133
  <span class="d-inline-block text-truncate">
142
- <v-chip
143
- :color="getStatusColor(value ?? '')"
144
- class="pa-5 text-capitalize text-center whitespace-nowrap text-body-1"
145
- variant="flat"
146
- >
147
- {{ value }}
148
- </v-chip>
134
+ <StatusChip class="text-capitalize" :status="value" />
149
135
  </span>
150
136
  </template>
151
137
  <template #item.assigneeInfo="{ item, index }">
@@ -183,9 +169,10 @@
183
169
  <span v-else class="mt-1 text-wrap"> Not Assigned </span>
184
170
  </template>
185
171
  </v-data-table>
186
- </v-card>
172
+ </AppCard>
187
173
  </v-col>
188
- </v-row>
174
+ </v-row>
175
+ </div>
189
176
 
190
177
  <!-- <FeedbackForm
191
178
  v-model="showCreateDialog"
@@ -660,7 +647,6 @@ const _feedback = ref({
660
647
  });
661
648
 
662
649
  const {
663
- getStatusColor,
664
650
  debounce,
665
651
  standardFormatDateTime,
666
652
  standardFormatDate,