@7365admin1/layer-common 3.2.1 → 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 (123) hide show
  1. package/.changeset/camera-wall-gated-endpoint.md +47 -0
  2. package/.changeset/camera-wall-plain-english.md +60 -0
  3. package/.changeset/camera-wall-selection-and-guidance.md +65 -0
  4. package/.changeset/dark-primary-contrast.md +39 -0
  5. package/.changeset/dashboard-dark-theme-contrast.md +15 -0
  6. package/.changeset/notification-preferences.md +17 -0
  7. package/.changeset/pm-sidebar-grouping-and-list-scaffold.md +27 -0
  8. package/.changeset/service-provider-invitation-actions.md +28 -0
  9. package/assets/css/primitives.css +680 -0
  10. package/assets/css/screens.css +541 -0
  11. package/assets/css/shell.css +258 -0
  12. package/assets/css/tokens.css +220 -0
  13. package/components/AccessCardQrTagging.vue +1 -1
  14. package/components/AccessManagement.vue +53 -72
  15. package/components/AppButton.vue +52 -0
  16. package/components/AppCard.vue +20 -0
  17. package/components/AppDateField.vue +35 -0
  18. package/components/AppField.vue +46 -0
  19. package/components/AppKpiTile.vue +65 -0
  20. package/components/AppSegmented.vue +38 -0
  21. package/components/AppSelect.vue +203 -0
  22. package/components/AreaChecklistHistoryMain.vue +6 -2
  23. package/components/AreaMain.vue +9 -17
  24. package/components/AttendanceMain.vue +4 -3
  25. package/components/Avatar/Main.vue +54 -2
  26. package/components/BillingMain.vue +26 -30
  27. package/components/BuildingManagement/buildings.vue +27 -38
  28. package/components/BuildingManagement/units.vue +26 -42
  29. package/components/BulletinBoardManagement.vue +23 -10
  30. package/components/CameraWall.vue +1034 -0
  31. package/components/CameraWallTile.vue +818 -0
  32. package/components/CardToolbar.vue +37 -0
  33. package/components/CleaningScheduleMain.vue +29 -46
  34. package/components/ClientMain.vue +90 -78
  35. package/components/DashboardEmptyState.vue +11 -1
  36. package/components/DashboardMain.vue +349 -275
  37. package/components/DashboardPanel.vue +1 -1
  38. package/components/DocumentManagement.vue +129 -93
  39. package/components/EntryPassInformation.vue +4 -4
  40. package/components/EntryPassMain.vue +49 -27
  41. package/components/EquipmentItemMain.vue +4 -6
  42. package/components/EquipmentManagementMain.vue +9 -7
  43. package/components/FeedbackMain.vue +42 -56
  44. package/components/FormDialog.vue +60 -21
  45. package/components/HidAccessLogDashboard.vue +119 -44
  46. package/components/HidCameraCaptureDialog.vue +199 -0
  47. package/components/HidIntercomManagement.vue +825 -202
  48. package/components/HidQrCodeConfiguration.vue +927 -93
  49. package/components/HidServiceSettingsPanel.vue +20 -1
  50. package/components/HidUserEnrollment.vue +454 -106
  51. package/components/InvitationMain.vue +46 -61
  52. package/components/Layout/Header.vue +289 -37
  53. package/components/Layout/NavigationDrawer.vue +340 -30
  54. package/components/ManageChecklistMain.vue +17 -17
  55. package/components/MemberMain.vue +79 -60
  56. package/components/MyAttendanceMain.vue +3 -5
  57. package/components/NavigationItem.vue +9 -0
  58. package/components/Nfc/NFCPatrolRouteMain.vue +116 -103
  59. package/components/NotificationSettings.vue +361 -0
  60. package/components/OnlineFormConfigurationForm.vue +620 -224
  61. package/components/OnlineFormFill.vue +36 -8
  62. package/components/OnlineFormsConfiguration.vue +7 -3
  63. package/components/PageHeader.vue +43 -0
  64. package/components/PassInformation.vue +104 -45
  65. package/components/QrTemplate/CreditCardLandscape.vue +19 -12
  66. package/components/QrTemplate/PrintDialog.vue +209 -196
  67. package/components/RolePermissionMain.vue +184 -91
  68. package/components/ScanVisitorQRCode.vue +35 -7
  69. package/components/ScheduleAreaMain.vue +15 -49
  70. package/components/ScheduleTaskMain.vue +23 -26
  71. package/components/ScheduleTastTicketMain.vue +18 -43
  72. package/components/ServiceProviderInvitationPrompt.vue +150 -0
  73. package/components/ServiceProviderMain.vue +346 -157
  74. package/components/SiteSettings.vue +25 -10
  75. package/components/SpecificAttr.vue +10 -2
  76. package/components/StatusChip.vue +93 -0
  77. package/components/SwitchContext.vue +33 -1
  78. package/components/TableHygiene.vue +29 -46
  79. package/components/TableList.vue +1 -1
  80. package/components/TableListSecondary.vue +1 -1
  81. package/components/TableMain.vue +161 -77
  82. package/components/TableV3.vue +61 -66
  83. package/components/UnitMain.vue +9 -17
  84. package/components/VehicleManagement.vue +20 -9
  85. package/components/VisitorForm.vue +294 -78
  86. package/components/VisitorManagement.vue +221 -71
  87. package/components/VisitorSocketPopUp.vue +56 -2
  88. package/components/WorkOrder/Main.vue +42 -51
  89. package/composables/useComment.ts +3 -3
  90. package/composables/useHidAmico.ts +151 -0
  91. package/composables/useHidNavigation.ts +0 -7
  92. package/composables/useLocalAuth.ts +9 -36
  93. package/composables/useNotificationPreference.ts +87 -0
  94. package/composables/useOnlineFormPages.ts +2 -0
  95. package/composables/useOrg.ts +1 -1
  96. package/composables/useRole.ts +22 -0
  97. package/composables/useServiceProviderInvitation.ts +145 -0
  98. package/composables/useSipWebPhone.ts +336 -0
  99. package/composables/useSiteSettings.ts +50 -0
  100. package/composables/useThemePreference.ts +63 -0
  101. package/composables/useVisitor.ts +6 -1
  102. package/composables/useVisitorSocket.ts +26 -0
  103. package/composables/useWebUsb.ts +127 -37
  104. package/nuxt.config.ts +31 -0
  105. package/package.json +4 -1
  106. package/pages/[org]/[site]/access-mgmt/intercom/index.vue +1 -1
  107. package/pages/notification-settings.vue +19 -0
  108. package/plugins/secure-member.client.ts +21 -56
  109. package/plugins/vuetify.ts +68 -9
  110. package/test/visitor-socket.test.mjs +36 -0
  111. package/types/org.d.ts +2 -0
  112. package/types/site.d.ts +71 -0
  113. package/utils/camera-wall.test.ts +571 -0
  114. package/utils/camera-wall.ts +926 -0
  115. package/utils/nav-sections.test.ts +190 -0
  116. package/utils/nav-sections.ts +151 -0
  117. package/utils/status.test.ts +77 -0
  118. package/utils/status.ts +90 -0
  119. package/utils/theme.test.ts +534 -0
  120. package/utils/theme.ts +294 -0
  121. package/components/HidIdentityMapping.vue +0 -975
  122. package/middleware/member.ts +0 -4
  123. package/pages/[org]/[site]/access-mgmt/identity-mapping/index.vue +0 -23
@@ -1,85 +1,158 @@
1
+ <!--
2
+ PHASE 3 - THE STANDARD TABLE CARD.
3
+
4
+ The redesign is, more than anything else, a table card: toolbar row (refresh,
5
+ optional in-card search, pagination) -> uppercase header row -> rows. This is
6
+ the shared one, so this file is where that pattern is drawn once.
7
+
8
+ VISUAL ONLY, and that matters more here than anywhere. Every column, filter,
9
+ sort, action, slot, prop, emit and page still behaves exactly as it did:
10
+ the headers are the caller's, the items are the caller's, every slot is still
11
+ forwarded to `v-data-table` untouched, and no prop's default has changed. The
12
+ empty state is the same wording `v-data-table` already showed, with the icon
13
+ the design draws above it - and it is only supplied when the caller has NOT
14
+ passed its own `no-data` slot.
15
+ -->
1
16
  <template>
2
- <v-row no-gutters>
3
- <!-- Top Actions -->
4
- <v-col cols="12" class="mb-2" v-if="canCreate || $slots.actions">
5
- <v-row no-gutters>
6
- <slot name="actions">
7
- <v-col cols="12">
8
- <v-row no-gutters class="ga-2">
9
- <v-btn
10
- v-if="canCreate"
11
- class="text-none"
12
- rounded="pill"
13
- variant="tonal"
14
- size="large"
15
- @click="emits('create')"
16
- >
17
- {{ createLabel }}
18
- </v-btn>
19
- <v-text-field
20
- v-if="canSearch"
21
- v-model="searchInput"
22
- density="compact"
23
- placeholder="Search"
24
- clearable
25
- max-width="300"
26
- append-inner-icon="mdi-magnify"
27
- hide-details
28
- />
29
- <v-btn
30
- v-if="canScanVisitorQRCode"
31
- text="Scan QR Code"
32
- class="text-none ml-2"
33
- rounded="pill"
34
- variant="tonal"
35
- size="large"
36
- @click="emits('scan')"
37
- />
38
- </v-row>
39
- </v-col>
40
- </slot>
41
- </v-row>
17
+ <v-row no-gutters class="table-card__shrink">
18
+ <!--
19
+ THE DESIGN'S PAGE TITLE (Phase 9).
20
+
21
+ The design opens every screen with a 22px/800 heading. This shared table
22
+ card is mounted by 17 module screens, and none of them had one - four of
23
+ them (Units, Areas, Equipment Item, Equipment Management) were already
24
+ PASSING `:title` to this component, which had no `title` prop, so the
25
+ string was landing on the root element as a plain HTML `title` attribute:
26
+ a browser tooltip nobody asked for. It now draws the heading it was
27
+ always meant to draw.
28
+
29
+ Placement only. No caller changes, no slot moves, no behaviour: a screen
30
+ that passes no title renders exactly what it rendered before.
31
+ -->
32
+ <!--
33
+ The actions used to be drawn on a SECOND row underneath that heading,
34
+ which is not where the design puts them - the design's title row is the
35
+ page name on the left and the screen's actions on the right of the SAME
36
+ line, which is why every screen converted so far had to bypass this
37
+ component and draw its own `PageHeader` above it. They are on the title
38
+ row now, in `PageHeader`'s own `actions` slot.
39
+
40
+ Same buttons, same slot name, same content, same events - one row instead
41
+ of two. A screen with no title still gets its actions at the left, where
42
+ they have always been, because the title block is not drawn at all.
43
+ -->
44
+ <v-col cols="12" v-if="title || canCreate || $slots.actions">
45
+ <PageHeader :title="title">
46
+ <template v-if="canCreate || $slots.actions" #actions>
47
+ <slot name="actions">
48
+ <!--
49
+ The design's primary button: the accent fill that may carry a
50
+ white label (`--accent-strong`; white on `--accent` is 3.23:1),
51
+ 40px, 10px radius, leading icon. Same click, same label.
52
+ -->
53
+ <v-btn
54
+ v-if="canCreate"
55
+ class="text-none table-card__primary"
56
+ variant="flat"
57
+ prepend-icon="mdi-plus"
58
+ @click="emits('create')"
59
+ >
60
+ {{ createLabel }}
61
+ </v-btn>
62
+ <v-text-field
63
+ v-if="canSearch"
64
+ v-model="searchInput"
65
+ density="compact"
66
+ placeholder="Search"
67
+ clearable
68
+ max-width="300"
69
+ append-inner-icon="mdi-magnify"
70
+ hide-details
71
+ class="table-card__search"
72
+ />
73
+ <!-- Ghost/secondary, as the design specifies for Scan QR Code. -->
74
+ <v-btn
75
+ v-if="canScanVisitorQRCode"
76
+ text="Scan QR Code"
77
+ class="text-none table-card__ghost"
78
+ variant="outlined"
79
+ @click="emits('scan')"
80
+ />
81
+ </slot>
82
+ </template>
83
+ </PageHeader>
42
84
  </v-col>
43
85
 
44
86
  <!-- Table Card -->
45
- <v-col cols="12">
46
- <v-card
47
- width="100%"
48
- variant="outlined"
49
- border="thin"
50
- rounded="lg"
51
- :loading="loading"
52
- >
87
+ <!--
88
+ `table-card__shrink` is the fix for the wide-table blowout: this column is a
89
+ flex item, and a flex item's automatic minimum size is its CONTENT's, so
90
+ a table with enough columns set a floor the column would not shrink below
91
+ and the whole page scrolled sideways at 360 and 768. The table scrolls
92
+ inside the card instead - see `.table-card__shrink` in `screens.css`.
93
+ -->
94
+ <v-col cols="12" class="table-card__shrink">
95
+ <!--
96
+ THE DESIGN'S CARD AND ITS IN-CARD TOP ROW (Phase 9).
97
+
98
+ Was a `v-card` + `v-toolbar`. `v-card` brings its own radius, elevation
99
+ and surface at a specificity a class has to fight, and `v-toolbar` puts
100
+ its `extension` on a SECOND stacked row. `AppCard` + `CardToolbar` are
101
+ the primitives the design is drawn with, already proven on
102
+ `ServiceProviderMain` / `MemberMain`.
103
+
104
+ Placement only. The refresh button, the `prepend-additional` slot, the
105
+ page range, the pager, the `extension` slot and the `footer` slot are
106
+ the same controls, emitting the same events, in the same order. The
107
+ `loading` bar `v-card` used to draw is drawn explicitly so a loading
108
+ screen still says so.
109
+ -->
110
+ <AppCard class="table-card">
111
+ <v-progress-linear
112
+ v-if="loading"
113
+ indeterminate
114
+ height="2"
115
+ class="table-card__loading"
116
+ />
117
+
53
118
  <!-- Toolbar -->
54
- <v-toolbar
55
- density="compact"
56
- color="grey-lighten-4"
57
- :extension-height="extensionHeight"
58
- >
59
- <template #prepend>
60
- <v-btn fab icon density="comfortable" @click="emits('refresh')">
119
+ <CardToolbar :count="pageRange">
120
+ <!--
121
+ The design draws a screen's tabs ON the toolbar row, not on a
122
+ stacked strip above the table. `CardToolbar` has always had the
123
+ slot; `TableMain` simply never forwarded it, so callers put their
124
+ `v-tabs` in `extension` and got a second row. Purely additive: a
125
+ caller that passes no `tabs` renders exactly what it rendered.
126
+ -->
127
+ <template v-if="$slots.tabs" #tabs>
128
+ <slot name="tabs" />
129
+ </template>
130
+
131
+ <template #left>
132
+ <v-btn fab icon density="comfortable" variant="text" @click="emits('refresh')">
61
133
  <v-icon>mdi-refresh</v-icon>
62
134
  </v-btn>
63
135
  <slot name="prepend-additional" />
64
136
  </template>
65
137
 
66
- <template #append>
67
- <v-row no-gutters justify="end" align="center">
68
- <span class="mr-2 text-caption text-fontgray">
69
- {{ pageRange }}
70
- </span>
71
- <local-pagination
72
- v-model="internalPage"
73
- :length="pages"
74
- @update:value="emits('update:page', internalPage)"
75
- />
76
- </v-row>
138
+ <template #right>
139
+ <local-pagination
140
+ v-model="internalPage"
141
+ :length="pages"
142
+ @update:value="emits('update:page', internalPage)"
143
+ />
77
144
  </template>
145
+ </CardToolbar>
78
146
 
79
- <template v-if="$slots.extension" #extension>
80
- <slot name="extension" />
81
- </template>
82
- </v-toolbar>
147
+ <!--
148
+ The callers' `extension` is a FILTER row (selects, date ranges), not
149
+ tabs - it is full-width by construction, so it keeps its own line
150
+ under the toolbar rather than being folded into it, which would push
151
+ the pager off the end of the row.
152
+ -->
153
+ <div v-if="$slots.extension" class="table-card__extension">
154
+ <slot name="extension" />
155
+ </div>
83
156
 
84
157
  <!-- Data Table -->
85
158
  <v-data-table
@@ -96,15 +169,32 @@
96
169
  <template v-for="(_, slotName) in $slots" #[slotName]="slotProps">
97
170
  <slot :name="slotName" v-bind="slotProps" />
98
171
  </template>
172
+
173
+ <!--
174
+ The design's empty state. Same sentence the table already showed;
175
+ only supplied when the caller has not passed its own, so nobody's
176
+ existing empty state is replaced.
177
+ -->
178
+ <template v-if="!$slots['no-data']" #no-data>
179
+ <div class="table-card__empty">
180
+ <v-icon icon="mdi-magnify-close" size="32" />
181
+ <span>No data available</span>
182
+ </div>
183
+ </template>
99
184
  </v-data-table>
100
185
  <slot name="footer" />
101
- </v-card>
186
+ </AppCard>
102
187
  </v-col>
103
188
  </v-row>
104
189
  </template>
105
190
 
106
191
  <script lang="ts" setup>
107
192
  const props = defineProps({
193
+ // The design's page heading. Empty = no heading, i.e. exactly today's markup.
194
+ title: {
195
+ type: String,
196
+ default: "",
197
+ },
108
198
  headers: {
109
199
  type: Array as PropType<Array<Record<string, any>>>,
110
200
  required: true,
@@ -185,9 +275,3 @@ watch(
185
275
  }
186
276
  );
187
277
  </script>
188
-
189
- <style scoped>
190
- .v-data-table :deep(thead th) {
191
- font-weight: bold !important;
192
- }
193
- </style>
@@ -5,43 +5,39 @@
5
5
  border="thin"
6
6
  rounded="lg"
7
7
  :loading="loading"
8
- class="table-v3"
8
+ class="table-v3 table-card"
9
9
  >
10
- <v-toolbar density="compact" color="grey-lighten-5">
11
- <template #prepend>
10
+ <div class="app-toolbar app-toolbar--plain">
11
+ <v-btn
12
+ icon
13
+ variant="text"
14
+ density="comfortable"
15
+ class="table-v3__refresh-btn"
16
+ @click="emit('refresh')"
17
+ >
18
+ <v-icon size="24">mdi-history</v-icon>
19
+ </v-btn>
20
+
21
+ <div class="app-toolbar__spacer" />
22
+
23
+ <div class="app-toolbar__right">
24
+ <span class="table-card__range">{{ pageRange }}</span>
12
25
  <v-btn
13
- icon
26
+ icon="mdi-chevron-left"
14
27
  variant="text"
15
28
  density="comfortable"
16
- class="table-v3__refresh-btn"
17
- @click="emit('refresh')"
18
- >
19
- <v-icon size="28" color="black">mdi-history</v-icon>
20
- </v-btn>
21
- </template>
22
-
23
- <template #append>
24
- <v-row no-gutters justify="end" align="center">
25
- <span class="mr-2 text-caption text-fontgray">
26
- {{ pageRange }}
27
- </span>
28
- <v-btn
29
- icon="mdi-chevron-left"
30
- variant="text"
31
- density="comfortable"
32
- :disabled="!canGoPrev"
33
- @click="goPrev"
34
- />
35
- <v-btn
36
- icon="mdi-chevron-right"
37
- variant="text"
38
- density="comfortable"
39
- :disabled="!canGoNext"
40
- @click="goNext"
41
- />
42
- </v-row>
43
- </template>
44
- </v-toolbar>
29
+ :disabled="!canGoPrev"
30
+ @click="goPrev"
31
+ />
32
+ <v-btn
33
+ icon="mdi-chevron-right"
34
+ variant="text"
35
+ density="comfortable"
36
+ :disabled="!canGoNext"
37
+ @click="goNext"
38
+ />
39
+ </div>
40
+ </div>
45
41
 
46
42
  <div v-if="$slots.breadcrumb" class="table-v3__breadcrumb">
47
43
  <slot name="breadcrumb" />
@@ -62,29 +58,27 @@
62
58
  </template>
63
59
  </v-data-table>
64
60
 
65
- <v-toolbar density="compact" color="grey-lighten-5" class="table-v3__footer">
66
- <template #append>
67
- <v-row no-gutters justify="end" align="center">
68
- <span class="mr-2 text-caption text-fontgray">
69
- {{ pageRange }}
70
- </span>
71
- <v-btn
72
- icon="mdi-chevron-left"
73
- variant="text"
74
- density="comfortable"
75
- :disabled="!canGoPrev"
76
- @click="goPrev"
77
- />
78
- <v-btn
79
- icon="mdi-chevron-right"
80
- variant="text"
81
- density="comfortable"
82
- :disabled="!canGoNext"
83
- @click="goNext"
84
- />
85
- </v-row>
86
- </template>
87
- </v-toolbar>
61
+ <div class="app-toolbar app-toolbar--plain table-v3__footer">
62
+ <div class="app-toolbar__spacer" />
63
+
64
+ <div class="app-toolbar__right">
65
+ <span class="table-card__range">{{ pageRange }}</span>
66
+ <v-btn
67
+ icon="mdi-chevron-left"
68
+ variant="text"
69
+ density="comfortable"
70
+ :disabled="!canGoPrev"
71
+ @click="goPrev"
72
+ />
73
+ <v-btn
74
+ icon="mdi-chevron-right"
75
+ variant="text"
76
+ density="comfortable"
77
+ :disabled="!canGoNext"
78
+ @click="goNext"
79
+ />
80
+ </div>
81
+ </div>
88
82
  </v-card>
89
83
  </template>
90
84
 
@@ -161,19 +155,20 @@ watch(
161
155
  </script>
162
156
 
163
157
  <style scoped>
164
- .table-v3 :deep(thead th) {
165
- background-color: #f5f5f5;
166
- color: #6b7280;
167
- font-weight: 600 !important;
168
- font-size: 12px;
169
- }
170
-
158
+ /*
159
+ * The header row is no longer drawn here. It was `#f5f5f5` on `#6b7280`,
160
+ * both literal, so in the dark theme the fixed-header rule won the background
161
+ * and the header read 3.45:1. `.table-card` on the root gives this table the
162
+ * same tokenised header, cell and hover treatment as every other table in the
163
+ * product, which measures 4.77:1 light / 4.71:1 dark.
164
+ */
171
165
  .table-v3 :deep(tbody td) {
172
166
  height: 54px;
173
167
  }
174
168
 
175
169
  .table-v3__footer {
176
- border-top: 1px solid rgba(0, 0, 0, 0.08);
170
+ border-top: 1px solid var(--border);
171
+ border-bottom: 0;
177
172
  }
178
173
 
179
174
  .table-v3__refresh-btn {
@@ -182,8 +177,8 @@ watch(
182
177
  }
183
178
 
184
179
  .table-v3__breadcrumb {
185
- border-bottom: 1px solid rgba(0, 0, 0, 0.08);
186
- background-color: #fff;
180
+ border-bottom: 1px solid var(--border);
181
+ background-color: var(--card);
187
182
  padding: 6px 12px;
188
183
  }
189
184
  </style>
@@ -24,10 +24,8 @@
24
24
  <!-- Primary action -->
25
25
  <v-btn
26
26
  v-if="canCreateUnit"
27
- class="text-none mr-3"
28
- rounded="pill"
29
- variant="tonal"
30
- size="large"
27
+ class="text-none mr-3 screen-btn-primary"
28
+ variant="flat"
31
29
  @click="onCreateUnit()"
32
30
  >
33
31
  Add Unit
@@ -37,10 +35,8 @@
37
35
  <template #activator="{ props: t }">
38
36
  <v-btn
39
37
  v-bind="t"
40
- class="ma-1"
41
- variant="tonal"
42
- size="large"
43
- rounded="pill"
38
+ class="ma-1 screen-icon-btn"
39
+ variant="flat"
44
40
  href="/files/unit-sample-data.xlsx"
45
41
  target="_blank"
46
42
  download
@@ -55,10 +51,8 @@
55
51
  <template #activator="{ props: t }">
56
52
  <v-btn
57
53
  v-bind="t"
58
- class="ma-1"
59
- variant="tonal"
60
- size="large"
61
- rounded="pill"
54
+ class="ma-1 screen-icon-btn"
55
+ variant="flat"
62
56
  @click="onImportData"
63
57
  >
64
58
  <v-icon>mdi-upload</v-icon>
@@ -71,10 +65,8 @@
71
65
  <template #activator="{ props: t }">
72
66
  <v-btn
73
67
  v-bind="t"
74
- class="ma-1"
75
- variant="tonal"
76
- size="large"
77
- rounded="pill"
68
+ class="ma-1 screen-icon-btn"
69
+ variant="flat"
78
70
  @click="handleDownloadExcel"
79
71
  :loading="isDownloading"
80
72
  >
@@ -88,7 +80,7 @@
88
80
 
89
81
  <v-spacer />
90
82
 
91
- <v-col cols="auto">
83
+ <v-col cols="auto" class="filter-field">
92
84
  <v-text-field
93
85
  v-model="searchInput"
94
86
  density="compact"
@@ -1,17 +1,28 @@
1
1
  <template>
2
2
  <v-row no-gutters>
3
+ <!--
4
+ The design's title row: page name left, primary action right. Was a
5
+ `tonal` pill drawn by `TableMain` on its own row under the (absent)
6
+ heading. Same handler, same permission gate.
7
+ -->
8
+ <v-col cols="12">
9
+ <PageHeader title="Vehicle Mgmt">
10
+ <template #actions>
11
+ <AppButton v-if="canCreateVehicle" icon="mdi-plus" @click="handleAddVehicleClick">
12
+ Add Vehicle
13
+ </AppButton>
14
+ </template>
15
+ </PageHeader>
16
+ </v-col>
17
+
3
18
  <TableMain :headers="headers" :items="items" :loading="loading || getVehiclesPending" :page="page" :pages="pages"
4
- :pageRange="pageRange" :extension-height="70" :canCreate="canCreateVehicle" @refresh="getVehiclesRefresh"
5
- createLabel="Add Vehicle" show-header @row-click="handleRowClick" @create="handleAddVehicleClick"
19
+ :pageRange="pageRange" @refresh="getVehiclesRefresh"
20
+ show-header @row-click="handleRowClick"
6
21
  @update:page="handleUpdatePage">
7
22
 
8
- <template #extension>
9
- <v-row no-gutters class="px-5 py-1 d-flex align-center justify-end ga-3">
10
- <v-text-field v-model="searchInput" density="compact" placeholder="Search" clearable max-width="300"
11
- append-inner-icon="mdi-magnify" hide-details />
12
- <!-- <v-select v-model="vehicleTypeFilter" density="compact" item-title="label" item-value="value"
13
- placeholder="Filter by Type" clearable max-width="200" hide-details :items="typeOptions" /> -->
14
- </v-row>
23
+ <!-- Search joins the toolbar row rather than stacking a second one. -->
24
+ <template #prepend-additional>
25
+ <AppField v-model="searchInput" search compact placeholder="Search" klass="app-toolbar__search" />
15
26
  </template>
16
27
 
17
28
  <template #item.block="{ value }">