@7365admin1/layer-common 3.2.2-staging.84 → 3.2.2-staging.86
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/screens.css +471 -0
- package/components/AttendanceMain.vue +4 -3
- package/components/CleaningScheduleMain.vue +29 -46
- package/components/HidIntercomManagement.vue +40 -24
- package/components/HidQrCodeConfiguration.vue +24 -2
- package/components/HidUserEnrollment.vue +68 -31
- package/components/InvitationMain.vue +10 -8
- package/components/MemberMain.vue +9 -6
- package/components/MyAttendanceMain.vue +3 -5
- package/components/RolePermissionMain.vue +15 -10
- package/components/ScheduleTaskMain.vue +23 -26
- package/components/ServiceProviderMain.vue +17 -42
- package/components/StatusChip.vue +16 -2
- package/components/TableMain.vue +0 -133
- package/components/VisitorForm.vue +100 -7
- package/components/VisitorManagement.vue +170 -2
- package/composables/useHidAmico.ts +26 -2
- package/composables/useSipWebPhone.ts +28 -3
- package/package.json +1 -1
- package/plugins/vuetify.ts +1 -0
- package/utils/status.test.ts +36 -0
- package/utils/status.ts +36 -0
|
@@ -33,7 +33,7 @@ type HidVisitorQrData = {
|
|
|
33
33
|
readerId: string;
|
|
34
34
|
hidUserId: number;
|
|
35
35
|
qrValue: string;
|
|
36
|
-
qrFormat: "0"
|
|
36
|
+
qrFormat: "0";
|
|
37
37
|
issuedAt: string;
|
|
38
38
|
expiresAt: string;
|
|
39
39
|
};
|
|
@@ -120,7 +120,7 @@ export default function useHidAmico() {
|
|
|
120
120
|
|
|
121
121
|
function issueVisitorQr(
|
|
122
122
|
readerId: string,
|
|
123
|
-
payload: { visitorId: string; validityMinutes: number
|
|
123
|
+
payload: { visitorId: string; validityMinutes: number },
|
|
124
124
|
) {
|
|
125
125
|
return useNuxtApp().$api<HidVisitorQrResponse>(`${basePath}/readers/${readerId}/visitor-qr`, {
|
|
126
126
|
method: "POST",
|
|
@@ -189,6 +189,27 @@ export default function useHidAmico() {
|
|
|
189
189
|
);
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
+
function getUserPinStatus(readerId: string, hidUserId: string | number) {
|
|
193
|
+
return useNuxtApp().$api<{ data: { pinEnrolled: boolean } }>(
|
|
194
|
+
`${basePath}/readers/${readerId}/users/${hidUserId}/pin`,
|
|
195
|
+
{ method: "GET" },
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function setUserPin(readerId: string, hidUserId: string | number, pin: string) {
|
|
200
|
+
return useNuxtApp().$api<{ data: { pinEnrolled: boolean } }>(
|
|
201
|
+
`${basePath}/readers/${readerId}/users/${hidUserId}/pin`,
|
|
202
|
+
{ method: "PUT", body: { pin } },
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function deleteUserPin(readerId: string, hidUserId: string | number) {
|
|
207
|
+
return useNuxtApp().$api<{ data: { pinEnrolled: boolean } }>(
|
|
208
|
+
`${basePath}/readers/${readerId}/users/${hidUserId}/pin`,
|
|
209
|
+
{ method: "DELETE" },
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
192
213
|
function createIdentity(readerId: string, payload: HidIdentityPayload) {
|
|
193
214
|
return useNuxtApp().$api<Record<string, any>>(`${basePath}/readers/${readerId}/identities`, {
|
|
194
215
|
method: "POST",
|
|
@@ -286,6 +307,9 @@ export default function useHidAmico() {
|
|
|
286
307
|
getUserImage,
|
|
287
308
|
setUserImage,
|
|
288
309
|
deleteUserImage,
|
|
310
|
+
getUserPinStatus,
|
|
311
|
+
setUserPin,
|
|
312
|
+
deleteUserPin,
|
|
289
313
|
getIdentities,
|
|
290
314
|
createIdentity,
|
|
291
315
|
updateIdentity,
|
|
@@ -31,6 +31,12 @@ export default function useSipWebPhone() {
|
|
|
31
31
|
|
|
32
32
|
const isRegistered = computed(() => connectionState.value === "registered");
|
|
33
33
|
const hasCall = computed(() => callState.value !== "idle");
|
|
34
|
+
const audioConstraints: MediaTrackConstraints = {
|
|
35
|
+
echoCancellation: true,
|
|
36
|
+
noiseSuppression: true,
|
|
37
|
+
autoGainControl: true,
|
|
38
|
+
channelCount: 1,
|
|
39
|
+
};
|
|
34
40
|
|
|
35
41
|
async function connect(config: SipWebPhoneConfig, media: SipWebPhoneMedia) {
|
|
36
42
|
validateConfig(config);
|
|
@@ -73,6 +79,7 @@ export default function useSipWebPhone() {
|
|
|
73
79
|
},
|
|
74
80
|
onCallAnswered: () => {
|
|
75
81
|
callState.value = "active";
|
|
82
|
+
ensureMicrophoneSending();
|
|
76
83
|
syncVideoState();
|
|
77
84
|
},
|
|
78
85
|
onCallHangup: () => {
|
|
@@ -104,7 +111,9 @@ export default function useSipWebPhone() {
|
|
|
104
111
|
aor: normalizeSipUri(config.aor),
|
|
105
112
|
delegate,
|
|
106
113
|
media: {
|
|
107
|
-
|
|
114
|
+
// The browser sends microphone audio and receives HID audio/video.
|
|
115
|
+
// Do not request a local camera merely because the HID video leg is enabled.
|
|
116
|
+
constraints: { audio: audioConstraints, video: false },
|
|
108
117
|
remote: {
|
|
109
118
|
audio: media.remoteAudio || undefined,
|
|
110
119
|
video: media.remoteVideo || undefined,
|
|
@@ -167,7 +176,7 @@ export default function useSipWebPhone() {
|
|
|
167
176
|
callState.value = "calling";
|
|
168
177
|
try {
|
|
169
178
|
const sessionDescriptionHandlerOptions: SessionDescriptionHandlerOptions = {
|
|
170
|
-
constraints: { audio:
|
|
179
|
+
constraints: { audio: audioConstraints, video: false },
|
|
171
180
|
offerOptions: {
|
|
172
181
|
offerToReceiveAudio: true,
|
|
173
182
|
offerToReceiveVideo: Boolean(activeConfig.value?.video),
|
|
@@ -184,7 +193,11 @@ export default function useSipWebPhone() {
|
|
|
184
193
|
async function answer() {
|
|
185
194
|
const user = requireRegisteredUser();
|
|
186
195
|
const sessionDescriptionHandlerOptions: SessionDescriptionHandlerOptions = {
|
|
187
|
-
constraints: { audio:
|
|
196
|
+
constraints: { audio: audioConstraints, video: false },
|
|
197
|
+
offerOptions: {
|
|
198
|
+
offerToReceiveAudio: true,
|
|
199
|
+
offerToReceiveVideo: Boolean(activeConfig.value?.video),
|
|
200
|
+
},
|
|
188
201
|
};
|
|
189
202
|
await user.answer({ sessionDescriptionHandlerOptions });
|
|
190
203
|
}
|
|
@@ -214,6 +227,18 @@ export default function useSipWebPhone() {
|
|
|
214
227
|
muted.value = !enable;
|
|
215
228
|
}
|
|
216
229
|
|
|
230
|
+
function ensureMicrophoneSending() {
|
|
231
|
+
const tracks = simpleUser.value?.localMediaStream?.getAudioTracks() || [];
|
|
232
|
+
if (!tracks.length) {
|
|
233
|
+
errorMessage.value = "The call connected without a microphone track. Reconnect and allow microphone access.";
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
tracks.forEach((track) => {
|
|
237
|
+
track.enabled = true;
|
|
238
|
+
});
|
|
239
|
+
muted.value = false;
|
|
240
|
+
}
|
|
241
|
+
|
|
217
242
|
function syncVideoState() {
|
|
218
243
|
const user = simpleUser.value;
|
|
219
244
|
const remoteStream = user?.remoteMediaStream;
|
package/package.json
CHANGED
package/plugins/vuetify.ts
CHANGED
package/utils/status.test.ts
CHANGED
|
@@ -35,6 +35,42 @@ test("an unmapped status comes out neutral rather than guessed", () => {
|
|
|
35
35
|
}
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
+
/**
|
|
39
|
+
* PHASE 4. Each of these words is on a module screen today with a colour a
|
|
40
|
+
* per-screen `getStatusColor` gave it. The list has to keep giving it the SAME
|
|
41
|
+
* tone, or adopting the shared chip would quietly repaint a status.
|
|
42
|
+
*/
|
|
43
|
+
test("the product's own status words keep the tone the screens already gave them", () => {
|
|
44
|
+
for (const s of ["Accepted", "Approved", "Resolved", "Active"]) {
|
|
45
|
+
assert.equal(statusTone(s), "ok", s);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
for (const s of ["Rejected", "Deleted", "Suspended"]) {
|
|
49
|
+
assert.equal(statusTone(s), "err", s);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
for (const s of ["In Progress", "In-Progress", "To-Do", "Awaiting Approval"]) {
|
|
53
|
+
assert.equal(statusTone(s), "warn", s);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
for (const s of ["Ongoing", "For Review"]) {
|
|
57
|
+
assert.equal(statusTone(s), "info", s);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
for (const s of ["Inactive", "Expired"]) {
|
|
61
|
+
assert.equal(statusTone(s), "neutral", s);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The one place the handoff and the old per-screen colour disagree: `Open` was
|
|
67
|
+
* grey on the cleaning schedules and the design names it `ok`. Pinned so the
|
|
68
|
+
* disagreement is a decision on the record rather than a surprise.
|
|
69
|
+
*/
|
|
70
|
+
test("Open follows the design, not the old grey", () => {
|
|
71
|
+
assert.equal(statusTone("open"), "ok");
|
|
72
|
+
});
|
|
73
|
+
|
|
38
74
|
test("the class name matches what tokens.css actually defines", () => {
|
|
39
75
|
assert.equal(statusToneClass("Paid"), "tone-ok");
|
|
40
76
|
assert.equal(statusToneClass("Whatever"), "tone-neutral");
|
package/utils/status.ts
CHANGED
|
@@ -34,6 +34,42 @@ const TONES: Record<string, TStatusTone> = {
|
|
|
34
34
|
|
|
35
35
|
role: "info",
|
|
36
36
|
info: "info",
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* PHASE 4 - THE WORDS THIS PRODUCT ACTUALLY USES.
|
|
40
|
+
*
|
|
41
|
+
* The handoff names ten words; the module screens show more than that, and
|
|
42
|
+
* each of them is ALREADY painted a colour today by a per-screen
|
|
43
|
+
* `getStatusColor`. So these are not new opinions - each one is the tone the
|
|
44
|
+
* screens already give that word, moved into the one list so that "Ongoing"
|
|
45
|
+
* is the same blue on a cleaning schedule and on a scheduled area. Where the
|
|
46
|
+
* old per-screen colour and the design disagree the design wins, and there
|
|
47
|
+
* is exactly one of those: `Open` was grey and the handoff names it `ok`.
|
|
48
|
+
*
|
|
49
|
+
* Anything still unlisted stays neutral, deliberately - see above.
|
|
50
|
+
*/
|
|
51
|
+
ongoing: "info", // was `primary`
|
|
52
|
+
"in progress": "warn", // was #FB8C00, orange
|
|
53
|
+
"for review": "info", // was `primary`
|
|
54
|
+
"to do": "warn", // was `orange`
|
|
55
|
+
|
|
56
|
+
accepted: "ok", // was `success`
|
|
57
|
+
approved: "ok",
|
|
58
|
+
resolved: "ok", // was #4CAF50, green
|
|
59
|
+
active: "ok", // was `success`
|
|
60
|
+
|
|
61
|
+
rejected: "err", // was `error`
|
|
62
|
+
deleted: "err", // was `error`
|
|
63
|
+
suspended: "err", // was `error`
|
|
64
|
+
|
|
65
|
+
complete: "ok",
|
|
66
|
+
|
|
67
|
+
inactive: "neutral", // was `grey`
|
|
68
|
+
"in active": "neutral", // the same word, printed "In-Active"
|
|
69
|
+
declined: "neutral", // was Vuetify's `default`
|
|
70
|
+
expired: "neutral", // was `grey`
|
|
71
|
+
|
|
72
|
+
"awaiting approval": "warn", // a waiting state, like Pending
|
|
37
73
|
};
|
|
38
74
|
|
|
39
75
|
/**
|