@7365admin1/layer-common 4.0.2 → 4.0.3-staging.221
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/components/AccessCardQrTagging.vue +0 -4
- package/components/AccessManagement.vue +0 -4
- package/components/BuildingManagement/buildings.vue +0 -4
- package/components/BulletinBoardManagement.vue +0 -3
- package/components/DashboardMain.vue +27 -8
- package/components/DocumentManagement.vue +0 -4
- package/components/IncidentReport/Authorities.vue +0 -4
- package/components/IncidentReport/IncidentInformation.vue +0 -4
- package/components/IncidentReport/IncidentInformationDownload.vue +0 -4
- package/components/IncidentReport/affectedEntities.vue +0 -4
- package/components/Input/InputPhoneNumberV2.vue +2 -2
- package/components/MemberMain.vue +8 -0
- package/components/RolePermissionFormCreate.vue +6 -1
- package/components/RolePermissionFormPreviewUpdate.vue +22 -11
- package/components/RolePermissionMain.vue +37 -3
- package/components/VehicleManagement.vue +0 -4
- package/components/VisitorManagement.vue +0 -3
- package/components/VisitorsReportPreview.vue +0 -3
- package/composables/useBulletinBoardPermission.ts +6 -1
- package/composables/useSettingsPermission.ts +7 -1
- package/package.json +2 -2
|
@@ -138,10 +138,6 @@
|
|
|
138
138
|
// into the consuming apps but not reliably into the layer's own components.
|
|
139
139
|
import { readListPage } from "../utils/list-page";
|
|
140
140
|
|
|
141
|
-
definePageMeta({
|
|
142
|
-
middleware: ["01-auth", "02-org"],
|
|
143
|
-
memberOnly: true,
|
|
144
|
-
});
|
|
145
141
|
const props = defineProps({
|
|
146
142
|
headers: {
|
|
147
143
|
type: Array as PropType<Array<Record<string, any>>>,
|
|
@@ -162,10 +162,6 @@
|
|
|
162
162
|
// into the consuming apps but not reliably into the layer's own components.
|
|
163
163
|
import { levelCount } from "../../utils/data";
|
|
164
164
|
|
|
165
|
-
definePageMeta({
|
|
166
|
-
middleware: ["01-auth", "02-org"],
|
|
167
|
-
memberOnly: true,
|
|
168
|
-
});
|
|
169
165
|
|
|
170
166
|
const props = defineProps({
|
|
171
167
|
headers: {
|
|
@@ -89,9 +89,6 @@ import { APP_CONSTANTS } from '../constants/app';
|
|
|
89
89
|
// is a ReferenceError that blanks the screen in every consuming app.
|
|
90
90
|
import { readListPage } from '../utils/list-page';
|
|
91
91
|
|
|
92
|
-
definePageMeta({
|
|
93
|
-
memberOnly: true,
|
|
94
|
-
})
|
|
95
92
|
|
|
96
93
|
const props = defineProps({
|
|
97
94
|
siteId: {
|
|
@@ -1343,27 +1343,41 @@ const { userAppRole } = useLocalSetup();
|
|
|
1343
1343
|
const { downloadPDF } = usePDFDownload();
|
|
1344
1344
|
const route = useRoute();
|
|
1345
1345
|
|
|
1346
|
+
// Each app spells its own resources, and the three that render this dashboard
|
|
1347
|
+
// disagree: work orders are `work_orders` in `-security`, `workOrder` in
|
|
1348
|
+
// `-property-management` and `work_order` in this layer's `useCleaningPermission`;
|
|
1349
|
+
// visitors are `visitor-mgmt` in `-security` and `visitorManagement` in
|
|
1350
|
+
// `-property-management`. A widget asking for one spelling is invisible to a role
|
|
1351
|
+
// granted another, so every spelling in use is listed. One canonical vocabulary
|
|
1352
|
+
// would let all of these collapse back to a single string -- that is a
|
|
1353
|
+
// cross-repo change, not one this component can make.
|
|
1354
|
+
const WORK_ORDER_SEE_ALL = [
|
|
1355
|
+
"work_orders:see-all-work-orders",
|
|
1356
|
+
"workOrder:see-all-work-orders",
|
|
1357
|
+
"work_order:see-all-work-orders",
|
|
1358
|
+
];
|
|
1359
|
+
|
|
1346
1360
|
const propertyWidgetPermissions: Record<string, string[]> = {
|
|
1347
|
-
workOrders:
|
|
1361
|
+
workOrders: WORK_ORDER_SEE_ALL,
|
|
1348
1362
|
incidents: ["incident-reports:see-incident-reports"],
|
|
1349
|
-
visitors: ["visitor-mgmt:see-all-visitor"],
|
|
1363
|
+
visitors: ["visitor-mgmt:see-all-visitor", "visitorManagement:see-all-visitor"],
|
|
1350
1364
|
facilityBookings: ["facility-booking-mgmt:see-all-facility-booking"],
|
|
1351
|
-
activity:
|
|
1365
|
+
activity: WORK_ORDER_SEE_ALL,
|
|
1352
1366
|
upcomingEvents: ["event-mgmt:see-all-event"],
|
|
1353
1367
|
todayReminders: ["event-mgmt:see-all-event"],
|
|
1354
1368
|
todayAttentions: [
|
|
1355
1369
|
"incident-reports:see-incident-reports",
|
|
1356
1370
|
"facility-booking-mgmt:see-all-facility-booking",
|
|
1357
1371
|
],
|
|
1358
|
-
workOrderStatus:
|
|
1372
|
+
workOrderStatus: WORK_ORDER_SEE_ALL,
|
|
1359
1373
|
feedbacks: ["feedbacks:see-all-feedback"],
|
|
1360
1374
|
activePatrol: ["virtual-patrol:see-all-virtual-patrol-logs"],
|
|
1361
1375
|
};
|
|
1362
1376
|
|
|
1363
1377
|
const moduleWidgetPermissions: Record<string, string[]> = {
|
|
1364
|
-
openWorkOrder:
|
|
1365
|
-
workOrders:
|
|
1366
|
-
activity:
|
|
1378
|
+
openWorkOrder: WORK_ORDER_SEE_ALL,
|
|
1379
|
+
workOrders: WORK_ORDER_SEE_ALL,
|
|
1380
|
+
activity: WORK_ORDER_SEE_ALL,
|
|
1367
1381
|
taskSchedule: [
|
|
1368
1382
|
"cleaning-schedule-mgmt:see-all-schedules",
|
|
1369
1383
|
"schedule-task-mgmt:see-all-schedule-tasks",
|
|
@@ -1406,7 +1420,12 @@ function canViewWidget(key: string) {
|
|
|
1406
1420
|
? propertyWidgetPermissions[key]
|
|
1407
1421
|
: moduleWidgetPermissions[key];
|
|
1408
1422
|
|
|
1409
|
-
|
|
1423
|
+
// Fail closed. This used to return `true` while `userAppRole` was still
|
|
1424
|
+
// unresolved, so the dashboard drew every widget and then took some away --
|
|
1425
|
+
// and if the role fetch failed outright it drew every widget permanently.
|
|
1426
|
+
// Every other gate in this package returns `false` without a role; this one
|
|
1427
|
+
// now agrees with them.
|
|
1428
|
+
if (!permissions) return false;
|
|
1410
1429
|
if (permissions.includes("*")) return true;
|
|
1411
1430
|
|
|
1412
1431
|
if (
|
|
@@ -610,10 +610,6 @@
|
|
|
610
610
|
// into the consuming apps but not reliably into the layer's own components.
|
|
611
611
|
import { readListPage } from "../utils/list-page";
|
|
612
612
|
|
|
613
|
-
definePageMeta({
|
|
614
|
-
middleware: ["01-auth", "02-org"],
|
|
615
|
-
memberOnly: true,
|
|
616
|
-
});
|
|
617
613
|
const props = defineProps({
|
|
618
614
|
headers: {
|
|
619
615
|
type: Array as PropType<Array<Record<string, any>>>,
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
persistent-placeholder
|
|
47
47
|
:density="density"
|
|
48
48
|
:disabled="props.disabled"
|
|
49
|
-
:placeholder="placeholder
|
|
49
|
+
:placeholder="placeholder"
|
|
50
50
|
@focus="emit('focus')"
|
|
51
51
|
@blur="emit('blur')"
|
|
52
52
|
/>
|
|
@@ -104,7 +104,7 @@ const currentMask = computed(() => {
|
|
|
104
104
|
const country = countries.find(
|
|
105
105
|
(c: TPhoneMask) => c.code === selectedCode.value
|
|
106
106
|
);
|
|
107
|
-
if (!country) return "
|
|
107
|
+
if (!country) return "";
|
|
108
108
|
return generateMaskFromRegex(country.regex);
|
|
109
109
|
});
|
|
110
110
|
|
|
@@ -84,8 +84,16 @@
|
|
|
84
84
|
{{ formatDateInvited(item.dateInvited) }}
|
|
85
85
|
</template>
|
|
86
86
|
<template #item.action-table="{ item }">
|
|
87
|
+
<!--
|
|
88
|
+
`props.assignRole` belongs in this test. The menu's only item that
|
|
89
|
+
is not status-dependent is Assign Role, but the menu itself opened
|
|
90
|
+
only for suspend / activate / delete -- so a role granted
|
|
91
|
+
`members:assign-member-role` and nothing else got no menu at all,
|
|
92
|
+
and the permission was unreachable.
|
|
93
|
+
-->
|
|
87
94
|
<v-menu
|
|
88
95
|
v-if="
|
|
96
|
+
props.assignRole ||
|
|
89
97
|
(currentStatus === 'active' &&
|
|
90
98
|
(canSuspendMembers || canDeleteMembers)) ||
|
|
91
99
|
(currentStatus === 'suspended' &&
|
|
@@ -317,7 +317,12 @@ async function submit() {
|
|
|
317
317
|
});
|
|
318
318
|
emit("success");
|
|
319
319
|
} catch (error: any) {
|
|
320
|
-
|
|
320
|
+
// Same unguarded chain as the preview dialog had: on a network or timeout
|
|
321
|
+
// failure `error.response` is undefined and the catch threw inside itself,
|
|
322
|
+
// leaving the user with no message at all.
|
|
323
|
+
message.value =
|
|
324
|
+
error?.response?._data?.message ??
|
|
325
|
+
"Could not create this role. Please check your connection and try again.";
|
|
321
326
|
} finally {
|
|
322
327
|
disable.value = false;
|
|
323
328
|
}
|
|
@@ -99,15 +99,12 @@
|
|
|
99
99
|
</div>
|
|
100
100
|
</v-col>
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
</v-col>
|
|
109
|
-
|
|
110
|
-
<v-col cols="12" class="my-2">
|
|
102
|
+
<!--
|
|
103
|
+
One message, once. This block used to appear twice -- inside
|
|
104
|
+
`v-if="edit"` and again unconditionally -- so a refused save printed
|
|
105
|
+
the server's sentence back to back.
|
|
106
|
+
-->
|
|
107
|
+
<v-col v-if="message" cols="12" class="my-2">
|
|
111
108
|
<v-row no-gutters>
|
|
112
109
|
<v-col cols="12" class="text-center">
|
|
113
110
|
<span class="role-perm__error">{{ message }}</span>
|
|
@@ -122,7 +119,7 @@
|
|
|
122
119
|
<AppButton v-if="!edit" variant="ghost" @click="emit('cancel')">Close</AppButton>
|
|
123
120
|
<AppButton v-else variant="ghost" @click="edit = false">Cancel</AppButton>
|
|
124
121
|
|
|
125
|
-
<AppButton v-if="!edit" :disabled="disable" @click="edit = true">Edit</AppButton>
|
|
122
|
+
<AppButton v-if="!edit && canUpdateRole" :disabled="disable" @click="edit = true">Edit</AppButton>
|
|
126
123
|
<AppButton v-else :disabled="Boolean(errorMessages) || disable" @click="submit()">
|
|
127
124
|
Submit
|
|
128
125
|
</AppButton>
|
|
@@ -159,6 +156,15 @@ const props = defineProps({
|
|
|
159
156
|
type: Array as PropType<string[]>,
|
|
160
157
|
default: () => [],
|
|
161
158
|
},
|
|
159
|
+
// "Update role". This dialog had no permission prop at all, so it showed Edit
|
|
160
|
+
// to everyone -- including on the seeded platform-owner role -- and the
|
|
161
|
+
// `canUpdateRole` the apps have been passing to `RolePermissionMain` in good
|
|
162
|
+
// faith did nothing. `RolePermissionMain` is the only thing that renders this
|
|
163
|
+
// dialog and always passes it, so the default is closed.
|
|
164
|
+
canUpdateRole: {
|
|
165
|
+
type: Boolean,
|
|
166
|
+
default: false,
|
|
167
|
+
},
|
|
162
168
|
});
|
|
163
169
|
|
|
164
170
|
const validForm = ref(false);
|
|
@@ -282,7 +288,12 @@ async function submit() {
|
|
|
282
288
|
await updatePermissionById(props.id, definedModel.value);
|
|
283
289
|
emit("success");
|
|
284
290
|
} catch (error: any) {
|
|
285
|
-
|
|
291
|
+
// `error.response` is undefined on a network or timeout failure, so the old
|
|
292
|
+
// unguarded chain threw inside its own catch: nothing was shown, the button
|
|
293
|
+
// came back enabled, and the save looked like it had simply been ignored.
|
|
294
|
+
message.value =
|
|
295
|
+
error?.response?._data?.message ??
|
|
296
|
+
"Could not save this role. Please check your connection and try again.";
|
|
286
297
|
} finally {
|
|
287
298
|
disable.value = false;
|
|
288
299
|
}
|
|
@@ -42,12 +42,13 @@
|
|
|
42
42
|
|
|
43
43
|
<v-data-table
|
|
44
44
|
:headers="props.headers"
|
|
45
|
-
:items="
|
|
45
|
+
:items="visibleItems"
|
|
46
46
|
item-value="_id"
|
|
47
47
|
items-per-page="20"
|
|
48
48
|
fixed-header
|
|
49
49
|
hide-default-footer
|
|
50
50
|
hide-default-header
|
|
51
|
+
:no-data-text="emptyText"
|
|
51
52
|
@click:row="tableRowClickHandler"
|
|
52
53
|
style="max-height: calc(100vh - (180px))"
|
|
53
54
|
>
|
|
@@ -102,6 +103,7 @@
|
|
|
102
103
|
v-model:edit="edit"
|
|
103
104
|
:name="name"
|
|
104
105
|
:id="roleId"
|
|
106
|
+
:can-update-role="props.canUpdateRole"
|
|
105
107
|
:web-only-resources="props.webOnlyResources"
|
|
106
108
|
/>
|
|
107
109
|
</v-dialog>
|
|
@@ -245,13 +247,18 @@ const props = defineProps({
|
|
|
245
247
|
type: Boolean,
|
|
246
248
|
default: false,
|
|
247
249
|
},
|
|
250
|
+
// "See all roles". Defaults to `true` because nine of the ten pages that
|
|
251
|
+
// render this component do not pass it, and until now nothing read it at all
|
|
252
|
+
// -- so `true` is exactly what those pages have always had. The one page that
|
|
253
|
+
// does pass it now gets a gate that works.
|
|
248
254
|
canViewRole: {
|
|
249
255
|
type: Boolean,
|
|
250
|
-
default:
|
|
256
|
+
default: true,
|
|
251
257
|
},
|
|
258
|
+
// "See role details" -- opening a role. Same reasoning as `canViewRole`.
|
|
252
259
|
canViewByRole: {
|
|
253
260
|
type: Boolean,
|
|
254
|
-
default:
|
|
261
|
+
default: true,
|
|
255
262
|
},
|
|
256
263
|
canDeleteRole: {
|
|
257
264
|
type: Boolean,
|
|
@@ -281,6 +288,7 @@ const {
|
|
|
281
288
|
data: getRoleReq,
|
|
282
289
|
refresh: getRoles,
|
|
283
290
|
status: getRoleReqStatus,
|
|
291
|
+
error: getRoleReqError,
|
|
284
292
|
} = useLazyAsyncData(
|
|
285
293
|
"roles-permissions-get-all",
|
|
286
294
|
() =>
|
|
@@ -305,7 +313,33 @@ watchEffect(() => {
|
|
|
305
313
|
}
|
|
306
314
|
});
|
|
307
315
|
|
|
316
|
+
// "See all roles". Without it the list is not drawn at all, and `emptyText`
|
|
317
|
+
// below says so rather than leaving a blank table to be read as a failure.
|
|
318
|
+
const visibleItems = computed(() => (props.canViewRole ? items.value : []));
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* SAY WHY THE TABLE IS EMPTY. "No data available" was shown for three
|
|
322
|
+
* different situations -- you may not see roles, the request failed, and this
|
|
323
|
+
* organisation genuinely has none -- so a refusal was indistinguishable from an
|
|
324
|
+
* empty list and neither was distinguishable from a broken screen.
|
|
325
|
+
*/
|
|
326
|
+
const emptyText = computed(() => {
|
|
327
|
+
if (!props.canViewRole) return "You do not have permission to view roles.";
|
|
328
|
+
const status = (getRoleReqError.value as any)?.response?.status
|
|
329
|
+
?? (getRoleReqError.value as any)?.statusCode;
|
|
330
|
+
if (status === 401 || status === 403) {
|
|
331
|
+
return "You do not have permission to view the roles for this organisation.";
|
|
332
|
+
}
|
|
333
|
+
if (getRoleReqError.value) {
|
|
334
|
+
return "Could not load roles. Please refresh to try again.";
|
|
335
|
+
}
|
|
336
|
+
return "No roles have been created yet.";
|
|
337
|
+
});
|
|
338
|
+
|
|
308
339
|
function tableRowClickHandler(_: any, data: any) {
|
|
340
|
+
// "See role details". Ungated until now, which is why granting or withholding
|
|
341
|
+
// it changed nothing.
|
|
342
|
+
if (!props.canViewByRole) return;
|
|
309
343
|
previewDialog.value = true;
|
|
310
344
|
roleId.value = data.item._id;
|
|
311
345
|
}
|
|
@@ -2,7 +2,12 @@ import { useCommonPermissions } from "./useCommonPermission";
|
|
|
2
2
|
|
|
3
3
|
export function useBulletinBoardPermission() {
|
|
4
4
|
const { hasPermission } = usePermission();
|
|
5
|
-
|
|
5
|
+
// Same nesting rule as every other permission composable: `hasPermission`
|
|
6
|
+
// reads `catalogue[resource][action]`, and `bulletinBoardPermissions` is a
|
|
7
|
+
// flat `{action: ...}` map. Unwrapped, `catalogue["bulletin-board"]` was
|
|
8
|
+
// undefined and all five gates were reachable only through `"*"`.
|
|
9
|
+
const { bulletinBoardPermissions } = useCommonPermissions();
|
|
10
|
+
const permissions: TPermissions = { "bulletin-board": bulletinBoardPermissions };
|
|
6
11
|
|
|
7
12
|
const { userAppRole } = useLocalSetup();
|
|
8
13
|
|
|
@@ -2,7 +2,13 @@ import { useCommonPermissions } from "./useCommonPermission";
|
|
|
2
2
|
|
|
3
3
|
export function useSettingsPermission() {
|
|
4
4
|
const { hasPermission } = usePermission();
|
|
5
|
-
|
|
5
|
+
// `hasPermission` looks the action up as `catalogue[resource][action]`, so the
|
|
6
|
+
// catalogue has to be nested by resource. `siteSettingsPermissions` is a flat
|
|
7
|
+
// `{action: ...}` map, so passing it straight through made
|
|
8
|
+
// `catalogue["site-settings"]` undefined and every check below fell through to
|
|
9
|
+
// `false` -- only the `"*"` short-circuit could ever open a panel.
|
|
10
|
+
const { siteSettingsPermissions } = useCommonPermissions();
|
|
11
|
+
const permissions: TPermissions = { "site-settings": siteSettingsPermissions };
|
|
6
12
|
const { userAppRole } = useLocalSetup();
|
|
7
13
|
|
|
8
14
|
const canManageSiteInformation = computed(() => {
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@7365admin1/layer-common",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "4.0.
|
|
5
|
+
"version": "4.0.3-staging.221",
|
|
6
6
|
"author": "7365admin1",
|
|
7
7
|
"main": "./nuxt.config.ts",
|
|
8
8
|
"//files": "What a consumer extending this layer actually loads. Without this npm ships the whole working tree - the changesets, the CI workflows, the render harness in tools/ and any scratch directory that happened to exist at publish time. Nuxt resolves a layer by directory, so every runtime directory below has to stay listed; adding a new top-level runtime directory means adding it here too.",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"build": "nuxt build .playground",
|
|
36
36
|
"generate": "nuxt generate .playground",
|
|
37
37
|
"preview": "nuxt preview .playground",
|
|
38
|
-
"test": "esbuild composables/useVisitorSocket.ts --format=esm --outfile=test/.build/useVisitorSocket.mjs --log-level=error && node --test \"test/*.test.mjs\" && yarn test:units",
|
|
38
|
+
"test": "esbuild composables/useVisitorSocket.ts --format=esm --outfile=test/.build/useVisitorSocket.mjs --log-level=error && node --experimental-strip-types --test \"test/*.test.mjs\" && yarn test:units",
|
|
39
39
|
"test:units": "node --experimental-strip-types --test \"utils/*.test.ts\"",
|
|
40
40
|
"release": "yarn run build && changeset publish"
|
|
41
41
|
},
|