@7365admin1/layer-common 3.2.7 → 3.2.8-staging.201
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/assets/css/primitives.css +1113 -0
- package/assets/css/screens.css +1097 -0
- package/assets/css/shell.css +258 -0
- package/assets/css/tokens.css +236 -0
- package/components/AcceptDialog.vue +17 -18
- package/components/AccessCard/AvailableStats.vue +24 -16
- package/components/AccessCardAddForm.vue +42 -42
- package/components/AccessCardAssignToUnitForm.vue +13 -21
- package/components/AccessCardDeleteDialog.vue +41 -39
- package/components/AccessCardDetailsDialog.vue +125 -79
- package/components/AccessCardPreviewDialog.vue +58 -84
- package/components/AccessCardQrTagging.vue +60 -63
- package/components/AccessCardReplaceForm.vue +27 -23
- package/components/AccessManagement.vue +86 -85
- package/components/AddEqupmentForm.vue +18 -22
- package/components/AddPassKeyToVisitor.vue +95 -73
- package/components/AppButton.vue +62 -0
- package/components/AppCard.vue +20 -0
- package/components/AppDateField.vue +49 -0
- package/components/AppField.vue +76 -0
- package/components/AppKpiTile.vue +65 -0
- package/components/AppSegmented.vue +38 -0
- package/components/AppSelect.vue +247 -0
- package/components/AreaChecklistHistoryLogs.vue +51 -44
- package/components/AreaChecklistHistoryMain.vue +14 -7
- package/components/AreaFormDialog.vue +71 -59
- package/components/AreaMain.vue +73 -107
- package/components/AttachmentsViewer.vue +84 -22
- package/components/AttendanceCheckInOutDialog.vue +85 -88
- package/components/AttendanceDetailsDialog.vue +57 -63
- package/components/AttendanceMain.vue +4 -3
- package/components/AttendanceMapSearchDialog.vue +42 -44
- package/components/AttendanceSettingsDialog.vue +79 -63
- package/components/Avatar/Main.vue +54 -2
- package/components/BillingMain.vue +26 -30
- package/components/BtnUploadFile.vue +53 -11
- package/components/BuildingForm.vue +34 -27
- package/components/BuildingManagement/buildings.vue +39 -39
- package/components/BuildingManagement/units.vue +26 -42
- package/components/BuildingUnitFormAdd.vue +20 -23
- package/components/BuildingUnitFormEdit.vue +8 -4
- package/components/BulletinBoardForm.vue +19 -24
- package/components/BulletinBoardManagement.vue +49 -57
- package/components/BulletinBoardView.vue +9 -6
- package/components/BulletinExpirationChip.vue +13 -4
- package/components/Button/Close.vue +2 -2
- package/components/CameraForm.vue +21 -22
- package/components/CameraMain.vue +50 -49
- package/components/Card/DeleteConfirmation.vue +24 -22
- package/components/Card/MemberInfoSummary.vue +28 -3
- package/components/Card/Toggle.vue +15 -3
- package/components/CardToolbar.vue +37 -0
- package/components/Carousel.vue +56 -36
- package/components/Chat/Bubbles.vue +43 -14
- package/components/Chat/Information.vue +49 -32
- package/components/Chat/ListCard.vue +38 -9
- package/components/Chat/Message.vue +41 -15
- package/components/Chat/Navigation.vue +27 -15
- package/components/Chat/SkeletonLoader.vue +9 -2
- package/components/ChecklistItemRow.vue +25 -1
- package/components/CleaningScheduleMain.vue +44 -49
- package/components/ClientDetailForm.vue +81 -56
- package/components/ClientMain.vue +92 -82
- package/components/ConfirmDialog.vue +14 -8
- package/components/Container/Standard.vue +9 -7
- package/components/CreateSubsciptionPlan.vue +148 -89
- package/components/DashboardEmptyState.vue +48 -3
- package/components/DashboardMain.vue +924 -676
- package/components/DashboardPanel.vue +84 -21
- package/components/DeliveryCompany.vue +34 -20
- package/components/Dialog/DeleteConfirmation.vue +20 -21
- package/components/Dialog/ReplaceAutofillPrompt.vue +19 -26
- package/components/Dialog/ReusablePrompt.vue +30 -29
- package/components/Dialog/UpdateMoreAction.vue +62 -59
- package/components/DocumentForm.vue +14 -21
- package/components/DocumentManagement.vue +162 -108
- package/components/DocumentViewer.vue +29 -12
- package/components/DrawImage.vue +29 -21
- package/components/EntryPass/QrTemplatePreview.vue +27 -16
- package/components/EntryPassInformation.vue +40 -10
- package/components/EntryPassMain.vue +49 -27
- package/components/EquipmentItemMain.vue +84 -97
- package/components/EquipmentManagementMain.vue +81 -103
- package/components/Facility/BookingSetup.vue +43 -52
- package/components/Feedback/Form.vue +26 -12
- package/components/FeedbackMain.vue +35 -49
- package/components/FileInput.vue +75 -58
- package/components/FileInputWithList.vue +59 -12
- package/components/FormDialog.vue +60 -21
- package/components/HidAccessLogDashboard.vue +512 -490
- package/components/HidCameraCaptureDialog.vue +216 -0
- package/components/HidEnabledGate.vue +67 -9
- package/components/HidIntercomManagement.vue +1322 -653
- package/components/HidQrCodeConfiguration.vue +1030 -258
- package/components/HidReaderForm.vue +142 -59
- package/components/HidReaderManagement.vue +211 -323
- package/components/HidServiceSettingsPanel.vue +44 -13
- package/components/HidUserEnrollment.vue +1185 -412
- package/components/HygieneUpdateMoreAction.vue +44 -44
- package/components/IncidentReport/Authorities.vue +30 -18
- package/components/IncidentReport/IncidentInformation.vue +10 -10
- package/components/IncidentReport/IncidentInformationDownload.vue +3 -4
- package/components/IncidentReport/affectedEntities.vue +41 -20
- package/components/Input/Date.vue +3 -0
- package/components/Input/DatePicker.vue +1 -2
- package/components/Input/DateTimePicker.vue +1 -1
- package/components/Input/File.vue +55 -10
- package/components/Input/FileV2.vue +39 -6
- package/components/Input/InputPhoneNumberV2.vue +2 -1
- package/components/Input/ListGroupSelection.vue +56 -8
- package/components/Input/NRICNumber.vue +1 -0
- package/components/Input/NewDate.vue +1 -0
- package/components/Input/Number.vue +1 -0
- package/components/Input/Password.vue +1 -0
- package/components/Input/PhoneNumber.vue +21 -1
- package/components/Input/VehicleNumber.vue +1 -0
- package/components/InputLabel.vue +2 -2
- package/components/InvitationClientForm.vue +21 -19
- package/components/InvitationForm.vue +15 -21
- package/components/InvitationMain.vue +46 -61
- package/components/Layout/Header.vue +263 -36
- package/components/Layout/NavigationDrawer.vue +300 -30
- package/components/ListItem.vue +33 -3
- package/components/LocalPagination.vue +27 -3
- package/components/ManageChecklistMain.vue +136 -86
- package/components/MemberInformation.vue +32 -16
- package/components/MemberMain.vue +113 -93
- package/components/MyAttendanceMain.vue +3 -5
- package/components/NavigationItem.vue +83 -3
- package/components/Nfc/NFCPatrolReportMain.vue +47 -33
- package/components/Nfc/NFCPatrolRouteForm.vue +18 -20
- package/components/Nfc/NFCPatrolRouteMain.vue +116 -103
- package/components/Nfc/NFCPatrolSettings.vue +14 -4
- package/components/Nfc/NFCTagForm.vue +22 -8
- package/components/Nfc/NFCTagMain.vue +41 -43
- package/components/Nfc/PatrolReport/DailyReportTab.vue +8 -1
- package/components/Nfc/PatrolReport/MonthlyReportTab.vue +8 -1
- package/components/Nfc/PatrolReport/MonthlyReportTable.vue +5 -3
- package/components/Nfc/PatrolReport/PatrolActivityTable.vue +26 -6
- package/components/Nfc/PatrolReport/PatrolReportTab.vue +8 -0
- package/components/Nfc/PatrolReport/PatrolRouteSummary.vue +41 -9
- package/components/Nfc/PatrolReport/RemarksDialog.vue +39 -9
- package/components/Nfc/PatrolReport/ReportEmptyState.vue +29 -9
- package/components/Nfc/PatrolReport/ReportFilters.vue +19 -10
- package/components/Nfc/PatrolReport/ReportLocationHeader.vue +32 -6
- package/components/Nfc/PatrolReport/SummaryReportTable.vue +28 -6
- package/components/NotificationSettings.vue +373 -0
- package/components/NumberSettingField.vue +9 -2
- package/components/OnlineFormConfigurationForm.vue +725 -237
- package/components/OnlineFormFill.vue +213 -106
- package/components/OnlineFormRenderPreview.vue +25 -30
- package/components/OnlineFormsConfiguration.vue +113 -109
- package/components/OrgViewDialog.vue +34 -16
- package/components/OvernightParkingAvailability.vue +49 -26
- package/components/OvernightParkingManagement.vue +78 -18
- package/components/PageHeader.vue +43 -0
- package/components/PassInformation.vue +109 -53
- package/components/PasswordConfirmation.vue +44 -26
- package/components/PeopleForm.vue +41 -20
- package/components/PhotoUpload.vue +110 -48
- package/components/PlaceholderComponent.vue +35 -19
- package/components/PlanActionsMenu.vue +12 -2
- package/components/PlanStatusBadge.vue +7 -8
- package/components/PlateNumberDisplay.vue +9 -6
- package/components/QrTemplate/PrintDialog.vue +66 -43
- package/components/RolePermissionFormCreate.vue +104 -79
- package/components/RolePermissionFormPreviewUpdate.vue +111 -109
- package/components/RolePermissionMain.vue +183 -81
- package/components/ScanVisitorQRCode.vue +99 -35
- package/components/ScheduleAreaMain.vue +34 -59
- package/components/ScheduleTaskForm.vue +61 -42
- package/components/ScheduleTaskMain.vue +56 -69
- package/components/SearchVehicleNumberUser.vue +39 -37
- package/components/ServiceProviderFormCreate.vue +29 -20
- package/components/ServiceProviderInvitationPrompt.vue +155 -0
- package/components/ServiceProviderMain.vue +601 -432
- package/components/Signature.vue +40 -34
- package/components/SignaturePad.vue +5 -29
- package/components/SiteSettings.vue +41 -11
- package/components/SiteSettingsWorkOrder.vue +61 -30
- package/components/SlideCardGroup.vue +35 -11
- package/components/Snackbar.vue +36 -3
- package/components/SpecificAttr.vue +18 -8
- package/components/StatusChip.vue +156 -0
- package/components/StockCard.vue +17 -12
- package/components/SubscriptionPlanTable.vue +207 -140
- package/components/SwitchContext.vue +201 -9
- package/components/TableHygiene.vue +36 -46
- package/components/TableList.vue +10 -10
- package/components/TableListSecondary.vue +105 -54
- package/components/TableMain.vue +251 -78
- package/components/TableV3.vue +61 -70
- package/components/TiptapEditor.vue +33 -40
- package/components/TiptapEditorQr.vue +5 -2
- package/components/Tooltip/Info.vue +69 -24
- package/components/UnitMain.vue +40 -60
- package/components/UnitPersonCard.vue +42 -13
- package/components/VehicleAddSelection.vue +48 -32
- package/components/VehicleForm.vue +112 -129
- package/components/VehicleManagement.vue +26 -12
- package/components/VehicleUpdateMoreAction.vue +59 -46
- package/components/VisitorDataFromScannedQRCodeForm.vue +90 -65
- package/components/VisitorForm.vue +774 -210
- package/components/VisitorFormSelection.vue +40 -37
- package/components/VisitorManagement.vue +239 -82
- package/components/VisitorPassKeyQRScanner.vue +65 -18
- package/components/VisitorSearchResultSummary.vue +60 -16
- package/components/VisitorSocketPopUp.vue +22 -14
- package/components/VisitorsReportPreview.vue +44 -29
- package/components/WorkOrder/Create.vue +26 -17
- package/components/WorkOrder/ListView.vue +38 -7
- package/components/WorkOrder/Main.vue +62 -59
- package/composables/useFeedback.ts +3 -3
- package/composables/useHidAmico.ts +269 -0
- package/composables/useHidNavigation.ts +2 -9
- package/composables/useMember.ts +3 -3
- package/composables/useNFCPatrolDailyReport.ts +13 -0
- package/composables/useNFCPatrolMonthlyReport.ts +22 -56
- package/composables/useNFCPatrolReport.ts +13 -0
- package/composables/useNotificationPreference.ts +87 -0
- package/composables/useOnlineFormPages.ts +2 -0
- package/composables/useRole.ts +22 -0
- package/composables/useServiceProvider.ts +3 -3
- package/composables/useServiceProviderInvitation.ts +145 -0
- package/composables/useSipWebPhone.ts +336 -0
- package/composables/useSubscriptionPlan.ts +62 -0
- package/composables/useThemePreference.ts +63 -0
- package/composables/useVisitor.ts +6 -1
- package/composables/useWebUsb.ts +127 -37
- package/composables/useWorkOrder.ts +17 -3
- package/error.vue +30 -6
- package/nuxt.config.ts +36 -0
- package/package.json +25 -4
- package/pages/[org]/[site]/access-mgmt/administrator/index.vue +2 -2
- package/pages/[org]/[site]/access-mgmt/{identity-mapping → hid-cards}/index.vue +2 -2
- package/pages/[org]/[site]/access-mgmt/intercom/index.vue +1 -1
- package/pages/notification-settings.vue +19 -0
- package/pages/payment-method-linked.vue +34 -10
- package/pages/require-customer.vue +48 -10
- package/pages/require-organization-membership.vue +32 -8
- package/pages/unauthorized.vue +29 -4
- package/plugins/secure-member.client.ts +21 -56
- package/plugins/vuetify.ts +69 -2
- package/types/nfc-patrol-report.ts +3 -2
- package/types/site.d.ts +71 -0
- package/types/subscription.ts +18 -11
- package/utils/breadcrumb.test.ts +108 -0
- package/utils/breadcrumb.ts +82 -0
- package/utils/dashboard.test.ts +118 -0
- package/utils/dashboard.ts +53 -0
- package/utils/data.test.ts +95 -0
- package/utils/data.ts +40 -2
- package/utils/hid-discover.test.ts +102 -0
- package/utils/list-page.test.ts +60 -0
- package/utils/list-page.ts +38 -0
- package/utils/materialSymbols.ts +363 -0
- package/utils/nav-sections.test.ts +190 -0
- package/utils/nav-sections.ts +151 -0
- package/utils/nfc-patrol-report.test.ts +130 -0
- package/utils/nfc-patrol-report.ts +101 -0
- package/utils/role.test.ts +69 -0
- package/utils/role.ts +10 -0
- package/utils/route-name.test.ts +85 -0
- package/utils/route-name.ts +24 -0
- package/utils/status.test.ts +110 -0
- package/utils/status.ts +135 -0
- package/utils/table-card.test.ts +84 -0
- package/utils/theme-aa-ledger.ts +148 -0
- package/utils/theme.test.ts +485 -7
- package/utils/theme.ts +295 -110
- package/.changeset/README.md +0 -8
- package/.changeset/config.json +0 -11
- package/.editorconfig +0 -12
- package/.github/workflows/main.yml +0 -17
- package/.github/workflows/publish-staging.yml +0 -47
- package/.github/workflows/publish.yml +0 -39
- package/PUBLISHING.md +0 -269
- package/components/HidIdentityMapping.vue +0 -975
- package/components/ScheduleTaskAreaFormDialog.vue +0 -144
- package/components/ScheduleTaskAreaUpdateMoreAction.vue +0 -109
- package/components/ScheduleTastTicketMain.vue +0 -182
- package/components/TableWithButton.vue +0 -94
- package/test/visitor-socket.test.mjs +0 -36
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<v-card width="100%">
|
|
3
|
-
<v-
|
|
4
|
-
<v-row no-gutters class="fill-height px-6" align="center">
|
|
5
|
-
<span class="font-weight-bold text-h5 text-capitalize">
|
|
6
|
-
{{ title }}
|
|
7
|
-
</span>
|
|
8
|
-
</v-row>
|
|
9
|
-
</v-toolbar>
|
|
2
|
+
<v-card width="100%" class="screen-modal">
|
|
3
|
+
<v-card-title class="text-capitalize">{{ title }}</v-card-title>
|
|
10
4
|
|
|
11
|
-
<v-card-text
|
|
5
|
+
<v-card-text class="screen-modal__body pa-5">
|
|
12
6
|
<v-form ref="formRef" v-model="validForm" :disabled="disable">
|
|
13
7
|
<v-row no-gutters class="pt-4">
|
|
14
8
|
<v-col v-if="isCCTV" cols="12">
|
|
15
9
|
<InputLabel class="text-capitalize" title="Camera Name" required />
|
|
16
10
|
<v-text-field
|
|
17
11
|
v-model.trim="camera.name"
|
|
12
|
+
class="filter-field"
|
|
18
13
|
density="comfortable"
|
|
19
14
|
:rules="[requiredRule]"
|
|
20
15
|
placeholder="Enter CCTV camera name"
|
|
@@ -24,6 +19,7 @@
|
|
|
24
19
|
<InputLabel class="text-capitalize" title="URL" required />
|
|
25
20
|
<v-text-field
|
|
26
21
|
v-model.trim="camera.host"
|
|
22
|
+
class="filter-field"
|
|
27
23
|
density="comfortable"
|
|
28
24
|
:rules="[requiredRule, hostRule]"
|
|
29
25
|
placeholder="Enter camera URL"
|
|
@@ -35,6 +31,7 @@
|
|
|
35
31
|
<v-select
|
|
36
32
|
v-model="camera.category"
|
|
37
33
|
:items="anprCategoryOptions"
|
|
34
|
+
class="filter-field"
|
|
38
35
|
density="comfortable"
|
|
39
36
|
:rules="[requiredRule]"
|
|
40
37
|
/>
|
|
@@ -45,6 +42,7 @@
|
|
|
45
42
|
<v-select
|
|
46
43
|
v-model="camera.direction"
|
|
47
44
|
:items="anprDirectionOptions"
|
|
45
|
+
class="filter-field"
|
|
48
46
|
density="comfortable"
|
|
49
47
|
:rules="[requiredRule]"
|
|
50
48
|
/>
|
|
@@ -55,6 +53,7 @@
|
|
|
55
53
|
<v-select
|
|
56
54
|
v-model="camera.guardPost"
|
|
57
55
|
:items="guardPostOptions"
|
|
56
|
+
class="filter-field"
|
|
58
57
|
density="comfortable"
|
|
59
58
|
/>
|
|
60
59
|
</v-col> -->
|
|
@@ -63,6 +62,7 @@
|
|
|
63
62
|
<InputLabel class="text-capitalize" title="User" required />
|
|
64
63
|
<v-text-field
|
|
65
64
|
v-model.trim="camera.username"
|
|
65
|
+
class="filter-field"
|
|
66
66
|
density="comfortable"
|
|
67
67
|
:rules="[requiredRule]"
|
|
68
68
|
placeholder="Enter username"
|
|
@@ -74,6 +74,7 @@
|
|
|
74
74
|
<v-text-field
|
|
75
75
|
v-model.trim="camera.password"
|
|
76
76
|
type="password"
|
|
77
|
+
class="filter-field"
|
|
77
78
|
density="comfortable"
|
|
78
79
|
:rules="[requiredRule]"
|
|
79
80
|
placeholder="Enter password"
|
|
@@ -86,10 +87,11 @@
|
|
|
86
87
|
v-model="camera.status"
|
|
87
88
|
hide-details
|
|
88
89
|
inset
|
|
90
|
+
class="filter-field"
|
|
89
91
|
density="comfortable"
|
|
90
92
|
true-value="active"
|
|
91
93
|
false-value="inactive"
|
|
92
|
-
:color="camera.status === 'active' ? 'success' : '
|
|
94
|
+
:color="camera.status === 'active' ? 'success' : 'muted'"
|
|
93
95
|
:label="camera.status === 'active' ? 'Active' : 'Inactive'"
|
|
94
96
|
></v-switch>
|
|
95
97
|
</v-col>
|
|
@@ -97,27 +99,24 @@
|
|
|
97
99
|
</v-form>
|
|
98
100
|
</v-card-text>
|
|
99
101
|
|
|
100
|
-
|
|
102
|
+
<!-- Was a `v-toolbar`: a grey slab with square (`tile`) buttons and a
|
|
103
|
+
hardcoded `color="black"` primary. The gate on Submit is unchanged. -->
|
|
104
|
+
<v-card-actions class="screen-modal__footer">
|
|
101
105
|
<v-row no-gutters>
|
|
102
|
-
<v-col cols="6">
|
|
106
|
+
<v-col cols="6" class="pr-2">
|
|
103
107
|
<v-btn
|
|
104
|
-
tile
|
|
105
108
|
block
|
|
106
|
-
variant="
|
|
107
|
-
class="text-none"
|
|
108
|
-
size="48"
|
|
109
|
+
variant="outlined"
|
|
110
|
+
class="text-none screen-btn-ghost"
|
|
109
111
|
@click="back"
|
|
110
112
|
text="Cancel"
|
|
111
113
|
/>
|
|
112
114
|
</v-col>
|
|
113
|
-
<v-col cols="6">
|
|
115
|
+
<v-col cols="6" class="pl-2">
|
|
114
116
|
<v-btn
|
|
115
|
-
tile
|
|
116
117
|
block
|
|
117
118
|
variant="flat"
|
|
118
|
-
|
|
119
|
-
class="text-none"
|
|
120
|
-
size="48"
|
|
119
|
+
class="text-none screen-btn-primary"
|
|
121
120
|
:disabled="!validForm || disable || !hasChanges"
|
|
122
121
|
@click="submit"
|
|
123
122
|
:loading="disable"
|
|
@@ -125,7 +124,7 @@
|
|
|
125
124
|
/>
|
|
126
125
|
</v-col>
|
|
127
126
|
</v-row>
|
|
128
|
-
</v-
|
|
127
|
+
</v-card-actions>
|
|
129
128
|
</v-card>
|
|
130
129
|
</template>
|
|
131
130
|
|
|
@@ -1,57 +1,45 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<v-row no-gutters>
|
|
3
3
|
<v-col cols="12">
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class="text-none"
|
|
18
|
-
@click="dialogAdd = true"
|
|
19
|
-
>
|
|
4
|
+
<!-- Two `grey-lighten-4` bars - one above the table, one below - are one
|
|
5
|
+
CardToolbar on the card's own surface. The literal grey stayed white
|
|
6
|
+
on the dark theme's card; measured as four 48px white slabs. -->
|
|
7
|
+
<AppCard class="table-card">
|
|
8
|
+
<CardToolbar :count="pageRange">
|
|
9
|
+
<template #left>
|
|
10
|
+
<AppButton
|
|
11
|
+
variant="icon"
|
|
12
|
+
icon="mdi-refresh"
|
|
13
|
+
aria-label="Refresh"
|
|
14
|
+
@click="getCameraRefresh()"
|
|
15
|
+
/>
|
|
16
|
+
<AppButton v-if="!prop.readOnly" @click="dialogAdd = true">
|
|
20
17
|
Add
|
|
21
|
-
</
|
|
18
|
+
</AppButton>
|
|
22
19
|
</template>
|
|
23
|
-
</v-toolbar>
|
|
24
|
-
|
|
25
|
-
<v-card-text class="pa-0 bg-red">
|
|
26
|
-
<v-data-table
|
|
27
|
-
:headers="computedHeaders"
|
|
28
|
-
:items="items"
|
|
29
|
-
fixed-header
|
|
30
|
-
hide-default-footer
|
|
31
|
-
@click:row="handleRowClick"
|
|
32
|
-
style="max-height: 300px"
|
|
33
|
-
>
|
|
34
|
-
<template #item.host="{ value }">
|
|
35
|
-
{{ maskHostUrl(value) }}
|
|
36
|
-
</template>
|
|
37
|
-
</v-data-table>
|
|
38
|
-
</v-card-text>
|
|
39
20
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
<local-pagination
|
|
47
|
-
v-model="page"
|
|
48
|
-
:length="pages"
|
|
49
|
-
@update:value="$emit('update:value', $event)"
|
|
50
|
-
/>
|
|
51
|
-
</v-row>
|
|
21
|
+
<template #right>
|
|
22
|
+
<local-pagination
|
|
23
|
+
v-model="page"
|
|
24
|
+
:length="pages"
|
|
25
|
+
@update:value="$emit('update:value', $event)"
|
|
26
|
+
/>
|
|
52
27
|
</template>
|
|
53
|
-
</
|
|
54
|
-
|
|
28
|
+
</CardToolbar>
|
|
29
|
+
|
|
30
|
+
<v-data-table
|
|
31
|
+
:headers="computedHeaders"
|
|
32
|
+
:items="items"
|
|
33
|
+
fixed-header
|
|
34
|
+
hide-default-footer
|
|
35
|
+
@click:row="handleRowClick"
|
|
36
|
+
style="max-height: 300px"
|
|
37
|
+
>
|
|
38
|
+
<template #item.host="{ value }">
|
|
39
|
+
{{ maskHostUrl(value) }}
|
|
40
|
+
</template>
|
|
41
|
+
</v-data-table>
|
|
42
|
+
</AppCard>
|
|
55
43
|
</v-col>
|
|
56
44
|
|
|
57
45
|
<!-- dialog -->
|
|
@@ -302,12 +290,25 @@ const computedHeaders = computed(() => {
|
|
|
302
290
|
{ title: "URL", key: "host" },
|
|
303
291
|
]
|
|
304
292
|
}else if(prop.type === "anpr"){
|
|
305
|
-
return [
|
|
293
|
+
return [
|
|
306
294
|
{ title: "URL", key: "host" },
|
|
307
295
|
{ title: "Type", key: "direction" },
|
|
308
296
|
]
|
|
309
297
|
}
|
|
310
|
-
//
|
|
298
|
+
// A THIRD TYPE FELL OFF THE END. Both arms above are early returns and the
|
|
299
|
+
// fallback was commented out, so `computedHeaders` resolved to `undefined`
|
|
300
|
+
// for any `type` that is neither `ip` nor `anpr` - and `:headers="undefined"`
|
|
301
|
+
// does not mean "no table", it means Vuetify derives the columns from the
|
|
302
|
+
// first item's own keys, i.e. a raw dump of every field including `_id`.
|
|
303
|
+
//
|
|
304
|
+
// `prop.headers` is the component's declared escape hatch for exactly this
|
|
305
|
+
// and nothing was reading it, so the prop was dead. Reading it makes the
|
|
306
|
+
// fallback deterministic and gives a caller a way to supply its own columns.
|
|
307
|
+
//
|
|
308
|
+
// The four commented-out entries in that prop's default (Host, Category,
|
|
309
|
+
// Guard House, Actions) are ANOTHER TEAM'S deliberate change (9610a7d,
|
|
310
|
+
// f24fa80) and are left exactly as they are.
|
|
311
|
+
return prop.headers;
|
|
311
312
|
});
|
|
312
313
|
|
|
313
314
|
const items = ref<Array<TCamera>>([]);
|
|
@@ -1,30 +1,32 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<v-card width="100%" :disabled="loading" :loading="loading">
|
|
3
|
-
<v-card-text
|
|
4
|
-
<span> {{ promptTitle }}</span>
|
|
2
|
+
<v-card class="screen-modal" width="100%" :disabled="loading" :loading="loading">
|
|
3
|
+
<v-card-text class="screen-modal__body pa-5 my-5 px-7 text-center">
|
|
4
|
+
<span class="screen-card-title"> {{ promptTitle }}</span>
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
<!-- `text-error` is Vuetify's own class over `--v-theme-error`,
|
|
7
|
+
which is the design's `--err` in both themes - already a token,
|
|
8
|
+
so it stays. -->
|
|
9
|
+
<!-- A `div`, as in the Dialog/ twin: on an inline `span` the `mt-2`
|
|
10
|
+
does nothing and the warning ran straight into the title with
|
|
11
|
+
no space - "delete this?This removes the record...". -->
|
|
12
|
+
<div v-if="message" class="text-error mt-2">
|
|
7
13
|
{{ message }} Do you want to delete anyway?
|
|
8
|
-
</
|
|
14
|
+
</div>
|
|
9
15
|
</v-card-text>
|
|
10
16
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
<!-- The design's footer: two equal-width buttons, ghost then primary,
|
|
18
|
+
on a bordered strip. The handoff draws no destructive variant, so
|
|
19
|
+
the confirming action takes the primary shape like every other
|
|
20
|
+
modal's Submit. -->
|
|
21
|
+
<div class="screen-modal__footer">
|
|
22
|
+
<v-btn class="screen-btn-ghost" variant="outlined" @click="emit('close')">
|
|
23
|
+
Cancel
|
|
24
|
+
</v-btn>
|
|
19
25
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
</v-btn>
|
|
25
|
-
</v-col>
|
|
26
|
-
</v-row>
|
|
27
|
-
</v-toolbar>
|
|
26
|
+
<v-btn class="screen-btn-primary" variant="flat" @click="emit('delete')">
|
|
27
|
+
Delete
|
|
28
|
+
</v-btn>
|
|
29
|
+
</div>
|
|
28
30
|
</v-card>
|
|
29
31
|
</template>
|
|
30
32
|
|
|
@@ -48,4 +50,4 @@ const emit = defineEmits(["close", "delete"])
|
|
|
48
50
|
|
|
49
51
|
</script>
|
|
50
52
|
|
|
51
|
-
<style scoped></style>
|
|
53
|
+
<style scoped></style>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<v-card flat
|
|
2
|
+
<v-card flat class="w-100 member-summary">
|
|
3
3
|
<v-card-text>
|
|
4
4
|
<v-row no-gutters>
|
|
5
5
|
<v-col cols="10" no-gutters>
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
</v-row>
|
|
14
14
|
</v-col>
|
|
15
15
|
<v-col cols="2" class="d-flex align-center justify-center">
|
|
16
|
-
<v-btn icon="mdi-trash-outline"
|
|
16
|
+
<v-btn class="member-summary__remove" icon="mdi-trash-outline" variant="text" size="small" @click="emit('remove')" />
|
|
17
17
|
</v-col>
|
|
18
18
|
</v-row>
|
|
19
19
|
</v-card-text>
|
|
@@ -45,4 +45,29 @@ const displayFields: Record<keyof TMemberInfo, string> = {
|
|
|
45
45
|
|
|
46
46
|
</script>
|
|
47
47
|
|
|
48
|
-
<style scoped
|
|
48
|
+
<style scoped>
|
|
49
|
+
/*
|
|
50
|
+
* Was `border="sm grey-lighten-2" color="grey-lighten-3"` - two LITERAL light
|
|
51
|
+
* greys, i.e. a pale slab with a pale frame drawn straight across the dark
|
|
52
|
+
* theme. The design draws an added-member row as an inset on the card it sits
|
|
53
|
+
* in, so it takes `--hover` (the neutral wash the chips and avatars use) on
|
|
54
|
+
* `--border`, at the inner radius.
|
|
55
|
+
*
|
|
56
|
+
* The remove action was `color="red"` - not a token, and a solid red circle the
|
|
57
|
+
* design never draws. It is the `--err` glyph the rest of the layer uses.
|
|
58
|
+
*/
|
|
59
|
+
.member-summary {
|
|
60
|
+
background: var(--hover);
|
|
61
|
+
border: 1px solid var(--border);
|
|
62
|
+
border-radius: var(--r-inner);
|
|
63
|
+
color: var(--text);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.member-summary :deep(.v-card-text) {
|
|
67
|
+
font-size: var(--fs-cell);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.member-summary__remove {
|
|
71
|
+
color: var(--err);
|
|
72
|
+
}
|
|
73
|
+
</style>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<v-card flat
|
|
2
|
+
<v-card flat class="screen-card toggle-card">
|
|
3
3
|
<v-card-text class="d-flex justify-space-between align-center py-0">
|
|
4
|
-
<p>{{ label }}</p>
|
|
4
|
+
<p class="toggle-card__label">{{ label }}</p>
|
|
5
5
|
<v-switch v-model="toggle" :color="toggleColor" hide-details />
|
|
6
6
|
</v-card-text>
|
|
7
7
|
</v-card>
|
|
@@ -22,4 +22,16 @@ defineProps({
|
|
|
22
22
|
const toggle = defineModel({default: false})
|
|
23
23
|
</script>
|
|
24
24
|
|
|
25
|
-
<style scoped
|
|
25
|
+
<style scoped>
|
|
26
|
+
/*
|
|
27
|
+
* Was `border="md black"` - a 2px LITERAL black frame, which is a black box
|
|
28
|
+
* drawn across the dark theme. The design draws a settings row as the ordinary
|
|
29
|
+
* 1px card, so this is `.screen-card` plus the design's cell type on the label
|
|
30
|
+
* (Vuetify's `<p>` inherits the body size, not the design's).
|
|
31
|
+
*/
|
|
32
|
+
.toggle-card__label {
|
|
33
|
+
font-size: var(--fs-cell);
|
|
34
|
+
color: var(--text);
|
|
35
|
+
margin: 0;
|
|
36
|
+
}
|
|
37
|
+
</style>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
THE IN-CARD TOP ROW.
|
|
3
|
+
|
|
4
|
+
The design does NOT put a screen's tabs above its card - they are the card's
|
|
5
|
+
own first row, with the pagination on the opposite end of that same line, and
|
|
6
|
+
the tab underline meets the card's rule. Today those tabs sit in a
|
|
7
|
+
`v-toolbar`'s extension slot, which stacks them BELOW a separate bar and
|
|
8
|
+
costs a whole row of height.
|
|
9
|
+
|
|
10
|
+
Two shapes, both from the handoff: with tabs (`0 20px`, the tabs supply the
|
|
11
|
+
vertical padding) and without (`10px 14px`, a refresh button on the left).
|
|
12
|
+
This component only places things - the tabs, the count and the pager are
|
|
13
|
+
passed in and keep their own behaviour.
|
|
14
|
+
-->
|
|
15
|
+
<template>
|
|
16
|
+
<div class="app-toolbar" :class="{ 'app-toolbar--plain': !$slots.tabs }">
|
|
17
|
+
<div v-if="$slots.tabs" class="app-tabs" role="tablist">
|
|
18
|
+
<slot name="tabs" />
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<slot name="left" />
|
|
22
|
+
|
|
23
|
+
<div class="app-toolbar__spacer" />
|
|
24
|
+
|
|
25
|
+
<div class="app-toolbar__right">
|
|
26
|
+
<span v-if="count" class="app-toolbar__count">{{ count }}</span>
|
|
27
|
+
<slot name="right" />
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</template>
|
|
31
|
+
|
|
32
|
+
<script setup lang="ts">
|
|
33
|
+
defineProps({
|
|
34
|
+
/** The design's `1-1 of 1` range, printed as the screen already computes it. */
|
|
35
|
+
count: { type: String, default: "" },
|
|
36
|
+
});
|
|
37
|
+
</script>
|
package/components/Carousel.vue
CHANGED
|
@@ -22,9 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
<div class="edit-btn mb-5" v-if="imgEditable">
|
|
24
24
|
<v-btn
|
|
25
|
-
|
|
26
|
-
height="42px"
|
|
27
|
-
width="100px"
|
|
25
|
+
class="screen-btn-primary"
|
|
28
26
|
@click.stop="onMediaEdit(item.url, idx)"
|
|
29
27
|
>
|
|
30
28
|
Edit
|
|
@@ -32,14 +30,9 @@
|
|
|
32
30
|
</div>
|
|
33
31
|
|
|
34
32
|
<div class="delete-btn mb-3" v-if="imgDelete">
|
|
35
|
-
<
|
|
36
|
-
color="red"
|
|
37
|
-
height="42px"
|
|
38
|
-
width="100px"
|
|
39
|
-
@click.stop="onMediaDelete(item.url)"
|
|
40
|
-
>
|
|
33
|
+
<AppButton variant="err" @click.stop="onMediaDelete(item.url)">
|
|
41
34
|
Delete
|
|
42
|
-
</
|
|
35
|
+
</AppButton>
|
|
43
36
|
</div>
|
|
44
37
|
</v-carousel-item>
|
|
45
38
|
|
|
@@ -53,20 +46,15 @@
|
|
|
53
46
|
<source :src="item.url" />
|
|
54
47
|
</video>
|
|
55
48
|
<div class="play-overlay">
|
|
56
|
-
<v-btn class="play-btn" icon size="55"
|
|
49
|
+
<v-btn class="play-btn" icon size="55">
|
|
57
50
|
<v-icon icon="mdi-play" size="40" />
|
|
58
51
|
</v-btn>
|
|
59
52
|
</div>
|
|
60
53
|
|
|
61
54
|
<div class="delete-btn pb-4" v-if="imgDelete">
|
|
62
|
-
<
|
|
63
|
-
color="red"
|
|
64
|
-
height="42px"
|
|
65
|
-
width="100px"
|
|
66
|
-
@click.stop="onMediaDelete(item.url)"
|
|
67
|
-
>
|
|
55
|
+
<AppButton variant="err" @click.stop="onMediaDelete(item.url)">
|
|
68
56
|
Delete
|
|
69
|
-
</
|
|
57
|
+
</AppButton>
|
|
70
58
|
</div>
|
|
71
59
|
</div>
|
|
72
60
|
</v-carousel-item>
|
|
@@ -82,19 +70,16 @@
|
|
|
82
70
|
<v-icon
|
|
83
71
|
:icon="getFileIcon(item.type)"
|
|
84
72
|
size="150"
|
|
85
|
-
:
|
|
73
|
+
:class="getFileIconTone(getFileIcon(item.type))"
|
|
86
74
|
/>
|
|
87
|
-
<div class="
|
|
75
|
+
<div class="screen-card-title text-center text-wrap mt-2">
|
|
76
|
+
{{ item.name }}
|
|
77
|
+
</div>
|
|
88
78
|
|
|
89
79
|
<div class="delete-btn pb-4" v-if="imgDelete">
|
|
90
|
-
<
|
|
91
|
-
color="red"
|
|
92
|
-
height="42px"
|
|
93
|
-
width="100px"
|
|
94
|
-
@click.stop="onMediaDelete(item.url)"
|
|
95
|
-
>
|
|
80
|
+
<AppButton variant="err" @click.stop="onMediaDelete(item.url)">
|
|
96
81
|
Delete
|
|
97
|
-
</
|
|
82
|
+
</AppButton>
|
|
98
83
|
</div>
|
|
99
84
|
</div>
|
|
100
85
|
</v-carousel-item>
|
|
@@ -105,7 +90,7 @@
|
|
|
105
90
|
|
|
106
91
|
<!-- Dialog for fullscreen view -->
|
|
107
92
|
<v-dialog v-model="isDialogOpen" max-width="700">
|
|
108
|
-
<v-card>
|
|
93
|
+
<v-card class="screen-modal">
|
|
109
94
|
<v-btn icon class="close-btn" @click="isDialogOpen = false">
|
|
110
95
|
<v-icon icon="mdi-close" size="40" />
|
|
111
96
|
</v-btn>
|
|
@@ -346,14 +331,21 @@ const getFileIcon = (type: string) => {
|
|
|
346
331
|
return iconMap[type] || "mdi-file-outline";
|
|
347
332
|
};
|
|
348
333
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
334
|
+
/**
|
|
335
|
+
* The file-type tone. The handoff draws no file-type icon palette at all, so
|
|
336
|
+
* the six distinctions this screen already made are kept and simply said in the
|
|
337
|
+
* tokens that carry those tones - the same reading `AttachmentsViewer` took.
|
|
338
|
+
* The one exception is the archive icon, which was purple: there is no purple
|
|
339
|
+
* token, so it falls in with the generic files on `--muted`. That IS a change
|
|
340
|
+
* of meaning and is recorded rather than papered over.
|
|
341
|
+
*/
|
|
342
|
+
function getFileIconTone(icon?: string) {
|
|
343
|
+
if (icon === "mdi-file-pdf-box") return "tone-err";
|
|
344
|
+
if (icon === "mdi-file-word-box") return "tone-info";
|
|
352
345
|
if (icon === "mdi-file-excel-box" || icon === "mdi-file-delimited-outline")
|
|
353
|
-
return "
|
|
354
|
-
if (icon === "mdi-file-powerpoint-box") return "
|
|
355
|
-
|
|
356
|
-
return "grey";
|
|
346
|
+
return "tone-ok";
|
|
347
|
+
if (icon === "mdi-file-powerpoint-box") return "tone-warn";
|
|
348
|
+
return "tone-muted";
|
|
357
349
|
}
|
|
358
350
|
|
|
359
351
|
const message = ref("");
|
|
@@ -419,6 +411,13 @@ async function downloadFile({
|
|
|
419
411
|
right: 5%;
|
|
420
412
|
}
|
|
421
413
|
|
|
414
|
+
/**
|
|
415
|
+
* DELIBERATELY NOT `.screen-modal__close`. This close sits ON the picture, not
|
|
416
|
+
* on a card, so the design's borderless muted glyph would be unreadable over a
|
|
417
|
+
* bright photo in light theme and over a dark one in dark theme. The existing
|
|
418
|
+
* dark chip is legible on both, and the handoff draws no media lightbox at all
|
|
419
|
+
* - parity with a readable control wins over a primitive that would disappear.
|
|
420
|
+
*/
|
|
422
421
|
.close-btn {
|
|
423
422
|
position: absolute;
|
|
424
423
|
top: 10px;
|
|
@@ -440,8 +439,29 @@ async function downloadFile({
|
|
|
440
439
|
pointer-events: auto;
|
|
441
440
|
}
|
|
442
441
|
|
|
442
|
+
/* Was `color="primary"`, i.e. the FOREGROUND token used as a fill. The button
|
|
443
|
+
the design fills is `--accent-strong` with `--on-accent-strong` ink. */
|
|
443
444
|
.play-btn {
|
|
444
|
-
border-radius:
|
|
445
|
+
border-radius: var(--r-pill);
|
|
446
|
+
background: var(--accent-strong);
|
|
447
|
+
color: var(--on-accent-strong);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/* The file-type tones, on tokens instead of six hard-coded hexes. */
|
|
451
|
+
.tone-err {
|
|
452
|
+
color: var(--err);
|
|
453
|
+
}
|
|
454
|
+
.tone-info {
|
|
455
|
+
color: var(--info);
|
|
456
|
+
}
|
|
457
|
+
.tone-ok {
|
|
458
|
+
color: var(--ok);
|
|
459
|
+
}
|
|
460
|
+
.tone-warn {
|
|
461
|
+
color: var(--warn);
|
|
462
|
+
}
|
|
463
|
+
.tone-muted {
|
|
464
|
+
color: var(--muted);
|
|
445
465
|
}
|
|
446
466
|
|
|
447
467
|
.dialog-video-container {
|
|
@@ -1,36 +1,32 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<v-card class="mb-2 pa-4
|
|
2
|
+
<v-card class="chat-bubble screen-card mb-2 pa-4" width="400" elevation="0">
|
|
3
3
|
<v-row no-gutters align="center">
|
|
4
4
|
<v-col cols="auto" class="mr-3">
|
|
5
|
-
<v-avatar
|
|
5
|
+
<v-avatar class="chat-bubble__avatar" size="35">
|
|
6
6
|
<v-img v-if="currentUser?.profile" width="15" height="15" />
|
|
7
|
-
<span v-else
|
|
7
|
+
<span v-else>{{ getNameInitials(senderName) }}</span>
|
|
8
8
|
</v-avatar>
|
|
9
9
|
</v-col>
|
|
10
10
|
|
|
11
11
|
<v-col class="d-flex flex-column">
|
|
12
|
-
<span class="
|
|
13
|
-
<span class="
|
|
12
|
+
<span class="chat-bubble__sender">{{ senderName }}</span>
|
|
13
|
+
<span class="chat-bubble__sub">{{ role }}</span>
|
|
14
14
|
</v-col>
|
|
15
15
|
</v-row>
|
|
16
16
|
|
|
17
17
|
<v-row no-gutters class="mt-1">
|
|
18
18
|
<v-col>
|
|
19
|
-
<span class="py-2">{{ message }}</span>
|
|
19
|
+
<span class="chat-bubble__text py-2">{{ message }}</span>
|
|
20
20
|
|
|
21
21
|
<v-row no-gutters justify="space-between" align="center" class="mt-1">
|
|
22
22
|
<v-col cols="auto">
|
|
23
|
-
<span class="
|
|
23
|
+
<span class="chat-bubble__sub">{{ timestamp }}</span>
|
|
24
24
|
</v-col>
|
|
25
|
-
<v-col
|
|
26
|
-
v-
|
|
27
|
-
cols="auto"
|
|
28
|
-
class="d-flex align-center bg-grey-200"
|
|
29
|
-
>
|
|
30
|
-
<v-icon size="16" class="mr-1 text--secondary"
|
|
25
|
+
<v-col v-if="isCurrentUser" cols="auto" class="d-flex align-center">
|
|
26
|
+
<v-icon size="16" class="chat-bubble__sub mr-1"
|
|
31
27
|
>mdi-eye-outline</v-icon
|
|
32
28
|
>
|
|
33
|
-
<span class="
|
|
29
|
+
<span class="chat-bubble__sub">{{ seenLabel }}</span>
|
|
34
30
|
</v-col>
|
|
35
31
|
</v-row>
|
|
36
32
|
</v-col>
|
|
@@ -51,3 +47,36 @@ defineProps<{
|
|
|
51
47
|
const { currentUser } = useLocalAuth();
|
|
52
48
|
const { getNameInitials } = useUtils();
|
|
53
49
|
</script>
|
|
50
|
+
|
|
51
|
+
<style scoped>
|
|
52
|
+
.chat-bubble {
|
|
53
|
+
font-family: var(--font-sans);
|
|
54
|
+
color: var(--text);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* Was `color="surface-variant"` with the initials at `text-h5`, i.e. a 24px
|
|
58
|
+
pair of letters in a 35px circle. The design's avatar is the tone wash with
|
|
59
|
+
the accent ink, and the initials fit inside it. */
|
|
60
|
+
.chat-bubble__avatar {
|
|
61
|
+
background: var(--hover);
|
|
62
|
+
color: var(--text2);
|
|
63
|
+
font-size: 13px;
|
|
64
|
+
font-weight: 700;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.chat-bubble__sender {
|
|
68
|
+
font-size: var(--fs-cell);
|
|
69
|
+
font-weight: 600;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.chat-bubble__text {
|
|
73
|
+
font-size: var(--fs-cell);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* The role, the timestamp and the seen label were `text-grey`, `text-caption`
|
|
77
|
+
and `text--secondary` - three different greys for one sub-line. One tone. */
|
|
78
|
+
.chat-bubble__sub {
|
|
79
|
+
font-size: var(--fs-chip);
|
|
80
|
+
color: var(--muted);
|
|
81
|
+
}
|
|
82
|
+
</style>
|