@7365admin1/layer-common 4.149.2 → 4.151.0
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/CHANGELOG.md +16 -4
- package/components/EntryPassInformation.vue +13 -0
- package/components/HidAccessPermissions.vue +202 -40
- package/components/HidEnabledGate.vue +29 -10
- package/components/HidIntercomManagement.vue +26 -13
- package/components/HidProfileQrCode.vue +6 -2
- package/components/HidQrCodeConfiguration.vue +2 -2
- package/components/HidReaderForm.vue +239 -1
- package/components/HidReaderManagement.vue +26 -8
- package/components/HidReaderUserRoster.vue +3 -1
- package/components/HidServiceSettingsPanel.vue +2 -2
- package/components/HidUserEnrollment.vue +1662 -162
- package/components/OvernightParkingAvailability.vue +123 -11
- package/components/Robot/Detail.vue +23 -4
- package/components/SiteSettings.vue +5 -2
- package/components/VisitorForm.vue +39 -11
- package/composables/useCleaningPermission.ts +11 -0
- package/composables/useHidAccessPermission.ts +176 -81
- package/composables/useHidAmico.ts +37 -0
- package/composables/useHidNavigation.ts +44 -23
- package/composables/useLandscapingPermission.ts +11 -0
- package/composables/useMechanicalElectricalPermission.ts +5 -0
- package/composables/usePestControlPermission.ts +11 -0
- package/composables/usePoolMaintenancePermission.ts +11 -0
- package/composables/usePropertyPermission.ts +31 -7
- package/composables/useSecurityPermission.ts +33 -13
- package/composables/useServiceProvider.ts +9 -4
- package/constants/permissions.ts +73 -21
- package/package.json +1 -1
- package/pages/[org]/[site]/access-mgmt/hid-qr-code/index.vue +3 -0
- package/types/overnight-parking.d.ts +14 -0
- package/types/site.d.ts +7 -1
- package/utils/hid-enrolment-subject.ts +226 -4
- package/utils/hid-permission-assignments.ts +75 -6
- package/utils/hid-qr-validity.ts +25 -0
- package/utils/hid-reader-audiences.ts +557 -0
- package/utils/hid-registration.ts +24 -0
- package/utils/module-routes.ts +15 -2
- package/utils/nav-sections.ts +1 -1
- package/utils/robot-credential-form.ts +4 -2
- package/utils/robot-display.ts +28 -6
- package/utils/robot-setup.ts +1 -0
|
@@ -49,6 +49,60 @@
|
|
|
49
49
|
Disabling keeps the reader in Standalone mode and uses manual log pulls instead.
|
|
50
50
|
</p>
|
|
51
51
|
|
|
52
|
+
<!-- WHO THIS READER IS FOR.
|
|
53
|
+
A site with several doors rarely wants every door open to everybody:
|
|
54
|
+
a service entrance for contractors, a lobby for residents. Switching
|
|
55
|
+
an audience off stops new grants in that category - the server
|
|
56
|
+
refuses them, so this is not only a hint - and takes the matching tab
|
|
57
|
+
off the Access Permissions screen.
|
|
58
|
+
Each switch says what it DOES rather than only what it is called: a
|
|
59
|
+
row of four labels with no sentences is a guess about what they
|
|
60
|
+
control. -->
|
|
61
|
+
<InputLabel title="Who may use this reader" required class="mb-1" />
|
|
62
|
+
<div class="reader-audiences mb-1">
|
|
63
|
+
<div
|
|
64
|
+
v-for="field in audienceFields"
|
|
65
|
+
:key="field.value"
|
|
66
|
+
class="reader-audience"
|
|
67
|
+
>
|
|
68
|
+
<v-switch
|
|
69
|
+
v-model="audiences[field.value]"
|
|
70
|
+
:label="field.label"
|
|
71
|
+
:aria-label="field.label"
|
|
72
|
+
hide-details
|
|
73
|
+
inset
|
|
74
|
+
density="compact"
|
|
75
|
+
:color="audiences[field.value] ? 'success' : 'muted'"
|
|
76
|
+
/>
|
|
77
|
+
<p class="reader-audience__hint">{{ field.hint }}</p>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
<!-- WHAT THIS SAVE TAKES AWAY.
|
|
81
|
+
Switching an account type off revokes the people already granted
|
|
82
|
+
this reader - their credentials stay on the device, so they go from
|
|
83
|
+
authorised to recognised-and-refused and back again if it is switched
|
|
84
|
+
on later. Said with a number, because "removes access" and "removes
|
|
85
|
+
access for 40 people" are different decisions. -->
|
|
86
|
+
<p v-if="revocationWarning" class="reader-discovery-hint reader-audiences__revoking mb-1">
|
|
87
|
+
{{ revocationWarning }}
|
|
88
|
+
</p>
|
|
89
|
+
<!-- And whether anybody is left with a door at all. -->
|
|
90
|
+
<p v-if="strandedWarning" class="reader-discovery-hint reader-audiences__empty mb-1">
|
|
91
|
+
{{ strandedWarning }}
|
|
92
|
+
</p>
|
|
93
|
+
<p v-if="needsConfirmation && revocationConfirmed" class="reader-discovery-hint reader-audiences__revoking mb-3">
|
|
94
|
+
Press Save again to apply this.
|
|
95
|
+
</p>
|
|
96
|
+
|
|
97
|
+
<p v-if="noAudienceSelected" class="reader-discovery-hint reader-audiences__empty mb-3">
|
|
98
|
+
Nobody can be enrolled on this reader while all four are off. Existing
|
|
99
|
+
people keep the access they already have.
|
|
100
|
+
</p>
|
|
101
|
+
<p v-else class="reader-discovery-hint mt-1 mb-3">
|
|
102
|
+
Switching one off stops new people of that kind being given this reader.
|
|
103
|
+
It does not remove anybody who already has it.
|
|
104
|
+
</p>
|
|
105
|
+
|
|
52
106
|
<InputLabel title="Username" required class="mb-1" />
|
|
53
107
|
<v-text-field
|
|
54
108
|
v-model="draft.username"
|
|
@@ -206,7 +260,7 @@
|
|
|
206
260
|
:disabled="mode === 'add' && (!draft.deviceId || !draft.portalId || draft.cardReadingMode === 'unconfigured')"
|
|
207
261
|
@click="submit"
|
|
208
262
|
>
|
|
209
|
-
|
|
263
|
+
{{ submitLabel }}
|
|
210
264
|
</v-btn>
|
|
211
265
|
</v-card-actions>
|
|
212
266
|
</v-card>
|
|
@@ -214,6 +268,18 @@
|
|
|
214
268
|
</template>
|
|
215
269
|
|
|
216
270
|
<script setup lang="ts">
|
|
271
|
+
/* Mirrors core's `hid-reader-audience.util.ts`, which is what enforces this —
|
|
272
|
+
kept in a util with tests because the absent-means-all default is the part
|
|
273
|
+
that could close a door by accident. */
|
|
274
|
+
import {
|
|
275
|
+
audienceRevocations,
|
|
276
|
+
audiencesLeftWithNoReader,
|
|
277
|
+
HID_READER_AUDIENCE_FIELDS,
|
|
278
|
+
readerAudiences,
|
|
279
|
+
type THidPermissionCounts,
|
|
280
|
+
type THidReaderAudiences,
|
|
281
|
+
} from "../utils/hid-reader-audiences";
|
|
282
|
+
|
|
217
283
|
type HidReaderFormRecord = {
|
|
218
284
|
name?: string;
|
|
219
285
|
baseUrl?: string;
|
|
@@ -248,6 +314,7 @@ type HidReaderFormPayload = {
|
|
|
248
314
|
pacsFormat: "raw" | "wiegand";
|
|
249
315
|
operatingMode: "standalone" | "online";
|
|
250
316
|
monitorEnabled: boolean;
|
|
317
|
+
audiences: THidReaderAudiences;
|
|
251
318
|
};
|
|
252
319
|
|
|
253
320
|
const props = defineProps({
|
|
@@ -278,6 +345,23 @@ const props = defineProps({
|
|
|
278
345
|
type: String,
|
|
279
346
|
default: "",
|
|
280
347
|
},
|
|
348
|
+
// Which organisation's permission document to read the granted counts from.
|
|
349
|
+
// The reader's audiences are the reader's, but who is granted under them is
|
|
350
|
+
// per organisation - see `getSitePermissions`.
|
|
351
|
+
org: {
|
|
352
|
+
type: String,
|
|
353
|
+
default: "",
|
|
354
|
+
},
|
|
355
|
+
/*
|
|
356
|
+
* Every reader at this site, so switching the LAST one serving an account type
|
|
357
|
+
* can be warned about. Passed in rather than fetched: the parent already holds
|
|
358
|
+
* the list it draws the table from, and a second request would be a second
|
|
359
|
+
* answer that could disagree with what is on screen.
|
|
360
|
+
*/
|
|
361
|
+
siteReaders: {
|
|
362
|
+
type: Array as () => Array<Record<string, unknown>>,
|
|
363
|
+
default: () => [],
|
|
364
|
+
},
|
|
281
365
|
});
|
|
282
366
|
|
|
283
367
|
const emit = defineEmits<{
|
|
@@ -314,6 +398,112 @@ const draft = reactive({
|
|
|
314
398
|
operatingMode: "standalone" as "standalone" | "online",
|
|
315
399
|
});
|
|
316
400
|
|
|
401
|
+
/*
|
|
402
|
+
* WHO THIS READER IS FOR. All four on by default, which is the same reading core
|
|
403
|
+
* gives an absent field - a reader being added should not quietly be closed to
|
|
404
|
+
* somebody because a switch defaulted off.
|
|
405
|
+
*/
|
|
406
|
+
const audiences = reactive<THidReaderAudiences>({
|
|
407
|
+
resident: true,
|
|
408
|
+
residentVisitor: true,
|
|
409
|
+
member: true,
|
|
410
|
+
serviceProvider: true,
|
|
411
|
+
});
|
|
412
|
+
const audienceFields = HID_READER_AUDIENCE_FIELDS;
|
|
413
|
+
|
|
414
|
+
/*
|
|
415
|
+
* WHAT THE READER LOOKED LIKE WHEN THE DIALOG OPENED.
|
|
416
|
+
*
|
|
417
|
+
* Only an on-to-off change costs anybody their access, so the warnings compare
|
|
418
|
+
* against this rather than against the stored record - which the switches are
|
|
419
|
+
* already editing.
|
|
420
|
+
*/
|
|
421
|
+
const audiencesOnOpen = ref<THidReaderAudiences>({ ...audiences });
|
|
422
|
+
|
|
423
|
+
/** Who is granted on this reader, per category. Read once per opening. */
|
|
424
|
+
const grantedCounts = ref<THidPermissionCounts | null>(null);
|
|
425
|
+
const { getSitePermissions } = useHidAmico();
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* NEVER THROWS, and a failure means the counts are simply unknown.
|
|
429
|
+
*
|
|
430
|
+
* The warning below says how many people a switch-off costs; without the counts
|
|
431
|
+
* it says that it will remove access without a number, which is still the thing
|
|
432
|
+
* the operator needs to know. Failing the dialog over a count would be worse.
|
|
433
|
+
*/
|
|
434
|
+
async function loadGrantedCounts() {
|
|
435
|
+
grantedCounts.value = null;
|
|
436
|
+
const readerId = String(props.reader?._id ?? "");
|
|
437
|
+
if (!readerId || !props.site || !props.org) return;
|
|
438
|
+
try {
|
|
439
|
+
const response = await getSitePermissions(props.site, props.org, readerId);
|
|
440
|
+
grantedCounts.value = (response?.data?.counts ?? null) as THidPermissionCounts | null;
|
|
441
|
+
} catch (error: unknown) {
|
|
442
|
+
console.error("Unable to read who is granted on this reader:", error);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/** The account types this edit switches off, and what each costs. */
|
|
447
|
+
const revocations = computed(() => audienceRevocations(
|
|
448
|
+
audiencesOnOpen.value,
|
|
449
|
+
audiences,
|
|
450
|
+
grantedCounts.value,
|
|
451
|
+
));
|
|
452
|
+
|
|
453
|
+
/** Account types this edit would leave with no reader anywhere at the site. */
|
|
454
|
+
const strandedAudiences = computed(() => audiencesLeftWithNoReader(
|
|
455
|
+
props.reader?._id,
|
|
456
|
+
audiences,
|
|
457
|
+
props.siteReaders,
|
|
458
|
+
));
|
|
459
|
+
|
|
460
|
+
const revocationWarning = computed(() => {
|
|
461
|
+
if (!revocations.value.length) return "";
|
|
462
|
+
return revocations.value
|
|
463
|
+
.map((item) => {
|
|
464
|
+
if (item.audience === "residentVisitor") {
|
|
465
|
+
// Not a revocation: a visitor pass is bound through `user_access_rules`
|
|
466
|
+
// with its own expiry, so issued passes run out rather than stopping.
|
|
467
|
+
return `${item.label}: no new passes will be issued. Passes already issued run out on their own.`;
|
|
468
|
+
}
|
|
469
|
+
if (!item.people) {
|
|
470
|
+
return `${item.label}: nobody is currently granted this reader under it.`;
|
|
471
|
+
}
|
|
472
|
+
return `${item.label}: removes access for ${item.people} ${item.people === 1 ? "person" : "people"}.`;
|
|
473
|
+
})
|
|
474
|
+
.join(" ");
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
const strandedWarning = computed(() => {
|
|
478
|
+
if (!strandedAudiences.value.length) return "";
|
|
479
|
+
const labels = strandedAudiences.value.map((item) => item.label);
|
|
480
|
+
const subject = labels.length === 1 ? labels[0] : labels.join(", ");
|
|
481
|
+
return `No other reader at this site serves ${subject}.`
|
|
482
|
+
+ ` Saving this leaves ${labels.length === 1 ? "that account type" : "those account types"} with no way in.`;
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
/*
|
|
486
|
+
* A SECOND PRESS when something is being taken away.
|
|
487
|
+
*
|
|
488
|
+
* Revoking forty people is not an edit to make on one click, and a dialog over
|
|
489
|
+
* a dialog for a switch is heavier than it needs to be. Save states what it is
|
|
490
|
+
* about to do and then does it.
|
|
491
|
+
*/
|
|
492
|
+
const revocationConfirmed = ref(false);
|
|
493
|
+
const needsConfirmation = computed(
|
|
494
|
+
() => Boolean(revocations.value.length || strandedAudiences.value.length)
|
|
495
|
+
);
|
|
496
|
+
const submitLabel = computed(() => (
|
|
497
|
+
needsConfirmation.value && !revocationConfirmed.value
|
|
498
|
+
? "Review and save"
|
|
499
|
+
: props.mode === "edit" ? "Save Changes" : "Add Reader"
|
|
500
|
+
));
|
|
501
|
+
/* Switching every audience off leaves a reader nobody can be enrolled on, which
|
|
502
|
+
is legal but almost never meant - so it is said out loud rather than refused. */
|
|
503
|
+
const noAudienceSelected = computed(() =>
|
|
504
|
+
HID_READER_AUDIENCE_FIELDS.every((field) => audiences[field.value] === false)
|
|
505
|
+
);
|
|
506
|
+
|
|
317
507
|
const cardModeOptions = [
|
|
318
508
|
{ title: "PACS (facility code + card number)", value: "pacs" },
|
|
319
509
|
{ title: "CSN", value: "csn" },
|
|
@@ -352,6 +542,14 @@ watch(
|
|
|
352
542
|
discoveryError.value = "";
|
|
353
543
|
discoveredConnection.value = "";
|
|
354
544
|
draft.location = props.reader?.location ?? "";
|
|
545
|
+
// Read through `readerAudiences`, so a reader saved before this field
|
|
546
|
+
// existed opens with all four on rather than all four off - and a save from
|
|
547
|
+
// this form therefore cannot close it by accident.
|
|
548
|
+
Object.assign(audiences, readerAudiences(props.reader as Record<string, unknown> | null));
|
|
549
|
+
// The baseline the warnings compare against, and the counts behind them.
|
|
550
|
+
audiencesOnOpen.value = { ...audiences };
|
|
551
|
+
revocationConfirmed.value = false;
|
|
552
|
+
void loadGrantedCounts();
|
|
355
553
|
draft.username = props.reader?.username ?? "";
|
|
356
554
|
draft.password = props.reader?.password ?? "";
|
|
357
555
|
monitorEnabled.value = props.reader?.monitorStatus !== "disabled";
|
|
@@ -446,11 +644,22 @@ function submit() {
|
|
|
446
644
|
discoveryError.value = "Select the model printed on the physical HID reader.";
|
|
447
645
|
return;
|
|
448
646
|
}
|
|
647
|
+
/*
|
|
648
|
+
* The first press on a save that takes access away only acknowledges it. The
|
|
649
|
+
* warnings are already on screen; this makes the operator read them.
|
|
650
|
+
*/
|
|
651
|
+
if (needsConfirmation.value && !revocationConfirmed.value) {
|
|
652
|
+
revocationConfirmed.value = true;
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
449
655
|
const portal = portals.value.find((item) => item.id === Number(draft.portalId));
|
|
450
656
|
const payload: HidReaderFormPayload = {
|
|
451
657
|
...draft,
|
|
452
658
|
portalName: portal?.name || draft.portalName,
|
|
453
659
|
monitorEnabled: monitorEnabled.value,
|
|
660
|
+
// Spread, not passed by reference: the payload leaves this dialog and the
|
|
661
|
+
// reactive object keeps being edited behind it.
|
|
662
|
+
audiences: { ...audiences },
|
|
454
663
|
};
|
|
455
664
|
|
|
456
665
|
emit("submit", payload);
|
|
@@ -462,6 +671,35 @@ function submit() {
|
|
|
462
671
|
overflow: hidden;
|
|
463
672
|
}
|
|
464
673
|
|
|
674
|
+
/* Four rows rather than a row of four: each switch carries a sentence, and at
|
|
675
|
+
phone width a horizontal strip would either wrap badly or clip the hints. */
|
|
676
|
+
.reader-audiences {
|
|
677
|
+
display: flex;
|
|
678
|
+
flex-direction: column;
|
|
679
|
+
gap: 2px;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
.reader-audience__hint {
|
|
683
|
+
margin: -4px 0 0;
|
|
684
|
+
padding-left: 2px;
|
|
685
|
+
color: var(--muted);
|
|
686
|
+
font-size: 12px;
|
|
687
|
+
line-height: 1.4;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/* Said in the warning colour, not refused: a reader nobody may use is legal and
|
|
691
|
+
occasionally deliberate - a door being decommissioned. */
|
|
692
|
+
.reader-audiences__empty {
|
|
693
|
+
color: var(--err);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
/* A revocation is a warning rather than an error: it is a legitimate thing to
|
|
697
|
+
do, deliberately, and the colour should not imply the save is refused. */
|
|
698
|
+
.reader-audiences__revoking {
|
|
699
|
+
color: var(--warn, var(--err));
|
|
700
|
+
font-weight: 600;
|
|
701
|
+
}
|
|
702
|
+
|
|
465
703
|
/* Type comes from `.screen-modal .v-card-title`; only the header strip's own
|
|
466
704
|
padding and rule stay here. `#ececec` was a fixed near-white line, i.e.
|
|
467
705
|
invisible on a light card and a bright scratch across a dark one. */
|
|
@@ -29,7 +29,10 @@
|
|
|
29
29
|
show-header
|
|
30
30
|
@refresh="loadReaders"
|
|
31
31
|
>
|
|
32
|
-
|
|
32
|
+
<!-- Hidden, not disabled, for a member without the right: the server
|
|
33
|
+
refuses the call anyway, and a button that cannot work is noise.
|
|
34
|
+
`hid-mgmt:manage-hid-readers` (`useHidAccessPermission`). -->
|
|
35
|
+
<template v-if="canManageHidReaders" #actions>
|
|
33
36
|
<AppButton icon="mdi-plus" @click="openAdd">Add HID Reader</AppButton>
|
|
34
37
|
</template>
|
|
35
38
|
|
|
@@ -85,14 +88,23 @@
|
|
|
85
88
|
<AppButton v-bind="menuProps" variant="row" icon="mdi-dots-vertical" />
|
|
86
89
|
</template>
|
|
87
90
|
|
|
91
|
+
<!-- View is the view right. Access Permissions assigns people to the
|
|
92
|
+
reader's group, so it is the users right; everything else acts on
|
|
93
|
+
the reader itself and is the readers right. -->
|
|
88
94
|
<v-list density="compact" min-width="180">
|
|
89
95
|
<v-list-item title="View" @click="openView(item)" />
|
|
90
|
-
<v-list-item
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
<v-
|
|
96
|
+
<v-list-item
|
|
97
|
+
v-if="canManageHidUsers"
|
|
98
|
+
title="Access Permissions"
|
|
99
|
+
@click="openPermissions(item)"
|
|
100
|
+
/>
|
|
101
|
+
<template v-if="canManageHidReaders">
|
|
102
|
+
<v-list-item title="Identification Methods" @click="openIdentification(item)" />
|
|
103
|
+
<v-list-item title="Sync HID Reader" @click="openSyncConfirm(item)" />
|
|
104
|
+
<v-list-item title="Test Connection" @click="openTestConfirm(item)" />
|
|
105
|
+
<v-list-item title="Edit" @click="openEdit(item)" />
|
|
106
|
+
<v-list-item title="Delete" class="text-error" @click="openDeleteConfirm(item)" />
|
|
107
|
+
</template>
|
|
96
108
|
</v-list>
|
|
97
109
|
</v-menu>
|
|
98
110
|
</template>
|
|
@@ -111,8 +123,10 @@
|
|
|
111
123
|
<HidReaderForm
|
|
112
124
|
v-model="formDialog"
|
|
113
125
|
:site="props.site"
|
|
126
|
+
:org="props.org"
|
|
114
127
|
:mode="selectedReader ? 'edit' : 'add'"
|
|
115
128
|
:reader="selectedReader"
|
|
129
|
+
:site-readers="readers"
|
|
116
130
|
:loading="saving"
|
|
117
131
|
@submit="saveReader"
|
|
118
132
|
/>
|
|
@@ -134,7 +148,7 @@
|
|
|
134
148
|
<span>Username</span><strong>{{ selectedReader?.username || "N/A" }}</strong>
|
|
135
149
|
</div>
|
|
136
150
|
|
|
137
|
-
<div class="dialog-actions-inline">
|
|
151
|
+
<div v-if="canManageHidReaders" class="dialog-actions-inline">
|
|
138
152
|
<AppButton variant="ghost" @click="openTestConfirm(selectedReader)">Test Connection</AppButton>
|
|
139
153
|
<AppButton variant="ghost" @click="openSyncConfirm(selectedReader)">Sync Reader</AppButton>
|
|
140
154
|
</div>
|
|
@@ -479,6 +493,10 @@ const props = defineProps({
|
|
|
479
493
|
},
|
|
480
494
|
});
|
|
481
495
|
|
|
496
|
+
// Which buttons this member gets. The screen itself is behind `HidEnabledGate`
|
|
497
|
+
// (the view right); these two decide the write actions on it.
|
|
498
|
+
const { canManageHidReaders, canManageHidUsers } = useHidAccessPermission();
|
|
499
|
+
|
|
482
500
|
const {
|
|
483
501
|
getReaders,
|
|
484
502
|
createReader,
|
|
@@ -68,7 +68,8 @@
|
|
|
68
68
|
</span>
|
|
69
69
|
</template>
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
<!-- Setting a PIN is the users right; the roster itself is the view right. -->
|
|
72
|
+
<template v-if="canManageHidUsers" #[`item.action-table`]="{ item }">
|
|
72
73
|
<AppButton
|
|
73
74
|
variant="row"
|
|
74
75
|
icon="mdi-form-textbox-password"
|
|
@@ -199,6 +200,7 @@ type HidReaderUser = Record<string, unknown> & {
|
|
|
199
200
|
metadata?: HidMetadata;
|
|
200
201
|
};
|
|
201
202
|
|
|
203
|
+
const { canManageHidUsers } = useHidAccessPermission();
|
|
202
204
|
const {
|
|
203
205
|
getReaders,
|
|
204
206
|
getReaderUsers,
|
|
@@ -115,7 +115,7 @@ function buildHidServices(enabled: boolean) {
|
|
|
115
115
|
|
|
116
116
|
const service = {
|
|
117
117
|
key: "hid_amico",
|
|
118
|
-
name: "HID
|
|
118
|
+
name: "HID Reader",
|
|
119
119
|
enabled,
|
|
120
120
|
status: enabled ? "active" : "inactive",
|
|
121
121
|
};
|
|
@@ -154,7 +154,7 @@ async function saveHidService(value: boolean) {
|
|
|
154
154
|
hidEnabled.value = value;
|
|
155
155
|
emit("refresh");
|
|
156
156
|
await refreshNuxtData(`layout-selected-site-details-${useRuntimeConfig().public.APP}`);
|
|
157
|
-
showMessage(`HID
|
|
157
|
+
showMessage(`HID Reader ${value ? "enabled" : "disabled"} successfully`, "success");
|
|
158
158
|
} catch (error: unknown) {
|
|
159
159
|
hidEnabled.value = previousValue;
|
|
160
160
|
showMessage(errorConverter(error), "error");
|