@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,36 +1,31 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<v-card width="100%">
|
|
3
|
-
<
|
|
4
|
-
<v-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
</span>
|
|
8
|
-
</v-row>
|
|
9
|
-
</v-toolbar>
|
|
10
|
-
<v-card-text style="max-height: 100vh; overflow-y: auto" class="pb-0">
|
|
2
|
+
<v-card width="100%" class="screen-modal role-perm">
|
|
3
|
+
<div class="role-perm__head">
|
|
4
|
+
<v-card-title>{{ props.name }}</v-card-title>
|
|
5
|
+
</div>
|
|
6
|
+
<div class="screen-modal__body role-perm__body">
|
|
11
7
|
<v-form v-model="validForm" :disabled="disable">
|
|
12
8
|
<v-row no-gutters>
|
|
13
9
|
<v-col cols="12" class="mt-4">
|
|
14
|
-
<div class="
|
|
10
|
+
<div class="role-perm__legend">
|
|
15
11
|
<span class="d-flex align-center ga-2">
|
|
16
|
-
<v-icon
|
|
12
|
+
<v-icon class="role-perm__dot--both" size="14">mdi-circle</v-icon>
|
|
17
13
|
Web and Mobile
|
|
18
14
|
</span>
|
|
19
15
|
<span class="d-flex align-center ga-2">
|
|
20
|
-
<v-icon
|
|
16
|
+
<v-icon class="role-perm__dot--web" size="14">mdi-circle</v-icon>
|
|
21
17
|
Web only
|
|
22
18
|
</span>
|
|
23
19
|
</div>
|
|
24
|
-
<
|
|
20
|
+
<AppButton
|
|
25
21
|
v-if="edit"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class="text-none mb-2"
|
|
22
|
+
variant="ghost"
|
|
23
|
+
class="mb-2"
|
|
29
24
|
@click="toggleSelectAll(!isAllSelected)"
|
|
30
25
|
>
|
|
31
26
|
{{ isAllSelected ? "Unselect All" : "Select All" }}
|
|
32
|
-
</
|
|
33
|
-
<
|
|
27
|
+
</AppButton>
|
|
28
|
+
<div class="role-perm__list">
|
|
34
29
|
<template
|
|
35
30
|
v-for="(actions, resourceKey, resourceIndex) in safePermissions"
|
|
36
31
|
:key="resourceKey"
|
|
@@ -38,28 +33,23 @@
|
|
|
38
33
|
<v-divider v-if="resourceIndex > 0" />
|
|
39
34
|
<v-list-item density="compact" :ripple="false">
|
|
40
35
|
<template #prepend>
|
|
41
|
-
<
|
|
36
|
+
<StatusChip
|
|
42
37
|
class="mr-3"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
:
|
|
46
|
-
|
|
47
|
-
{{ selectedActionCount(String(resourceKey)) }}
|
|
48
|
-
</v-chip>
|
|
38
|
+
:dot="false"
|
|
39
|
+
:tone="isWebOnly(String(resourceKey)) ? 'info' : 'warn'"
|
|
40
|
+
:label="String(selectedActionCount(String(resourceKey)))"
|
|
41
|
+
/>
|
|
49
42
|
</template>
|
|
50
|
-
<v-list-item-title
|
|
51
|
-
class="text-capitalize text-subtitle-2 font-weight-medium"
|
|
52
|
-
>
|
|
43
|
+
<v-list-item-title class="text-capitalize role-perm__row-title">
|
|
53
44
|
{{ formatResourceLabel(String(resourceKey)) }}
|
|
54
45
|
</v-list-item-title>
|
|
55
46
|
<template #append>
|
|
56
47
|
<v-switch
|
|
57
48
|
:model-value="isCategoryEnabled(String(resourceKey))"
|
|
58
49
|
:disabled="!edit"
|
|
59
|
-
|
|
50
|
+
class="app-switch"
|
|
60
51
|
density="compact"
|
|
61
52
|
hide-details
|
|
62
|
-
inset
|
|
63
53
|
@update:model-value="
|
|
64
54
|
toggleCategory(String(resourceKey), $event)
|
|
65
55
|
"
|
|
@@ -81,7 +71,7 @@
|
|
|
81
71
|
)
|
|
82
72
|
"
|
|
83
73
|
:disabled="!edit"
|
|
84
|
-
color="
|
|
74
|
+
color="accent"
|
|
85
75
|
density="compact"
|
|
86
76
|
class="mr-2"
|
|
87
77
|
@update:model-value="
|
|
@@ -93,20 +83,18 @@
|
|
|
93
83
|
"
|
|
94
84
|
/>
|
|
95
85
|
</template>
|
|
96
|
-
<v-list-item-title
|
|
97
|
-
class="text-subtitle-2 font-weight-medium text-capitalize"
|
|
98
|
-
>
|
|
86
|
+
<v-list-item-title class="text-capitalize role-perm__row-title">
|
|
99
87
|
{{ String(actionKey).replace(/-/g, " ") }}
|
|
100
88
|
</v-list-item-title>
|
|
101
|
-
<v-list-item-subtitle class="
|
|
89
|
+
<v-list-item-subtitle class="role-perm__row-sub">
|
|
102
90
|
{{ action.description }}
|
|
103
91
|
</v-list-item-subtitle>
|
|
104
92
|
</v-list-item>
|
|
105
93
|
</template>
|
|
106
94
|
</template>
|
|
107
95
|
</template>
|
|
108
|
-
</
|
|
109
|
-
<div v-if="errorMessages" class="
|
|
96
|
+
</div>
|
|
97
|
+
<div v-if="errorMessages" class="role-perm__error mt-2">
|
|
110
98
|
{{ errorMessages }}
|
|
111
99
|
</div>
|
|
112
100
|
</v-col>
|
|
@@ -114,11 +102,7 @@
|
|
|
114
102
|
<v-col v-if="edit" cols="12" class="my-2">
|
|
115
103
|
<v-row no-gutters>
|
|
116
104
|
<v-col cols="12" class="text-center">
|
|
117
|
-
<span
|
|
118
|
-
class="text-none text-subtitle-2 font-weight-medium text-error"
|
|
119
|
-
>
|
|
120
|
-
{{ message }}
|
|
121
|
-
</span>
|
|
105
|
+
<span class="role-perm__error">{{ message }}</span>
|
|
122
106
|
</v-col>
|
|
123
107
|
</v-row>
|
|
124
108
|
</v-col>
|
|
@@ -126,74 +110,23 @@
|
|
|
126
110
|
<v-col cols="12" class="my-2">
|
|
127
111
|
<v-row no-gutters>
|
|
128
112
|
<v-col cols="12" class="text-center">
|
|
129
|
-
<span
|
|
130
|
-
class="text-none text-subtitle-2 font-weight-medium text-error"
|
|
131
|
-
>
|
|
132
|
-
{{ message }}
|
|
133
|
-
</span>
|
|
113
|
+
<span class="role-perm__error">{{ message }}</span>
|
|
134
114
|
</v-col>
|
|
135
115
|
</v-row>
|
|
136
116
|
</v-col>
|
|
137
117
|
</v-row>
|
|
138
118
|
</v-form>
|
|
139
|
-
</
|
|
140
|
-
|
|
141
|
-
<
|
|
142
|
-
<v-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
>
|
|
151
|
-
Close
|
|
152
|
-
</v-btn>
|
|
153
|
-
</v-col>
|
|
154
|
-
|
|
155
|
-
<v-col v-else cols="6">
|
|
156
|
-
<v-btn
|
|
157
|
-
block
|
|
158
|
-
variant="text"
|
|
159
|
-
class="text-none"
|
|
160
|
-
size="large"
|
|
161
|
-
@click="edit = false"
|
|
162
|
-
>
|
|
163
|
-
Cancel
|
|
164
|
-
</v-btn>
|
|
165
|
-
</v-col>
|
|
166
|
-
|
|
167
|
-
<v-col v-if="!edit" cols="6">
|
|
168
|
-
<v-btn
|
|
169
|
-
block
|
|
170
|
-
variant="flat"
|
|
171
|
-
color="black"
|
|
172
|
-
class="text-none"
|
|
173
|
-
size="large"
|
|
174
|
-
:disabled="disable"
|
|
175
|
-
@click="edit = true"
|
|
176
|
-
>
|
|
177
|
-
Edit
|
|
178
|
-
</v-btn>
|
|
179
|
-
</v-col>
|
|
180
|
-
|
|
181
|
-
<v-col v-else cols="6">
|
|
182
|
-
<v-btn
|
|
183
|
-
block
|
|
184
|
-
variant="flat"
|
|
185
|
-
color="black"
|
|
186
|
-
class="text-none"
|
|
187
|
-
size="large"
|
|
188
|
-
:disabled="Boolean(errorMessages) || disable"
|
|
189
|
-
:loading="disable"
|
|
190
|
-
@click="submit()"
|
|
191
|
-
>
|
|
192
|
-
Submit
|
|
193
|
-
</v-btn>
|
|
194
|
-
</v-col>
|
|
195
|
-
</v-row>
|
|
196
|
-
</v-toolbar>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<div class="role-perm__footer">
|
|
122
|
+
<AppButton v-if="!edit" variant="ghost" @click="emit('cancel')">Close</AppButton>
|
|
123
|
+
<AppButton v-else variant="ghost" @click="edit = false">Cancel</AppButton>
|
|
124
|
+
|
|
125
|
+
<AppButton v-if="!edit" :disabled="disable" @click="edit = true">Edit</AppButton>
|
|
126
|
+
<AppButton v-else :disabled="Boolean(errorMessages) || disable" @click="submit()">
|
|
127
|
+
Submit
|
|
128
|
+
</AppButton>
|
|
129
|
+
</div>
|
|
197
130
|
</v-card>
|
|
198
131
|
</template>
|
|
199
132
|
|
|
@@ -242,10 +175,8 @@ function formatResourceLabel(key: string) {
|
|
|
242
175
|
return props.webOnlyResources.includes(key) ? `${label} (Web Only)` : label;
|
|
243
176
|
}
|
|
244
177
|
|
|
245
|
-
function
|
|
246
|
-
return props.webOnlyResources.includes(resource)
|
|
247
|
-
? "blue-darken-2"
|
|
248
|
-
: "amber-darken-2";
|
|
178
|
+
function isWebOnly(resource: string) {
|
|
179
|
+
return props.webOnlyResources.includes(resource);
|
|
249
180
|
}
|
|
250
181
|
|
|
251
182
|
function selectedActionCount(resource: string) {
|
|
@@ -357,3 +288,74 @@ async function submit() {
|
|
|
357
288
|
}
|
|
358
289
|
}
|
|
359
290
|
</script>
|
|
291
|
+
|
|
292
|
+
<style scoped>
|
|
293
|
+
.role-perm__head {
|
|
294
|
+
display: flex;
|
|
295
|
+
align-items: center;
|
|
296
|
+
justify-content: space-between;
|
|
297
|
+
padding-right: 12px;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.role-perm__body {
|
|
301
|
+
padding: 0 20px;
|
|
302
|
+
max-height: calc(100vh - 160px);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.role-perm__legend {
|
|
306
|
+
display: flex;
|
|
307
|
+
align-items: center;
|
|
308
|
+
gap: 16px;
|
|
309
|
+
margin-bottom: 12px;
|
|
310
|
+
font-size: var(--fs-breadcrumb);
|
|
311
|
+
color: var(--text2);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/* The two legend dots carried `amber-darken-2` and `blue-darken-2` - fixed
|
|
315
|
+
Vuetify hues that do not follow the theme, and the same two hues the count
|
|
316
|
+
chip beside each row used. Both are the design's tones now, and the chip and
|
|
317
|
+
the dot stay in step because they read the same `isWebOnly`. */
|
|
318
|
+
.role-perm__dot--both {
|
|
319
|
+
color: var(--warn);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.role-perm__dot--web {
|
|
323
|
+
color: var(--info);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/* Was a `variant="outlined"` v-card, i.e. a card inside a card. It is the list
|
|
327
|
+
frame, so it takes the design's card border and radius without laying a
|
|
328
|
+
second surface over the first. */
|
|
329
|
+
.role-perm__list {
|
|
330
|
+
border: 1px solid var(--border);
|
|
331
|
+
border-radius: var(--r-card);
|
|
332
|
+
overflow: hidden;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.role-perm__row-title {
|
|
336
|
+
font-size: var(--fs-cell) !important;
|
|
337
|
+
font-weight: var(--fw-cell) !important;
|
|
338
|
+
color: var(--text);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.role-perm__row-sub {
|
|
342
|
+
font-size: var(--fs-breadcrumb) !important;
|
|
343
|
+
color: var(--text2);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.role-perm__error {
|
|
347
|
+
font-size: var(--fs-cell);
|
|
348
|
+
font-weight: 600;
|
|
349
|
+
color: var(--err);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/* The design's dialog footer. Deliberately not the shared
|
|
353
|
+
`.screen-modal__footer` - that class is on the batch-D branch (#1150), not
|
|
354
|
+
on this base, and a second copy in `screens.css` would collide with it. */
|
|
355
|
+
.role-perm__footer {
|
|
356
|
+
display: flex;
|
|
357
|
+
justify-content: flex-end;
|
|
358
|
+
gap: 8px;
|
|
359
|
+
padding: 16px 20px 20px;
|
|
360
|
+
}
|
|
361
|
+
</style>
|
|
@@ -1,49 +1,46 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
<div>
|
|
3
|
+
<!--
|
|
4
|
+
THE DESIGN'S PAGE SCAFFOLD (same shape as Service Providers).
|
|
5
|
+
|
|
6
|
+
`Create role` moves from a bare row above the card onto the title line,
|
|
7
|
+
on the right, where the design puts a screen's primary action. It is the
|
|
8
|
+
same button under the same `canCreateRole` permission opening the same
|
|
9
|
+
dialog. The refresh, the range, the pager and the table are unchanged.
|
|
10
|
+
-->
|
|
11
|
+
<PageHeader title="Roles and Permissions">
|
|
12
|
+
<template #actions>
|
|
13
|
+
<AppButton v-if="canCreateRole" @click="createDialog = true">
|
|
13
14
|
Create role
|
|
14
|
-
</
|
|
15
|
-
</
|
|
16
|
-
</
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
</template>
|
|
44
|
-
</v-toolbar>
|
|
45
|
-
|
|
46
|
-
<v-data-table
|
|
15
|
+
</AppButton>
|
|
16
|
+
</template>
|
|
17
|
+
</PageHeader>
|
|
18
|
+
|
|
19
|
+
<AppCard class="table-card">
|
|
20
|
+
<!-- `v-card`'s `:loading` bar, drawn explicitly: a loading screen must
|
|
21
|
+
still say so now that the card is not a `v-card`. -->
|
|
22
|
+
<v-progress-linear v-if="loading" indeterminate height="2" />
|
|
23
|
+
|
|
24
|
+
<CardToolbar :count="pageRange">
|
|
25
|
+
<template #left>
|
|
26
|
+
<AppButton
|
|
27
|
+
variant="icon"
|
|
28
|
+
icon="mdi-refresh"
|
|
29
|
+
aria-label="Refresh"
|
|
30
|
+
@click="getRoles()"
|
|
31
|
+
/>
|
|
32
|
+
</template>
|
|
33
|
+
|
|
34
|
+
<template #right>
|
|
35
|
+
<local-pagination
|
|
36
|
+
v-model="page"
|
|
37
|
+
:length="pages"
|
|
38
|
+
@update:value="getRoles()"
|
|
39
|
+
/>
|
|
40
|
+
</template>
|
|
41
|
+
</CardToolbar>
|
|
42
|
+
|
|
43
|
+
<v-data-table
|
|
47
44
|
:headers="props.headers"
|
|
48
45
|
:items="items"
|
|
49
46
|
item-value="_id"
|
|
@@ -58,7 +55,11 @@
|
|
|
58
55
|
<span class="text-caption font-weight-bold text-capitalize">
|
|
59
56
|
permissions
|
|
60
57
|
</span>
|
|
61
|
-
<
|
|
58
|
+
<StatusChip
|
|
59
|
+
:dot="false"
|
|
60
|
+
tone="info"
|
|
61
|
+
:label="String(getPermissionCount(value))"
|
|
62
|
+
/>
|
|
62
63
|
</template>
|
|
63
64
|
|
|
64
65
|
<template #item.action-table="{ item }" v-if="canDeleteRole">
|
|
@@ -73,9 +74,8 @@
|
|
|
73
74
|
</v-list>
|
|
74
75
|
</v-menu>
|
|
75
76
|
</template>
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
</v-col>
|
|
77
|
+
</v-data-table>
|
|
78
|
+
</AppCard>
|
|
79
79
|
|
|
80
80
|
<!-- dialogs -->
|
|
81
81
|
<v-dialog v-model="createDialog" width="500" persistent>
|
|
@@ -84,6 +84,7 @@
|
|
|
84
84
|
:permissions="props.permissions"
|
|
85
85
|
:org="props.orgId"
|
|
86
86
|
:site-id="props.siteId"
|
|
87
|
+
:site-state="siteFieldShown"
|
|
87
88
|
:web-only-resources="props.webOnlyResources"
|
|
88
89
|
@success="success()"
|
|
89
90
|
:type="props.type"
|
|
@@ -105,48 +106,88 @@
|
|
|
105
106
|
/>
|
|
106
107
|
</v-dialog>
|
|
107
108
|
|
|
108
|
-
|
|
109
|
-
v-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
<
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
109
|
+
<!--
|
|
110
|
+
Was a `v-toolbar` title bar over a toolbar footer of two block buttons,
|
|
111
|
+
the confirm one on `color="black"` - not a token, so it drew a
|
|
112
|
+
light-theme fill on the dark theme. The shared confirmation shape now.
|
|
113
|
+
Same question, same reassignment rows, same disabled rule.
|
|
114
|
+
-->
|
|
115
|
+
<v-dialog v-model="confirmDialog" max-width="520">
|
|
116
|
+
<v-card class="screen-modal">
|
|
117
|
+
<v-card-title>Delete Role</v-card-title>
|
|
118
|
+
|
|
119
|
+
<v-card-text class="screen-modal__body">
|
|
120
|
+
<span v-if="!hasAssignedMembers">
|
|
121
|
+
Are you sure you want to delete this role? This action cannot be
|
|
122
|
+
undone.
|
|
123
|
+
</span>
|
|
124
|
+
<template v-else>
|
|
125
|
+
<p class="mb-4">
|
|
126
|
+
There are currently
|
|
127
|
+
<span class="text-error font-weight-bold">
|
|
128
|
+
{{ deletionPreview.members.length }}
|
|
129
|
+
{{ deletionPreview.members.length === 1 ? "user" : "users" }}
|
|
130
|
+
</span>
|
|
131
|
+
assigned to this role. Reassign each user before deleting it so
|
|
132
|
+
they do not lose access.
|
|
133
|
+
</p>
|
|
134
|
+
|
|
135
|
+
<v-row
|
|
136
|
+
v-for="member in deletionPreview.members"
|
|
137
|
+
:key="member._id"
|
|
138
|
+
no-gutters
|
|
139
|
+
class="mb-3 pa-3 border rounded-lg"
|
|
140
|
+
align="center"
|
|
141
|
+
>
|
|
142
|
+
<v-col cols="12" sm="5" class="mb-2 mb-sm-0">
|
|
143
|
+
{{ member.name || member.user }}
|
|
144
|
+
</v-col>
|
|
145
|
+
<v-col cols="12" sm="7">
|
|
146
|
+
<v-select
|
|
147
|
+
v-model="memberReassignments[member._id]"
|
|
148
|
+
:items="deletionPreview.replacementRoles"
|
|
149
|
+
item-title="name"
|
|
150
|
+
item-value="_id"
|
|
151
|
+
label="Reassign role"
|
|
152
|
+
density="comfortable"
|
|
153
|
+
hide-details
|
|
154
|
+
class="filter-field"
|
|
155
|
+
/>
|
|
156
|
+
</v-col>
|
|
157
|
+
</v-row>
|
|
158
|
+
</template>
|
|
159
|
+
</v-card-text>
|
|
123
160
|
|
|
124
|
-
|
|
125
|
-
<div class="d-flex justify-center ga-3 w-100">
|
|
161
|
+
<div class="screen-modal__footer">
|
|
126
162
|
<v-btn
|
|
127
|
-
|
|
128
|
-
|
|
163
|
+
class="screen-btn-ghost"
|
|
164
|
+
variant="outlined"
|
|
129
165
|
:disabled="deleteLoading"
|
|
166
|
+
@click="closeDeleteDialog"
|
|
130
167
|
>
|
|
131
|
-
|
|
168
|
+
Cancel
|
|
132
169
|
</v-btn>
|
|
133
170
|
<v-btn
|
|
134
|
-
|
|
171
|
+
class="screen-btn-primary"
|
|
135
172
|
variant="flat"
|
|
136
|
-
@click="handleDeleteRole"
|
|
137
173
|
:loading="deleteLoading"
|
|
174
|
+
:disabled="hasAssignedMembers && !canConfirmDeletion"
|
|
175
|
+
@click="handleDeleteRole"
|
|
138
176
|
>
|
|
139
|
-
Delete
|
|
177
|
+
{{ hasAssignedMembers ? "Confirm" : "Delete" }}
|
|
140
178
|
</v-btn>
|
|
141
179
|
</div>
|
|
142
|
-
</
|
|
143
|
-
</
|
|
180
|
+
</v-card>
|
|
181
|
+
</v-dialog>
|
|
144
182
|
|
|
145
183
|
<Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
|
|
146
|
-
</
|
|
184
|
+
</div>
|
|
147
185
|
</template>
|
|
148
186
|
|
|
149
187
|
<script setup lang="ts">
|
|
188
|
+
// Relative import: this layer's `utils/` is not auto-imported into consumers.
|
|
189
|
+
import { showSiteField } from "../utils/role";
|
|
190
|
+
|
|
150
191
|
const props = defineProps({
|
|
151
192
|
orgId: {
|
|
152
193
|
type: String,
|
|
@@ -160,6 +201,18 @@ const props = defineProps({
|
|
|
160
201
|
type: String,
|
|
161
202
|
default: "",
|
|
162
203
|
},
|
|
204
|
+
/*
|
|
205
|
+
* Whether the Create-role dialog shows its (read-only) Site field.
|
|
206
|
+
* The dialog defaulted this to `true` but this component never declared or
|
|
207
|
+
* forwarded it, and its root is a `<div>`, so a fallthrough `site-state`
|
|
208
|
+
* attribute landed on that div instead of the dialog - no consuming app
|
|
209
|
+
* could switch the field off. Admin roles are never site-scoped, so the
|
|
210
|
+
* field drew permanently disabled and empty there (QA, admin PR #18).
|
|
211
|
+
*/
|
|
212
|
+
siteState: {
|
|
213
|
+
type: Boolean,
|
|
214
|
+
default: true,
|
|
215
|
+
},
|
|
163
216
|
type: {
|
|
164
217
|
type: String,
|
|
165
218
|
required: true,
|
|
@@ -259,6 +312,10 @@ function tableRowClickHandler(_: any, data: any) {
|
|
|
259
312
|
|
|
260
313
|
const createDialog = ref(false);
|
|
261
314
|
|
|
315
|
+
const siteFieldShown = computed(() =>
|
|
316
|
+
showSiteField(props.siteState, props.type)
|
|
317
|
+
);
|
|
318
|
+
|
|
262
319
|
function success() {
|
|
263
320
|
createDialog.value = false;
|
|
264
321
|
getRoles();
|
|
@@ -367,10 +424,49 @@ async function refreshRolesAfterUpdate(savedPermissions: string[]) {
|
|
|
367
424
|
const confirmDialog = ref(false);
|
|
368
425
|
const selectedRoleId = ref<string | null>(null);
|
|
369
426
|
const deleteLoading = ref(false);
|
|
427
|
+
const deletionPreview = ref({
|
|
428
|
+
members: [] as Array<Record<string, any>>,
|
|
429
|
+
replacementRoles: [] as Array<Record<string, any>>,
|
|
430
|
+
});
|
|
431
|
+
const memberReassignments = ref<Record<string, string>>({});
|
|
370
432
|
|
|
371
|
-
|
|
433
|
+
const hasAssignedMembers = computed(
|
|
434
|
+
() => deletionPreview.value.members.length > 0
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
const canConfirmDeletion = computed(() =>
|
|
438
|
+
deletionPreview.value.members.every(
|
|
439
|
+
(member) => Boolean(memberReassignments.value[member._id])
|
|
440
|
+
)
|
|
441
|
+
);
|
|
442
|
+
|
|
443
|
+
const { getDeletionPreview, deleteWithReassignments } = useRole();
|
|
444
|
+
|
|
445
|
+
async function openDeleteDialog(id: string) {
|
|
372
446
|
selectedRoleId.value = id;
|
|
373
|
-
|
|
447
|
+
deletionPreview.value = { members: [], replacementRoles: [] };
|
|
448
|
+
memberReassignments.value = {};
|
|
449
|
+
deleteLoading.value = true;
|
|
450
|
+
|
|
451
|
+
try {
|
|
452
|
+
const res = await getDeletionPreview(id);
|
|
453
|
+
deletionPreview.value = res.data;
|
|
454
|
+
confirmDialog.value = true;
|
|
455
|
+
} catch (error: any) {
|
|
456
|
+
const errorMessage =
|
|
457
|
+
error?.response?._data?.message || "Failed to load role assignments.";
|
|
458
|
+
showMessage(errorMessage, "error");
|
|
459
|
+
selectedRoleId.value = null;
|
|
460
|
+
} finally {
|
|
461
|
+
deleteLoading.value = false;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function closeDeleteDialog() {
|
|
466
|
+
confirmDialog.value = false;
|
|
467
|
+
selectedRoleId.value = null;
|
|
468
|
+
deletionPreview.value = { members: [], replacementRoles: [] };
|
|
469
|
+
memberReassignments.value = {};
|
|
374
470
|
}
|
|
375
471
|
|
|
376
472
|
function showMessage(msg: string, color: string) {
|
|
@@ -383,17 +479,23 @@ async function handleDeleteRole() {
|
|
|
383
479
|
if (!selectedRoleId.value) return;
|
|
384
480
|
deleteLoading.value = true;
|
|
385
481
|
try {
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
|
|
482
|
+
const reassignments = deletionPreview.value.members.map((member) => ({
|
|
483
|
+
memberId: member._id,
|
|
484
|
+
roleId: memberReassignments.value[member._id],
|
|
485
|
+
}));
|
|
486
|
+
const res = hasAssignedMembers.value
|
|
487
|
+
? await deleteWithReassignments(selectedRoleId.value, reassignments)
|
|
488
|
+
: await deleteRole(selectedRoleId.value);
|
|
489
|
+
|
|
490
|
+
closeDeleteDialog();
|
|
389
491
|
showMessage(res.message, "success");
|
|
390
492
|
getRoles();
|
|
391
493
|
} catch (error: any) {
|
|
392
|
-
const errorMessage =
|
|
494
|
+
const errorMessage =
|
|
495
|
+
error?.response?._data?.message || "Failed to delete role.";
|
|
393
496
|
showMessage(errorMessage, "error");
|
|
394
497
|
} finally {
|
|
395
498
|
deleteLoading.value = false;
|
|
396
|
-
selectedRoleId.value = null;
|
|
397
499
|
}
|
|
398
500
|
}
|
|
399
501
|
</script>
|