@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
@@ -21,7 +21,7 @@
21
21
  <template #extension>
22
22
  <v-row no-gutters class="w-100 d-flex flex-column">
23
23
  <v-card
24
- class="w-100 px-3 d-flex align-center ga-5 py-2"
24
+ class="w-100 filter-bar d-flex align-center ga-5"
25
25
  flat
26
26
  :height="60"
27
27
  >
@@ -68,20 +68,18 @@
68
68
  <v-spacer />
69
69
 
70
70
  <v-col cols="auto">
71
- <v-chip
71
+ <StatusChip
72
72
  v-if="isScheduleClosed"
73
- color="error"
74
- variant="tonal"
73
+ status="Closed"
75
74
  class="text-capitalize mr-2"
76
75
  >
77
- <v-icon start>mdi-lock</v-icon>
76
+ <v-icon size="14">mdi-lock</v-icon>
78
77
  Closed – View Only
79
- </v-chip>
78
+ </StatusChip>
80
79
  <v-btn
81
80
  v-if="canViewHistory"
82
- class="text-none mr-2"
83
- size="large"
84
- color="primary"
81
+ class="text-none mr-2 screen-btn-primary"
82
+ variant="flat"
85
83
  aria-label="History"
86
84
  @click="openHistory"
87
85
  >
@@ -89,9 +87,8 @@
89
87
  </v-btn>
90
88
  <v-btn
91
89
  v-if="canGenerateChecklist"
92
- class="text-none"
93
- size="large"
94
- color="primary"
90
+ class="text-none screen-btn-primary"
91
+ variant="flat"
95
92
  aria-label="Generate"
96
93
  @click="_generateChecklist"
97
94
  :loading="submitting"
@@ -114,20 +111,13 @@
114
111
  </span>
115
112
  </div>
116
113
  <div v-else-if="isScheduleClosed">
117
- <v-chip
118
- size="small"
119
- color="error"
120
- variant="tonal"
121
- class="text-capitalize"
122
- >Closed</v-chip
123
- >
114
+ <StatusChip status="Closed" class="text-capitalize" />
124
115
  </div>
125
116
  <div v-else>
126
117
  <v-btn
127
118
  v-if="canManageScheduleTasks"
128
119
  variant="outlined"
129
- size="medium"
130
- class="text-capitalize px-4 py-2"
120
+ class="text-capitalize screen-btn-ghost"
131
121
  @click.stop="() => _acceptAreaChecklist(item._id)"
132
122
  >
133
123
  Accept
@@ -146,14 +136,11 @@
146
136
  </template>
147
137
 
148
138
  <template #item.status="{ value }">
149
- <v-chip
139
+ <StatusChip
150
140
  class="text-capitalize"
151
- :color="getStatusColor(value)"
152
- variant="flat"
153
- pill
154
- >
155
- {{ value || "No Status" }}
156
- </v-chip>
141
+ :status="value"
142
+ :label="value || 'No Status'"
143
+ />
157
144
  </template>
158
145
  </TableMain></v-col
159
146
  >
@@ -262,27 +249,6 @@ watchEffect(() => {
262
249
  }
263
250
  });
264
251
 
265
- const getStatusColor = (status: unknown): string => {
266
- if (!status) return "grey";
267
-
268
- const normalized = String(status).toLowerCase();
269
-
270
- switch (normalized) {
271
- case "open":
272
- return "grey";
273
- case "ongoing":
274
- return "primary";
275
- case "completed":
276
- case "accepted":
277
- return "success";
278
- case "closed":
279
- case "rejected":
280
- return "error";
281
- default:
282
- return "secondary";
283
- }
284
- };
285
-
286
252
  function showMessage(msg: string, color: string = "error") {
287
253
  message.value = msg;
288
254
  messageColor.value = color;
@@ -21,10 +21,8 @@
21
21
  <v-row no-gutters align="center" class="w-100">
22
22
  <v-col cols="auto" v-if="canCreateScheduleTask">
23
23
  <v-btn
24
- class="text-none"
25
- rounded="pill"
26
- variant="tonal"
27
- size="large"
24
+ class="text-none screen-btn-primary"
25
+ variant="flat"
28
26
  @click="openFormDialog"
29
27
  >
30
28
  Create Schedule Task
@@ -33,7 +31,7 @@
33
31
 
34
32
  <v-spacer />
35
33
 
36
- <v-col cols="auto">
34
+ <v-col cols="auto" class="filter-field">
37
35
  <v-text-field
38
36
  v-model="searchInput"
39
37
  density="compact"
@@ -49,28 +47,31 @@
49
47
 
50
48
  <template #item.areas="{ value }">
51
49
  <div v-if="value && value.length > 0">
52
- <v-chip
50
+ <StatusChip
53
51
  v-for="(area, idx) in value.slice(0, 2)"
54
52
  :key="idx"
55
- size="small"
56
53
  class="mr-1 mb-1"
57
- >
58
- {{ area.name }}
59
- </v-chip>
60
- <v-chip v-if="value.length > 2" size="small" class="mb-1">
61
- +{{ value.length - 2 }} more
62
- </v-chip>
54
+ :dot="false"
55
+ tone="neutral"
56
+ :label="area.name"
57
+ />
58
+ <StatusChip
59
+ v-if="value.length > 2"
60
+ class="mb-1"
61
+ :dot="false"
62
+ tone="neutral"
63
+ :label="`+${value.length - 2} more`"
64
+ />
63
65
  </div>
64
66
  <span v-else>N/A</span>
65
67
  </template>
66
68
 
67
69
  <template #item.status="{ value }">
68
- <v-chip
69
- :color="value === 'active' ? 'success' : 'default'"
70
+ <StatusChip
70
71
  class="text-capitalize"
71
- >
72
- {{ value || "N/A" }}
73
- </v-chip>
72
+ :status="value"
73
+ :label="value || 'N/A'"
74
+ />
74
75
  </template>
75
76
  </TableMain>
76
77
 
@@ -270,15 +271,11 @@
270
271
  Status:
271
272
  </v-col>
272
273
  <v-col cols="7" class="text-subtitle-2">
273
- <v-chip
274
- :color="
275
- selectedTask.status === 'active' ? 'success' : 'default'
276
- "
277
- size="small"
274
+ <StatusChip
278
275
  class="text-capitalize"
279
- >
280
- {{ selectedTask.status || "N/A" }}
281
- </v-chip>
276
+ :status="selectedTask.status"
277
+ :label="selectedTask.status || 'N/A'"
278
+ />
282
279
  </v-col>
283
280
  </v-row>
284
281
 
@@ -32,13 +32,18 @@
32
32
  </template>
33
33
 
34
34
  <template #item.createdBy="{ item }">
35
- <v-avatar
36
- size="32"
37
- class="mr-2"
38
- :color="getAvatarColor(item.createdBy)"
39
- >
40
- <span class="white--text">{{ getInitials(item.createdBy) }}</span>
41
- </v-avatar>
35
+ <!--
36
+ THE 3.12:1 `HS` CHIP.
37
+
38
+ This was a hand-rolled copy of `AvatarMain` with two problems: its
39
+ fill was Vuetify's MATERIAL `blue`/`purple`/`grey` (not a theme
40
+ token, so no `on-*` pair applied to it) and its `white--text` is
41
+ Vuetify 2 syntax that does nothing in Vuetify 3 - leaving Vuetify to
42
+ derive WHITE, which reads 3.12:1 on that blue in BOTH themes.
43
+ `AvatarMain` draws the same circle with the same initials and now
44
+ guarantees its label clears 4.5:1.
45
+ -->
46
+ <AvatarMain :name="item.createdBy" :size="32" class="mr-2" />
42
47
  {{ item.createdBy || "N/A" }}
43
48
  </template>
44
49
 
@@ -47,13 +52,7 @@
47
52
  </template>
48
53
 
49
54
  <template #item.status="{ value }">
50
- <v-chip
51
- :color="getStatusColor(value)"
52
- class="font-weight-bold"
53
- style="background: #ffe6c2; color: #ff9800"
54
- >
55
- {{ value || "To-Do" }}
56
- </v-chip>
55
+ <StatusChip :status="value || 'To-Do'" :label="value || 'To-Do'" />
57
56
  </template>
58
57
  <!-- Completion Date/Time -->
59
58
  <template #item.completionDate="{ value }">
@@ -61,7 +60,11 @@
61
60
  </template>
62
61
  <!-- Action (Accept button) -->
63
62
  <template #item.action="{ item }">
64
- <v-btn outlined class="accept-btn" @click.stop="acceptTask(item)">
63
+ <v-btn
64
+ variant="outlined"
65
+ class="text-none screen-btn-ghost"
66
+ @click.stop="acceptTask(item)"
67
+ >
65
68
  Accept
66
69
  </v-btn>
67
70
  </template>
@@ -133,24 +136,6 @@ const headers = [
133
136
 
134
137
  const { formatDate } = useUtils();
135
138
 
136
- function getInitials(name: string) {
137
- if (!name) return "";
138
- return name
139
- .split(" ")
140
- .map((n) => n[0])
141
- .join("")
142
- .toUpperCase();
143
- }
144
- function getAvatarColor(name: string) {
145
- if (name === "Demo Smith") return "purple";
146
- if (name === "Hygiene SP") return "blue";
147
- return "grey";
148
- }
149
- function getStatusColor(status: string) {
150
- if (status === "To-Do") return "orange";
151
- if (status === "Completed") return "green";
152
- return "grey";
153
- }
154
139
  function acceptTask(item: any) {
155
140
  selectedTask.value = item;
156
141
  showAcceptDialog.value = true;
@@ -170,13 +155,3 @@ function onRowClick(data: any) {
170
155
  }
171
156
  }
172
157
  </script>
173
-
174
- <style scoped>
175
- .accept-btn {
176
- border: 1px solid #333;
177
- background: #fff;
178
- border-radius: 4px;
179
- cursor: pointer;
180
- min-width: 80px;
181
- }
182
- </style>
@@ -1,115 +1,91 @@
1
1
  <template>
2
- <v-row no-gutters>
3
- <v-col cols="12" class="mb-3">
4
- <v-row no-gutters align="center" justify="space-between">
5
- <div class="d-flex flex-wrap align-center">
6
- <v-btn
7
- v-if="showAddButton && canAddServiceProvider"
8
- class="text-none mr-2"
9
- rounded="pill"
10
- variant="tonal"
11
- size="large"
12
- @click="(createDialog = true), setServiceProvider({ mode: 'add' })"
13
- >
14
- Add
15
- </v-btn>
16
- <v-btn
17
- v-if="canInviteServiceProvider"
18
- class="text-none mr-2"
19
- rounded="pill"
20
- variant="tonal"
21
- size="large"
22
- @click="
23
- (createInviteDialog = true),
24
- setServiceProvider({ mode: 'invite' })
25
- "
26
- >
27
- Invite
28
- </v-btn>
29
- <v-btn
30
- v-if="showBillingButton"
31
- class="text-none mr-2"
32
- rounded="pill"
33
- variant="tonal"
34
- size="large"
35
- @click="
36
- useRouter().push({
37
- name: 'org-site-service-provider-mgmt-billing',
38
- })
39
- "
40
- >
41
- Billing
42
- </v-btn>
43
- </div>
44
-
45
- <v-text-field
46
- v-model="searchText"
47
- placeholder="Search"
48
- variant="outlined"
49
- density="comfortable"
50
- clearable
51
- hide-details
52
- prepend-inner-icon="mdi-magnify"
53
- class="ml-2"
54
- style="max-width: 280px"
55
- />
56
- </v-row>
57
- </v-col>
58
-
59
- <v-col cols="12">
60
- <v-card
61
- width="100%"
62
- variant="outlined"
63
- border="thin"
64
- rounded="lg"
65
- :loading="loading"
66
- >
67
- <div class="sp-table-header">
68
- <!-- No fixed colour: `grey-lighten-4` is a light-mode grey in both
69
- themes, which is what made this strip a white slab in dark
70
- mode. `transparent` lets the themed .sp-table-header show. -->
71
- <v-toolbar
72
- density="compact"
73
- color="transparent"
74
- flat
75
- class="sp-toolbar-top px-2"
76
- >
77
- <v-btn
78
- icon
79
- variant="text"
80
- density="comfortable"
81
- @click="loadList()"
82
- >
83
- <v-icon>mdi-refresh</v-icon>
84
- </v-btn>
85
-
86
- <v-spacer />
87
-
88
- <template v-if="canViewServiceProviders">
89
- <span class="mr-2 text-caption text-medium-emphasis">{{
90
- pageRange
91
- }}</span>
92
- <local-pagination
93
- v-model="page"
94
- :length="pages"
95
- @update:value="loadList()"
96
- />
97
- </template>
98
- </v-toolbar>
99
-
100
- <v-tabs
101
- v-model="listTab"
102
- color="primary"
103
- density="comfortable"
104
- class="sp-tabs"
105
- align-tabs="start"
2
+ <div>
3
+ <!--
4
+ THE DESIGN'S PAGE SCAFFOLD (same shape as Members).
5
+
6
+ The screen had no title at all, its primary actions sat top-LEFT, the
7
+ search box floated outside the card, and the refresh + pager sat on one
8
+ row with the tabs stacked on a second row below. The design draws a 22px
9
+ title with the actions on the right of that line, then ONE row inside the
10
+ card: tabs left, search / refresh / count / pager right.
11
+
12
+ Placement only. The same buttons open the same dialogs under the same
13
+ permission conditions, the same three tabs load the same three lists, the
14
+ same search feeds the same debounced `loadList()`, and the table, its
15
+ columns and its row menu are untouched.
16
+ -->
17
+ <PageHeader title="Service Providers">
18
+ <template #actions>
19
+ <AppButton
20
+ v-if="showBillingButton"
21
+ variant="ghost"
22
+ @click="
23
+ useRouter().push({
24
+ name: 'org-site-service-provider-mgmt-billing',
25
+ })
26
+ "
27
+ >
28
+ Billing
29
+ </AppButton>
30
+ <AppButton
31
+ v-if="showAddButton && canAddServiceProvider"
32
+ variant="ghost"
33
+ @click="(createDialog = true), setServiceProvider({ mode: 'add' })"
34
+ >
35
+ Add
36
+ </AppButton>
37
+ <AppButton
38
+ v-if="canInviteServiceProvider"
39
+ @click="
40
+ (createInviteDialog = true), setServiceProvider({ mode: 'invite' })
41
+ "
42
+ >
43
+ Invite
44
+ </AppButton>
45
+ </template>
46
+ </PageHeader>
47
+
48
+ <AppCard class="table-card">
49
+ <CardToolbar :count="canViewServiceProviders ? pageRange : ''">
50
+ <template #tabs>
51
+ <button
52
+ v-for="tab in listTabs"
53
+ :key="tab.value"
54
+ type="button"
55
+ role="tab"
56
+ class="app-tab"
57
+ :class="{ 'app-tab--active': listTab === tab.value }"
58
+ :aria-selected="listTab === tab.value"
59
+ @click="listTab = tab.value"
106
60
  >
107
- <v-tab value="active" class="text-none">Service Providers</v-tab>
108
- <v-tab value="pending" class="text-none">Pending</v-tab>
109
- <v-tab value="inactive" class="text-none">In-Active</v-tab>
110
- </v-tabs>
111
- </div>
112
-
61
+ {{ tab.label }}
62
+ </button>
63
+ </template>
64
+
65
+ <template #right>
66
+ <AppField
67
+ v-model="searchText"
68
+ search
69
+ compact
70
+ placeholder="Search"
71
+ klass="app-toolbar__search"
72
+ />
73
+ <AppButton
74
+ variant="icon"
75
+ icon="mdi-refresh"
76
+ aria-label="Refresh"
77
+ @click="loadList()"
78
+ />
79
+ <local-pagination
80
+ v-if="canViewServiceProviders"
81
+ v-model="page"
82
+ :length="pages"
83
+ @update:value="loadList()"
84
+ />
85
+ </template>
86
+ </CardToolbar>
87
+
88
+ <div class="app-table-scroll">
113
89
  <v-data-table
114
90
  v-if="canViewServiceProviders"
115
91
  :headers="headers"
@@ -118,6 +94,7 @@
118
94
  :items-per-page="pageSize"
119
95
  fixed-header
120
96
  hide-default-footer
97
+ :loading="loading"
121
98
  class="sp-table"
122
99
  style="max-height: calc(100vh - 280px)"
123
100
  >
@@ -147,14 +124,11 @@
147
124
  4.5:1 a 12px label needs. Filled puts the colour in the
148
125
  background and lets Vuetify pick a readable foreground for it.
149
126
  -->
150
- <v-chip
151
- :color="statusChipColor(item.status, item.statusRaw)"
152
- size="small"
153
- :variant="item.source === 'invite' ? 'flat' : 'tonal'"
127
+ <StatusChip
154
128
  class="text-none"
155
- >
156
- {{ item.status }}
157
- </v-chip>
129
+ :status="item.statusRaw || item.status"
130
+ :label="item.status"
131
+ />
158
132
  <div
159
133
  v-if="item.rejectionReason"
160
134
  class="text-caption text-medium-emphasis mt-1 sp-reject-reason"
@@ -232,12 +206,16 @@
232
206
  </v-menu>
233
207
  </template>
234
208
  </v-data-table>
235
- <v-card-text v-else class="text-body-2 text-medium-emphasis">
236
- You do not have permission to view service providers.
237
- </v-card-text>
238
- </v-card>
239
- </v-col>
240
- </v-row>
209
+ </div>
210
+
211
+ <div
212
+ v-if="!canViewServiceProviders"
213
+ class="pa-4 text-body-2 text-medium-emphasis"
214
+ >
215
+ You do not have permission to view service providers.
216
+ </div>
217
+ </AppCard>
218
+ </div>
241
219
 
242
220
  <v-dialog v-model="viewDialog" max-width="514">
243
221
  <v-card rounded="lg" class="sp-members-card">
@@ -520,7 +498,7 @@
520
498
 
521
499
  <v-dialog v-model="createInviteDialog" width="580" persistent>
522
500
  <v-card width="100%" rounded="lg" class="sp-invite-card">
523
- <v-toolbar flat color="grey-lighten-4" height="76">
501
+ <v-toolbar flat color="transparent" class="table-card__toolbar" height="76">
524
502
  <v-row no-gutters class="fill-height px-6" align="center">
525
503
  <span class="font-weight-bold text-h5">
526
504
  Invite Service Provider
@@ -801,6 +779,12 @@ const { requiredRule, emailRule, debounce } = useUtils();
801
779
  const { getUserByEmail } = useUser();
802
780
 
803
781
  const listTab = ref<ListTab>("active");
782
+ /** The same three tabs, in the same order, with the same labels. */
783
+ const listTabs: Array<{ value: ListTab; label: string }> = [
784
+ { value: "active", label: "Service Providers" },
785
+ { value: "pending", label: "Pending" },
786
+ { value: "inactive", label: "In-Active" },
787
+ ];
804
788
  const page = ref(1);
805
789
  const pages = ref(0);
806
790
  const pageRange = ref("-- - -- of --");
@@ -1147,24 +1131,9 @@ function statusDisplay(raw: string, source: TableRow["source"]) {
1147
1131
  return raw.charAt(0).toUpperCase() + raw.slice(1).toLowerCase();
1148
1132
  }
1149
1133
 
1150
- const INVITE_STATUS_COLOURS: Record<string, string> = {
1151
- "awaiting-approval": "warning",
1152
- pending: "info",
1153
- complete: "success",
1154
- rejected: "error",
1155
- declined: "default",
1156
- cancelled: "default",
1157
- expired: "default",
1158
- };
1159
-
1160
- function statusChipColor(statusLabel: string, statusRaw?: string) {
1161
- if (statusRaw) return INVITE_STATUS_COLOURS[statusRaw] ?? "primary";
1162
- const s = statusLabel.toLowerCase();
1163
- if (s === "active") return "success";
1164
- if (s === "pending") return "warning";
1165
- if (s === "in-active" || s === "inactive") return "default";
1166
- return "primary";
1167
- }
1134
+ /* The status chips read their tone from the shared mapping in
1135
+ `utils/status.ts` now - which carries every raw invitation status this map
1136
+ carried, with the same tone. */
1168
1137
 
1169
1138
  // async function loadInviteRoles() {
1170
1139
  // loadingInviteRoles.value = true;
@@ -1514,32 +1483,8 @@ async function submitServiceProviderInvite() {
1514
1483
  </script>
1515
1484
 
1516
1485
  <style scoped>
1517
- .sp-toolbar-top {
1518
- display: flex;
1519
- align-items: center;
1520
- flex-wrap: nowrap;
1521
- gap: 4px;
1522
- }
1523
-
1524
- .sp-table-header {
1525
- /* Was a hardcoded #f5f5f5 with a black border: in dark mode the tab strip
1526
- became a white slab and the tab labels and the page range disappeared
1527
- into it. These take the colour of whichever theme they are in. */
1528
- background: rgb(var(--v-theme-surface-light, var(--v-theme-surface)));
1529
- border-bottom: 1px solid
1530
- rgba(var(--v-border-color), var(--v-border-opacity));
1531
- }
1532
-
1533
- .sp-tabs {
1534
- width: 100%;
1535
- min-height: 40px;
1536
- }
1537
-
1538
- .sp-tabs :deep(.v-tab) {
1539
- min-width: 120px;
1540
- justify-content: flex-start;
1541
- text-transform: none;
1542
- }
1486
+ /* The tab strip's own theming went with the strip: the tabs are the card's
1487
+ first row now and take `.app-tab` from the shared primitives. */
1543
1488
 
1544
1489
  .sp-col-company {
1545
1490
  max-width: 220px;