@7365admin1/layer-common 3.2.1 → 3.2.2-staging.101
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.
- package/.changeset/camera-wall-gated-endpoint.md +47 -0
- package/.changeset/camera-wall-plain-english.md +60 -0
- package/.changeset/camera-wall-selection-and-guidance.md +65 -0
- package/.changeset/dark-primary-contrast.md +39 -0
- package/.changeset/dashboard-dark-theme-contrast.md +15 -0
- package/.changeset/notification-preferences.md +17 -0
- package/.changeset/pm-sidebar-grouping-and-list-scaffold.md +27 -0
- package/.changeset/service-provider-invitation-actions.md +28 -0
- package/assets/css/primitives.css +680 -0
- package/assets/css/screens.css +541 -0
- package/assets/css/shell.css +258 -0
- package/assets/css/tokens.css +220 -0
- package/components/AccessCardQrTagging.vue +1 -1
- package/components/AccessManagement.vue +53 -72
- package/components/AppButton.vue +52 -0
- package/components/AppCard.vue +20 -0
- package/components/AppDateField.vue +35 -0
- package/components/AppField.vue +51 -0
- package/components/AppKpiTile.vue +65 -0
- package/components/AppSegmented.vue +38 -0
- package/components/AppSelect.vue +203 -0
- package/components/AreaChecklistHistoryLogs.vue +51 -44
- package/components/AreaChecklistHistoryMain.vue +6 -2
- package/components/AreaMain.vue +9 -17
- package/components/AttendanceMain.vue +4 -3
- package/components/Avatar/Main.vue +54 -2
- package/components/BillingMain.vue +26 -30
- package/components/BuildingManagement/buildings.vue +27 -38
- package/components/BuildingManagement/units.vue +26 -42
- package/components/BulletinBoardManagement.vue +23 -10
- package/components/CameraWall.vue +1034 -0
- package/components/CameraWallTile.vue +818 -0
- package/components/CardToolbar.vue +37 -0
- package/components/CleaningScheduleMain.vue +29 -46
- package/components/ClientMain.vue +90 -78
- package/components/DashboardEmptyState.vue +11 -1
- package/components/DashboardMain.vue +349 -275
- package/components/DashboardPanel.vue +1 -1
- package/components/DocumentManagement.vue +129 -93
- package/components/EntryPassInformation.vue +4 -4
- package/components/EntryPassMain.vue +49 -27
- package/components/EquipmentItemMain.vue +4 -6
- package/components/EquipmentManagementMain.vue +9 -7
- package/components/FeedbackMain.vue +42 -56
- package/components/FormDialog.vue +60 -21
- package/components/HidAccessLogDashboard.vue +119 -44
- package/components/HidCameraCaptureDialog.vue +199 -0
- package/components/HidIntercomManagement.vue +825 -202
- package/components/HidQrCodeConfiguration.vue +927 -93
- package/components/HidServiceSettingsPanel.vue +20 -1
- package/components/HidUserEnrollment.vue +454 -106
- package/components/InvitationMain.vue +46 -61
- package/components/Layout/Header.vue +289 -37
- package/components/Layout/NavigationDrawer.vue +340 -30
- package/components/ManageChecklistMain.vue +17 -17
- package/components/MemberMain.vue +79 -60
- package/components/MyAttendanceMain.vue +3 -5
- package/components/NavigationItem.vue +9 -0
- package/components/Nfc/NFCPatrolRouteMain.vue +116 -103
- package/components/NotificationSettings.vue +361 -0
- package/components/OnlineFormConfigurationForm.vue +620 -224
- package/components/OnlineFormFill.vue +36 -8
- package/components/OnlineFormsConfiguration.vue +7 -3
- package/components/PageHeader.vue +43 -0
- package/components/PassInformation.vue +104 -45
- package/components/QrTemplate/CreditCardLandscape.vue +19 -12
- package/components/QrTemplate/PrintDialog.vue +209 -196
- package/components/RolePermissionMain.vue +184 -91
- package/components/ScanVisitorQRCode.vue +35 -7
- package/components/ScheduleAreaMain.vue +15 -49
- package/components/ScheduleTaskMain.vue +23 -26
- package/components/ScheduleTastTicketMain.vue +18 -43
- package/components/ServiceProviderInvitationPrompt.vue +150 -0
- package/components/ServiceProviderMain.vue +346 -157
- package/components/SiteSettings.vue +25 -10
- package/components/SiteSettingsWorkOrder.vue +61 -30
- package/components/SpecificAttr.vue +10 -2
- package/components/StatusChip.vue +93 -0
- package/components/StockCard.vue +17 -12
- package/components/SwitchContext.vue +33 -1
- package/components/TableHygiene.vue +29 -46
- package/components/TableList.vue +1 -1
- package/components/TableListSecondary.vue +1 -1
- package/components/TableMain.vue +161 -77
- package/components/TableV3.vue +61 -66
- package/components/UnitMain.vue +9 -17
- package/components/VehicleManagement.vue +20 -9
- package/components/VisitorForm.vue +294 -78
- package/components/VisitorManagement.vue +221 -71
- package/components/VisitorSocketPopUp.vue +56 -2
- package/components/WorkOrder/Main.vue +42 -51
- package/composables/useComment.ts +3 -3
- package/composables/useHidAmico.ts +151 -0
- package/composables/useHidNavigation.ts +0 -7
- package/composables/useLocalAuth.ts +9 -36
- package/composables/useNotificationPreference.ts +87 -0
- package/composables/useOnlineFormPages.ts +2 -0
- package/composables/useOrg.ts +1 -1
- package/composables/useRole.ts +22 -0
- package/composables/useServiceProviderInvitation.ts +145 -0
- package/composables/useSipWebPhone.ts +336 -0
- package/composables/useSiteSettings.ts +50 -0
- package/composables/useThemePreference.ts +63 -0
- package/composables/useVisitor.ts +6 -1
- package/composables/useVisitorSocket.ts +26 -0
- package/composables/useWebUsb.ts +127 -37
- package/nuxt.config.ts +31 -0
- package/package.json +4 -1
- package/pages/[org]/[site]/access-mgmt/intercom/index.vue +1 -1
- package/pages/notification-settings.vue +19 -0
- package/plugins/secure-member.client.ts +21 -56
- package/plugins/vuetify.ts +68 -9
- package/test/visitor-socket.test.mjs +36 -0
- package/types/org.d.ts +2 -0
- package/types/site.d.ts +71 -0
- package/utils/camera-wall.test.ts +571 -0
- package/utils/camera-wall.ts +926 -0
- package/utils/nav-sections.test.ts +190 -0
- package/utils/nav-sections.ts +151 -0
- package/utils/status.test.ts +77 -0
- package/utils/status.ts +90 -0
- package/utils/theme.test.ts +534 -0
- package/utils/theme.ts +294 -0
- package/components/HidIdentityMapping.vue +0 -975
- package/middleware/member.ts +0 -4
- package/pages/[org]/[site]/access-mgmt/identity-mapping/index.vue +0 -23
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<v-avatar :color="getColor(name || id)" :size="size">
|
|
3
|
-
|
|
3
|
+
<!--
|
|
4
|
+
A stored profile photo that 404s left the circle EMPTY - `v-img`
|
|
5
|
+
draws nothing when the source fails, and there was no fallback under
|
|
6
|
+
it. The initials the avatar already computes stand in, so the circle
|
|
7
|
+
is never blank.
|
|
8
|
+
-->
|
|
9
|
+
<v-img v-if="imageSrc" :alt="name" :src="imageUrl">
|
|
10
|
+
<template #error>
|
|
11
|
+
<div class="d-flex align-center justify-center fill-height">
|
|
12
|
+
<span :style="{ fontSize: `${fontSize}px` }">{{ initials }}</span>
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
15
|
+
</v-img>
|
|
4
16
|
<span v-else :style="{
|
|
5
17
|
fontSize: `${fontSize}px`,
|
|
6
18
|
lineHeight: `${size}px`
|
|
@@ -43,6 +55,39 @@ const initials = computed(() => {
|
|
|
43
55
|
|
|
44
56
|
const fontSize = computed(() => Math.round(props.size * 0.5))
|
|
45
57
|
|
|
58
|
+
/**
|
|
59
|
+
* WHITE INITIALS ON A HASHED COLOUR - THE ESTATE'S WORST CONTRAST DEFECT.
|
|
60
|
+
*
|
|
61
|
+
* The circle's fill is derived from the name, and Vuetify then picks a WHITE
|
|
62
|
+
* label for it. Measured across the whole space this generator can produce
|
|
63
|
+
* (360 hues x 20 saturations x 15 lightnesses): **70.6% of names fail AA**,
|
|
64
|
+
* and the worst - a yellow, `hsl(60,79%,59%)` - reads **1.30:1**. The 3.12:1
|
|
65
|
+
* seen on one screen was a mild sample, not the floor.
|
|
66
|
+
*
|
|
67
|
+
* The name's colour is its identity, so the HUE and SATURATION are kept
|
|
68
|
+
* exactly; only the lightness is walked down until white clears 4.5:1 on it.
|
|
69
|
+
* A yellow therefore darkens a long way and a blue barely moves, which is what
|
|
70
|
+
* makes a single fixed lightness impossible here.
|
|
71
|
+
*/
|
|
72
|
+
const AA = 4.5
|
|
73
|
+
|
|
74
|
+
const srgb = (c: number) => {
|
|
75
|
+
const v = c / 255
|
|
76
|
+
return v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Relative luminance of an `hsl()` triple, without going through the DOM. */
|
|
80
|
+
function luminance(h: number, s: number, l: number): number {
|
|
81
|
+
const sat = s / 100
|
|
82
|
+
const light = l / 100
|
|
83
|
+
const a = sat * Math.min(light, 1 - light)
|
|
84
|
+
const f = (n: number) => {
|
|
85
|
+
const k = (n + h / 30) % 12
|
|
86
|
+
return light - a * Math.max(-1, Math.min(k - 3, Math.min(9 - k, 1)))
|
|
87
|
+
}
|
|
88
|
+
return 0.2126 * srgb(f(0) * 255) + 0.7152 * srgb(f(8) * 255) + 0.0722 * srgb(f(4) * 255)
|
|
89
|
+
}
|
|
90
|
+
|
|
46
91
|
function getColor(str?: string): string {
|
|
47
92
|
if(!str) return "grey"
|
|
48
93
|
|
|
@@ -54,7 +99,14 @@ function getColor(str?: string): string {
|
|
|
54
99
|
|
|
55
100
|
const hue = Math.abs(hash) % 360
|
|
56
101
|
const saturation = 60 + (Math.abs((hash >> 3) % 20)) // 60–80%
|
|
57
|
-
|
|
102
|
+
let lightness = 45 + (Math.abs((hash >> 5) % 15)) // 45–60%
|
|
103
|
+
|
|
104
|
+
// ponytail: linear 1% walk, not a solve - the range is at most 45 steps and
|
|
105
|
+
// this runs once per avatar. Lightness bottoms out at 0, where every hue is
|
|
106
|
+
// black and white is 21:1, so the loop always terminates on a pass.
|
|
107
|
+
while (lightness > 0 && 1.05 / (luminance(hue, saturation, lightness) + 0.05) < AA) {
|
|
108
|
+
lightness -= 1
|
|
109
|
+
}
|
|
58
110
|
|
|
59
111
|
return `hsl(${hue}, ${saturation}%, ${lightness}%)`
|
|
60
112
|
}
|
|
@@ -1,30 +1,27 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
</v-row>
|
|
24
|
-
</template>
|
|
25
|
-
</v-toolbar>
|
|
2
|
+
<div>
|
|
3
|
+
<!--
|
|
4
|
+
THE DESIGN'S PAGE SCAFFOLD (same shape as Service Providers).
|
|
5
|
+
|
|
6
|
+
Placement only: the same refresh control, the same range text, the same
|
|
7
|
+
pager bound to the same `page`, and the table, its columns and its cell
|
|
8
|
+
templates are untouched. The screen simply gains the page title it has
|
|
9
|
+
never had, and its card + toolbar are the design's rather than Vuetify's.
|
|
10
|
+
-->
|
|
11
|
+
<PageHeader title="Billing" />
|
|
12
|
+
|
|
13
|
+
<AppCard class="table-card">
|
|
14
|
+
<CardToolbar :count="pageRange">
|
|
15
|
+
<template #left>
|
|
16
|
+
<AppButton variant="icon" icon="mdi-refresh" aria-label="Refresh" />
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<template #right>
|
|
20
|
+
<local-pagination v-model="page" :length="pages" />
|
|
21
|
+
</template>
|
|
22
|
+
</CardToolbar>
|
|
26
23
|
|
|
27
|
-
|
|
24
|
+
<v-data-table
|
|
28
25
|
:headers="headers"
|
|
29
26
|
:items="items"
|
|
30
27
|
item-value="_id"
|
|
@@ -38,7 +35,7 @@
|
|
|
38
35
|
<span class="text-caption font-weight-bold text-capitalize">
|
|
39
36
|
permissions
|
|
40
37
|
</span>
|
|
41
|
-
<
|
|
38
|
+
<StatusChip :dot="false" tone="neutral" :label="`${value.length}`" />
|
|
42
39
|
</template>
|
|
43
40
|
|
|
44
41
|
<template #item.nature="{ item }">
|
|
@@ -46,10 +43,9 @@
|
|
|
46
43
|
{{ item.nature }}
|
|
47
44
|
</span>
|
|
48
45
|
</template>
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
</v-row>
|
|
46
|
+
</v-data-table>
|
|
47
|
+
</AppCard>
|
|
48
|
+
</div>
|
|
53
49
|
</template>
|
|
54
50
|
|
|
55
51
|
<script setup lang="ts">
|
|
@@ -1,46 +1,35 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<v-row no-gutters>
|
|
3
|
-
|
|
4
|
-
<v-row no-gutters>
|
|
5
|
-
<v-btn class="text-none" rounded="pill" variant="tonal" @click="setBuilding()" size="large"
|
|
6
|
-
v-if="canCreate && canCreateBuilding">
|
|
7
|
-
Add Block
|
|
8
|
-
</v-btn>
|
|
9
|
-
</v-row>
|
|
10
|
-
</v-col>
|
|
3
|
+
<!-- Title row; nav calls this screen "Blocks". The Add pill moves onto it. -->
|
|
11
4
|
<v-col cols="12">
|
|
12
|
-
<
|
|
13
|
-
<
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
dense clearable class="mx-2" style="width: 200px;" />
|
|
20
|
-
</template>
|
|
21
|
-
|
|
22
|
-
<template #append>
|
|
23
|
-
<v-row no-gutters justify="end" align="center">
|
|
24
|
-
<span class="mr-2 text-caption text-fontgray">
|
|
25
|
-
{{ pageRange }}
|
|
26
|
-
</span>
|
|
27
|
-
<local-pagination v-model="page" :length="pages" @update:value="getBuildings()" />
|
|
28
|
-
</v-row>
|
|
29
|
-
</template>
|
|
30
|
-
</v-toolbar>
|
|
31
|
-
|
|
32
|
-
<v-data-table :headers="headers" :items="items" item-value="_id" items-per-page="20" fixed-header
|
|
33
|
-
hide-default-footer @click:row="tableRowClickHandler" style="max-height: calc(100vh - (200px))">
|
|
34
|
-
<template #item.createdAt="{ value }">
|
|
35
|
-
{{ new Date(value).toLocaleDateString() }}
|
|
36
|
-
</template>
|
|
37
|
-
<template #item.levels="{ item }">
|
|
38
|
-
{{ item.levels.length }}
|
|
39
|
-
</template>
|
|
40
|
-
</v-data-table>
|
|
41
|
-
</v-card>
|
|
5
|
+
<PageHeader title="Blocks">
|
|
6
|
+
<template #actions>
|
|
7
|
+
<AppButton v-if="canCreate && canCreateBuilding" icon="mdi-plus" @click="setBuilding()">
|
|
8
|
+
Add Block
|
|
9
|
+
</AppButton>
|
|
10
|
+
</template>
|
|
11
|
+
</PageHeader>
|
|
42
12
|
</v-col>
|
|
43
13
|
|
|
14
|
+
<!--
|
|
15
|
+
Was `v-card` + a `grey-lighten-4` `v-toolbar` + a bare `v-data-table`.
|
|
16
|
+
`TableMain` draws the same card, the same refresh/search/page-range/pager
|
|
17
|
+
row and the same table - with the design's uppercase header row.
|
|
18
|
+
-->
|
|
19
|
+
<TableMain :headers="headers" :items="items" :loading="loading" :page="page" :pages="pages"
|
|
20
|
+
:pageRange="pageRange" show-header @refresh="getBuildings()"
|
|
21
|
+
@update:page="page = $event; getBuildings()" @row-click="(data: any) => tableRowClickHandler(null, data)">
|
|
22
|
+
<template #prepend-additional>
|
|
23
|
+
<AppField v-model="searchInput" search compact placeholder="Search" klass="app-toolbar__search" />
|
|
24
|
+
</template>
|
|
25
|
+
<template #item.createdAt="{ value }">
|
|
26
|
+
{{ new Date(value).toLocaleDateString() }}
|
|
27
|
+
</template>
|
|
28
|
+
<template #item.levels="{ item }">
|
|
29
|
+
{{ item.levels.length }}
|
|
30
|
+
</template>
|
|
31
|
+
</TableMain>
|
|
32
|
+
|
|
44
33
|
<!-- Create Dialog -->
|
|
45
34
|
<v-dialog v-model="createDialog" width="450" persistent>
|
|
46
35
|
<BuildingForm :site="site" mode="add" :blocks="items" @cancel="createDialog = false" @success="successCreate()"
|
|
@@ -1,50 +1,34 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<v-row no-gutters>
|
|
3
|
-
|
|
4
|
-
<v-row no-gutters>
|
|
5
|
-
<v-btn class="text-none" rounded="pill" variant="tonal" @click="setBuilding()" size="large"
|
|
6
|
-
v-if="canCreate && canCreateUnit">
|
|
7
|
-
Add Unit
|
|
8
|
-
</v-btn>
|
|
9
|
-
<v-btn class="text-none ml-2" rounded="pill" variant="tonal" @click="openBulkUploadFilePicker()" size="large"
|
|
10
|
-
:loading="bulkUploadLoading" v-if="canCreate && canCreateUnit">
|
|
11
|
-
<v-icon start>mdi-upload</v-icon>
|
|
12
|
-
Bulk Upload
|
|
13
|
-
</v-btn>
|
|
14
|
-
<input ref="bulkUploadFileInput" type="file" accept=".xlsx,.xls,.csv" class="d-none"
|
|
15
|
-
@change="handleBulkUploadFileChange" />
|
|
16
|
-
</v-row>
|
|
17
|
-
</v-col>
|
|
3
|
+
<!-- Title row; nav calls this screen "Units". Both pills move onto it. -->
|
|
18
4
|
<v-col cols="12">
|
|
19
|
-
<
|
|
20
|
-
<
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
</
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
{{ pageRange }}
|
|
33
|
-
</span>
|
|
34
|
-
<local-pagination v-model="page" :length="pages" @update:value="getBuildingUnit()" />
|
|
35
|
-
</v-row>
|
|
36
|
-
</template>
|
|
37
|
-
</v-toolbar>
|
|
38
|
-
|
|
39
|
-
<v-data-table :headers="props.headers" :items="items" item-value="_id" items-per-page="20" fixed-header
|
|
40
|
-
hide-default-footer @click:row="tableRowClickHandler" style="max-height: calc(100vh - (200px))">
|
|
41
|
-
<template #item.block="{ value, item }">
|
|
42
|
-
{{ value ? `Block ${value}` : "" }}
|
|
43
|
-
</template>
|
|
44
|
-
</v-data-table>
|
|
45
|
-
</v-card>
|
|
5
|
+
<PageHeader title="Units">
|
|
6
|
+
<template #actions>
|
|
7
|
+
<AppButton v-if="canCreate && canCreateUnit" variant="ghost" icon="mdi-upload"
|
|
8
|
+
:disabled="bulkUploadLoading" @click="openBulkUploadFilePicker()">
|
|
9
|
+
Bulk Upload
|
|
10
|
+
</AppButton>
|
|
11
|
+
<AppButton v-if="canCreate && canCreateUnit" icon="mdi-plus" @click="setBuilding()">
|
|
12
|
+
Add Unit
|
|
13
|
+
</AppButton>
|
|
14
|
+
</template>
|
|
15
|
+
</PageHeader>
|
|
16
|
+
<input ref="bulkUploadFileInput" type="file" accept=".xlsx,.xls,.csv" class="d-none"
|
|
17
|
+
@change="handleBulkUploadFileChange" />
|
|
46
18
|
</v-col>
|
|
47
19
|
|
|
20
|
+
<!-- Was `v-card` + a `grey-lighten-4` `v-toolbar` + a bare `v-data-table`. -->
|
|
21
|
+
<TableMain :headers="props.headers" :items="items" :loading="loading" :page="page" :pages="pages"
|
|
22
|
+
:pageRange="pageRange" show-header @refresh="getBuildingUnit()"
|
|
23
|
+
@update:page="page = $event; getBuildingUnit()" @row-click="(data: any) => tableRowClickHandler(null, data)">
|
|
24
|
+
<template #prepend-additional>
|
|
25
|
+
<AppField v-model="searchInput" search compact placeholder="Search" klass="app-toolbar__search" />
|
|
26
|
+
</template>
|
|
27
|
+
<template #item.block="{ value, item }">
|
|
28
|
+
{{ value ? `Block ${value}` : "" }}
|
|
29
|
+
</template>
|
|
30
|
+
</TableMain>
|
|
31
|
+
|
|
48
32
|
<!-- Create Dialog -->
|
|
49
33
|
<v-dialog v-model="createDialog" :width="smAndUp ? '50svw' : '100%'" persistent>
|
|
50
34
|
<BuildingUnitFormAdd :site="site" @cancel="createDialog = false" @success="successCreate()"
|
|
@@ -1,17 +1,30 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<v-row no-gutters>
|
|
3
|
+
<!-- Title left, primary action right - the design's opening row. -->
|
|
4
|
+
<v-col cols="12">
|
|
5
|
+
<PageHeader title="Bulletin Board">
|
|
6
|
+
<template #actions>
|
|
7
|
+
<AppButton v-if="canCreateBulletinBoard" icon="mdi-plus" @click="handleCreateEvent">
|
|
8
|
+
Add Announcement
|
|
9
|
+
</AppButton>
|
|
10
|
+
</template>
|
|
11
|
+
</PageHeader>
|
|
12
|
+
</v-col>
|
|
13
|
+
|
|
3
14
|
<TableMain :headers="headers" :items="paginatePlaceholderItem" v-model:search="searchInput"
|
|
4
15
|
:loading="getAnnouncementPending" :page="page" :pages="pages" :pageRange="pageRange"
|
|
5
|
-
@refresh="getAnnouncementsRefresh" :show-header="APP_CONSTANTS.RESIDENT === props.recipients" @update:page="handleUpdatePage" @row-click="handleRowClick"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
@refresh="getAnnouncementsRefresh" :show-header="APP_CONSTANTS.RESIDENT === props.recipients" @update:page="handleUpdatePage" @row-click="handleRowClick">
|
|
17
|
+
<!--
|
|
18
|
+
The tabs move ONTO the toolbar row (the design draws them there)
|
|
19
|
+
instead of a stacked strip above the table. Same options, same
|
|
20
|
+
order, same `toRoute` - which already sets the status itself.
|
|
21
|
+
-->
|
|
22
|
+
<template #tabs>
|
|
23
|
+
<button v-for="tab in tabOptions" :key="tab.status" type="button" role="tab" class="app-tab"
|
|
24
|
+
:class="{ 'app-tab--active': status === tab.status }" :aria-selected="status === tab.status"
|
|
25
|
+
@click="status = tab.status as any; toRoute(tab.status)">
|
|
26
|
+
{{ tab.name }}
|
|
27
|
+
</button>
|
|
15
28
|
</template>
|
|
16
29
|
|
|
17
30
|
<template v-slot:item.createdAt="{ item }">
|