@7365admin1/layer-common 3.1.4-staging.53 → 3.2.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 +6 -0
- package/components/CreateSubsciptionPlan.vue +376 -0
- package/components/DashboardMain.vue +16 -38
- package/components/EntryPassInformation.vue +4 -4
- package/components/HidAccessLogDashboard.vue +8 -53
- package/components/HidIdentityMapping.vue +975 -0
- package/components/HidIntercomManagement.vue +174 -621
- package/components/HidQrCodeConfiguration.vue +94 -742
- package/components/HidServiceSettingsPanel.vue +1 -20
- package/components/HidUserEnrollment.vue +0 -2
- package/components/Nfc/NFCPatrolRouteMain.vue +7 -52
- package/components/PlanActionsMenu.vue +33 -0
- package/components/PlanStatusBadge.vue +26 -0
- package/components/QrTemplate/CreditCardLandscape.vue +12 -19
- package/components/QrTemplate/PrintDialog.vue +196 -209
- package/components/SubscriptionPlanTable.vue +362 -0
- package/components/VisitorForm.vue +78 -201
- package/components/VisitorManagement.vue +1 -0
- package/composables/useEquipmentManagementPermission.ts +1 -1
- package/composables/useEquipmentPermission.ts +1 -1
- package/composables/useHidAmico.ts +0 -66
- package/composables/useHidNavigation.ts +7 -0
- package/composables/useWebUsb.ts +37 -127
- package/package.json +1 -2
- package/pages/[org]/[site]/access-mgmt/identity-mapping/index.vue +23 -0
- package/plugins/secure-member.client.ts +56 -21
- package/types/site.d.ts +0 -65
- package/types/subscription.ts +51 -0
- package/composables/useSipWebPhone.ts +0 -236
|
@@ -12,17 +12,7 @@
|
|
|
12
12
|
Add Contacts
|
|
13
13
|
</v-btn>
|
|
14
14
|
|
|
15
|
-
<v-
|
|
16
|
-
v-else
|
|
17
|
-
rounded="xl"
|
|
18
|
-
class="text-none web-call-button"
|
|
19
|
-
:class="{ 'is-connected': webPhone.isRegistered.value, 'has-incoming-call': webPhone.callState.value === 'incoming' }"
|
|
20
|
-
variant="flat"
|
|
21
|
-
prepend-icon="mdi-phone-in-talk-outline"
|
|
22
|
-
@click="openWebPhoneDialog()"
|
|
23
|
-
>
|
|
24
|
-
{{ webPhone.callState.value === 'incoming' ? 'Incoming Call' : 'Web Call' }}
|
|
25
|
-
</v-btn>
|
|
15
|
+
<div v-else class="top-placeholder" />
|
|
26
16
|
|
|
27
17
|
<div class="toolbar-spacer" />
|
|
28
18
|
|
|
@@ -115,7 +105,6 @@
|
|
|
115
105
|
<v-list-item title="Call Settings" @click="openCallDialog(row)" />
|
|
116
106
|
<v-list-item title="Dial Code" @click="openDialCodeDialog(row)" />
|
|
117
107
|
<v-list-item title="Make a call" @click="openMakeCallDialog(row)" />
|
|
118
|
-
<v-list-item title="Web call" @click="openWebPhoneDialog(row)" />
|
|
119
108
|
</v-list>
|
|
120
109
|
</v-menu>
|
|
121
110
|
</td>
|
|
@@ -242,6 +231,36 @@
|
|
|
242
231
|
</v-card>
|
|
243
232
|
</v-dialog>
|
|
244
233
|
|
|
234
|
+
<v-dialog v-model="rebootConfirmDialog" max-width="700" persistent>
|
|
235
|
+
<v-card class="reboot-dialog" rounded="0">
|
|
236
|
+
<button class="reboot-close" type="button" aria-label="Close" @click="rebootConfirmDialog = false">
|
|
237
|
+
<v-icon icon="mdi-close" />
|
|
238
|
+
</button>
|
|
239
|
+
<v-card-title class="reboot-title">Reboot is needed</v-card-title>
|
|
240
|
+
<v-card-text class="reboot-message">
|
|
241
|
+
It is necessary to restart the device after applying the configurations. Do you want to proceed?
|
|
242
|
+
</v-card-text>
|
|
243
|
+
<v-card-actions class="reboot-actions">
|
|
244
|
+
<v-btn class="text-none reboot-cancel" variant="text" @click="rebootConfirmDialog = false">
|
|
245
|
+
Cancel
|
|
246
|
+
</v-btn>
|
|
247
|
+
<v-btn class="text-none reboot-ok" variant="flat" @click="startRebootWait">
|
|
248
|
+
OK
|
|
249
|
+
</v-btn>
|
|
250
|
+
</v-card-actions>
|
|
251
|
+
</v-card>
|
|
252
|
+
</v-dialog>
|
|
253
|
+
|
|
254
|
+
<v-dialog v-model="rebootingDialog" max-width="900" persistent>
|
|
255
|
+
<v-card class="rebooting-card" rounded="0">
|
|
256
|
+
<div class="wait-word">Wait!</div>
|
|
257
|
+
<div class="rebooting-copy">
|
|
258
|
+
<h3>Rebooting Equipment</h3>
|
|
259
|
+
<p>Wait {{ rebootCountdown }} seconds for the equipment to reboot.</p>
|
|
260
|
+
</div>
|
|
261
|
+
</v-card>
|
|
262
|
+
</v-dialog>
|
|
263
|
+
|
|
245
264
|
<v-dialog v-model="callDialog" max-width="360" persistent>
|
|
246
265
|
<v-card class="form-dialog call-dialog" rounded="lg">
|
|
247
266
|
<v-card-title class="dialog-title">Call Settings</v-card-title>
|
|
@@ -331,10 +350,10 @@
|
|
|
331
350
|
<v-card-title class="dialog-title">Make a Call</v-card-title>
|
|
332
351
|
<v-card-text class="dialog-body make-call-body">
|
|
333
352
|
<div class="make-call-field">
|
|
334
|
-
<div class="call-label">
|
|
353
|
+
<div class="call-label">Address to dial</div>
|
|
335
354
|
<v-text-field
|
|
336
355
|
v-model="makeCallForm.target"
|
|
337
|
-
placeholder="
|
|
356
|
+
placeholder="Enter extension, address, or contact"
|
|
338
357
|
variant="outlined"
|
|
339
358
|
density="compact"
|
|
340
359
|
hide-details="auto"
|
|
@@ -369,192 +388,8 @@
|
|
|
369
388
|
</div>
|
|
370
389
|
</v-card-text>
|
|
371
390
|
<v-card-actions class="dialog-actions pa-0">
|
|
372
|
-
<v-btn class="text-none action-
|
|
373
|
-
|
|
374
|
-
</v-card>
|
|
375
|
-
</v-dialog>
|
|
376
|
-
|
|
377
|
-
<v-dialog v-model="webPhoneDialog" max-width="820" persistent eager>
|
|
378
|
-
<v-card class="web-phone-dialog" rounded="lg">
|
|
379
|
-
<v-card-title class="dialog-title web-phone-title">
|
|
380
|
-
<span>Web Call</span>
|
|
381
|
-
<v-chip size="small" variant="flat" :class="webPhoneStatusClass">
|
|
382
|
-
{{ webPhoneStatusLabel }}
|
|
383
|
-
</v-chip>
|
|
384
|
-
</v-card-title>
|
|
385
|
-
|
|
386
|
-
<v-card-text class="web-phone-body">
|
|
387
|
-
<div class="web-phone-settings">
|
|
388
|
-
<div class="web-phone-section-title">SIP Account</div>
|
|
389
|
-
<div class="call-label">WebSocket Server <span>*</span></div>
|
|
390
|
-
<v-text-field
|
|
391
|
-
v-model="webPhoneForm.webSocketServer"
|
|
392
|
-
placeholder="wss://sip.example.com/ws"
|
|
393
|
-
variant="outlined"
|
|
394
|
-
density="compact"
|
|
395
|
-
hide-details="auto"
|
|
396
|
-
class="call-input"
|
|
397
|
-
:disabled="webPhone.isRegistered.value"
|
|
398
|
-
/>
|
|
399
|
-
<div class="call-label">SIP Address <span>*</span></div>
|
|
400
|
-
<v-text-field
|
|
401
|
-
v-model="webPhoneForm.aor"
|
|
402
|
-
placeholder="operator@example.com"
|
|
403
|
-
variant="outlined"
|
|
404
|
-
density="compact"
|
|
405
|
-
hide-details="auto"
|
|
406
|
-
class="call-input"
|
|
407
|
-
:disabled="webPhone.isRegistered.value"
|
|
408
|
-
/>
|
|
409
|
-
<div class="form-two-col">
|
|
410
|
-
<div>
|
|
411
|
-
<div class="call-label">Username <span>*</span></div>
|
|
412
|
-
<v-text-field
|
|
413
|
-
v-model="webPhoneForm.username"
|
|
414
|
-
placeholder="Username"
|
|
415
|
-
variant="outlined"
|
|
416
|
-
density="compact"
|
|
417
|
-
hide-details="auto"
|
|
418
|
-
class="call-input"
|
|
419
|
-
:disabled="webPhone.isRegistered.value"
|
|
420
|
-
/>
|
|
421
|
-
</div>
|
|
422
|
-
<div>
|
|
423
|
-
<div class="call-label">Password <span>*</span></div>
|
|
424
|
-
<v-text-field
|
|
425
|
-
v-model="webPhoneForm.password"
|
|
426
|
-
placeholder="Password"
|
|
427
|
-
:type="showWebPhonePassword ? 'text' : 'password'"
|
|
428
|
-
:append-inner-icon="showWebPhonePassword ? 'mdi-eye-off' : 'mdi-eye'"
|
|
429
|
-
variant="outlined"
|
|
430
|
-
density="compact"
|
|
431
|
-
hide-details="auto"
|
|
432
|
-
class="call-input"
|
|
433
|
-
:disabled="webPhone.isRegistered.value"
|
|
434
|
-
@click:append-inner="showWebPhonePassword = !showWebPhonePassword"
|
|
435
|
-
/>
|
|
436
|
-
</div>
|
|
437
|
-
</div>
|
|
438
|
-
<div class="call-toggle-row web-video-toggle">
|
|
439
|
-
<span>Enable video</span>
|
|
440
|
-
<v-switch
|
|
441
|
-
v-model="webPhoneForm.video"
|
|
442
|
-
color="success"
|
|
443
|
-
density="compact"
|
|
444
|
-
hide-details
|
|
445
|
-
:disabled="webPhone.isRegistered.value"
|
|
446
|
-
/>
|
|
447
|
-
</div>
|
|
448
|
-
<v-btn
|
|
449
|
-
v-if="!webPhone.isRegistered.value"
|
|
450
|
-
block
|
|
451
|
-
class="text-none web-phone-connect"
|
|
452
|
-
variant="flat"
|
|
453
|
-
prepend-icon="mdi-lan-connect"
|
|
454
|
-
:loading="webPhoneConnecting"
|
|
455
|
-
@click="connectWebPhone"
|
|
456
|
-
>
|
|
457
|
-
Connect Web Phone
|
|
458
|
-
</v-btn>
|
|
459
|
-
<v-btn
|
|
460
|
-
v-else
|
|
461
|
-
block
|
|
462
|
-
class="text-none web-phone-disconnect"
|
|
463
|
-
variant="outlined"
|
|
464
|
-
prepend-icon="mdi-lan-disconnect"
|
|
465
|
-
:loading="webPhoneConnecting"
|
|
466
|
-
:disabled="webPhone.hasCall.value"
|
|
467
|
-
@click="disconnectWebPhone"
|
|
468
|
-
>
|
|
469
|
-
Disconnect
|
|
470
|
-
</v-btn>
|
|
471
|
-
</div>
|
|
472
|
-
|
|
473
|
-
<div class="web-phone-console">
|
|
474
|
-
<div class="web-phone-media" :class="{ 'has-video': webPhoneForm.video && webPhone.callState.value === 'active' }">
|
|
475
|
-
<video ref="remoteVideoEl" autoplay playsinline class="remote-video" />
|
|
476
|
-
<video ref="localVideoEl" autoplay muted playsinline class="local-video" />
|
|
477
|
-
<audio ref="remoteAudioEl" autoplay />
|
|
478
|
-
<div v-if="!webPhoneForm.video || webPhone.callState.value !== 'active'" class="audio-call-state">
|
|
479
|
-
<v-icon size="54">{{ webPhoneCallIcon }}</v-icon>
|
|
480
|
-
<strong>{{ webPhoneCallLabel }}</strong>
|
|
481
|
-
<span v-if="webPhone.currentTarget.value">{{ webPhone.currentTarget.value }}</span>
|
|
482
|
-
</div>
|
|
483
|
-
</div>
|
|
484
|
-
|
|
485
|
-
<div v-if="webPhone.callState.value === 'incoming'" class="incoming-call-actions">
|
|
486
|
-
<v-btn
|
|
487
|
-
class="text-none answer-call"
|
|
488
|
-
variant="flat"
|
|
489
|
-
prepend-icon="mdi-phone"
|
|
490
|
-
:loading="webPhoneActionLoading"
|
|
491
|
-
@click="answerWebCall"
|
|
492
|
-
>
|
|
493
|
-
Answer
|
|
494
|
-
</v-btn>
|
|
495
|
-
<v-btn
|
|
496
|
-
class="text-none reject-call"
|
|
497
|
-
variant="flat"
|
|
498
|
-
prepend-icon="mdi-phone-hangup"
|
|
499
|
-
:disabled="webPhoneActionLoading"
|
|
500
|
-
@click="declineWebCall"
|
|
501
|
-
>
|
|
502
|
-
Decline
|
|
503
|
-
</v-btn>
|
|
504
|
-
</div>
|
|
505
|
-
|
|
506
|
-
<template v-else>
|
|
507
|
-
<div class="call-label">Extension or SIP Address</div>
|
|
508
|
-
<div class="web-phone-dial-row">
|
|
509
|
-
<v-text-field
|
|
510
|
-
v-model="webPhoneForm.destination"
|
|
511
|
-
placeholder="1001 or device@example.com"
|
|
512
|
-
variant="outlined"
|
|
513
|
-
density="compact"
|
|
514
|
-
hide-details="auto"
|
|
515
|
-
class="call-input mb-0"
|
|
516
|
-
:disabled="!webPhone.isRegistered.value || webPhone.hasCall.value"
|
|
517
|
-
@keyup.enter="startWebCall"
|
|
518
|
-
/>
|
|
519
|
-
<v-btn
|
|
520
|
-
icon="mdi-phone"
|
|
521
|
-
class="start-web-call"
|
|
522
|
-
variant="flat"
|
|
523
|
-
:loading="webPhoneActionLoading && webPhone.callState.value === 'calling'"
|
|
524
|
-
:disabled="!webPhone.isRegistered.value || webPhone.hasCall.value || !webPhoneForm.destination.trim()"
|
|
525
|
-
@click="startWebCall"
|
|
526
|
-
/>
|
|
527
|
-
</div>
|
|
528
|
-
|
|
529
|
-
<div v-if="webPhone.hasCall.value" class="active-call-controls">
|
|
530
|
-
<v-btn
|
|
531
|
-
:icon="webPhone.muted.value ? 'mdi-microphone-off' : 'mdi-microphone'"
|
|
532
|
-
variant="outlined"
|
|
533
|
-
:disabled="webPhone.callState.value !== 'active'"
|
|
534
|
-
:title="webPhone.muted.value ? 'Unmute' : 'Mute'"
|
|
535
|
-
@click="webPhone.toggleMute"
|
|
536
|
-
/>
|
|
537
|
-
<v-btn
|
|
538
|
-
icon="mdi-phone-hangup"
|
|
539
|
-
class="hangup-web-call"
|
|
540
|
-
variant="flat"
|
|
541
|
-
:loading="webPhoneActionLoading && webPhone.callState.value === 'ending'"
|
|
542
|
-
title="End call"
|
|
543
|
-
@click="hangupWebCall"
|
|
544
|
-
/>
|
|
545
|
-
</div>
|
|
546
|
-
</template>
|
|
547
|
-
|
|
548
|
-
<v-alert v-if="webPhone.errorMessage.value" type="error" variant="tonal" density="compact" class="web-phone-error">
|
|
549
|
-
{{ webPhone.errorMessage.value }}
|
|
550
|
-
</v-alert>
|
|
551
|
-
</div>
|
|
552
|
-
</v-card-text>
|
|
553
|
-
|
|
554
|
-
<v-card-actions class="dialog-actions pa-0">
|
|
555
|
-
<v-btn class="text-none action-submit full-action" variant="flat" height="44" @click="webPhoneDialog = false">
|
|
556
|
-
Close
|
|
557
|
-
</v-btn>
|
|
391
|
+
<v-btn class="text-none action-cancel" variant="flat" height="44" @click="makeCallDialog = false">Cancel</v-btn>
|
|
392
|
+
<v-btn class="text-none action-submit" variant="flat" height="44" @click="makeCallDialog = false">Close</v-btn>
|
|
558
393
|
</v-card-actions>
|
|
559
394
|
</v-card>
|
|
560
395
|
</v-dialog>
|
|
@@ -696,25 +531,21 @@ const limit = 20;
|
|
|
696
531
|
const selectedRow = ref<IntercomRow | null>(null);
|
|
697
532
|
const selectedContact = ref<ContactRow | null>(null);
|
|
698
533
|
const showSipPassword = ref(false);
|
|
699
|
-
const showWebPhonePassword = ref(false);
|
|
700
|
-
const webPhoneConnecting = ref(false);
|
|
701
|
-
const webPhoneActionLoading = ref(false);
|
|
702
|
-
const localVideoEl = ref<HTMLVideoElement | null>(null);
|
|
703
|
-
const remoteVideoEl = ref<HTMLVideoElement | null>(null);
|
|
704
|
-
const remoteAudioEl = ref<HTMLAudioElement | null>(null);
|
|
705
|
-
const webPhone = useSipWebPhone();
|
|
706
534
|
|
|
707
535
|
const viewDialog = ref(false);
|
|
708
536
|
const sipDialog = ref(false);
|
|
537
|
+
const rebootConfirmDialog = ref(false);
|
|
538
|
+
const rebootingDialog = ref(false);
|
|
709
539
|
const callDialog = ref(false);
|
|
710
540
|
const dialCodeDialog = ref(false);
|
|
711
541
|
const makeCallDialog = ref(false);
|
|
712
|
-
const webPhoneDialog = ref(false);
|
|
713
542
|
const contactDialog = ref(false);
|
|
714
543
|
const contactViewDialog = ref(false);
|
|
715
544
|
const deleteContactDialog = ref(false);
|
|
716
545
|
|
|
717
546
|
const message = reactive<{ type: MessageType; text: string }>({ type: "info", text: "" });
|
|
547
|
+
const rebootCountdown = ref(59);
|
|
548
|
+
let rebootTimer: ReturnType<typeof setInterval> | undefined;
|
|
718
549
|
|
|
719
550
|
const statusOptions = ["Status", "Connected", "Not connected", "Connecting", "Disabled", "Failed"];
|
|
720
551
|
|
|
@@ -751,7 +582,7 @@ const sipForm = reactive({
|
|
|
751
582
|
serverIp: "",
|
|
752
583
|
serverPort: "5060",
|
|
753
584
|
initialRtpPort: "10000",
|
|
754
|
-
finalRtpPort: "
|
|
585
|
+
finalRtpPort: "1000",
|
|
755
586
|
numericExtension: false,
|
|
756
587
|
extension: "",
|
|
757
588
|
login: "",
|
|
@@ -786,15 +617,6 @@ const makeCallForm = reactive({
|
|
|
786
617
|
statusType: "info" as MessageType,
|
|
787
618
|
});
|
|
788
619
|
|
|
789
|
-
const webPhoneForm = reactive({
|
|
790
|
-
webSocketServer: "",
|
|
791
|
-
aor: "",
|
|
792
|
-
username: "",
|
|
793
|
-
password: "",
|
|
794
|
-
destination: "",
|
|
795
|
-
video: false,
|
|
796
|
-
});
|
|
797
|
-
|
|
798
620
|
const contactForm = reactive({
|
|
799
621
|
id: undefined as number | undefined,
|
|
800
622
|
readerId: "",
|
|
@@ -895,35 +717,6 @@ const contactDetailRows = computed<DetailItem[]>(() => [
|
|
|
895
717
|
{ label: "Server Address", value: selectedContact.value?.serverAddress, link: true },
|
|
896
718
|
{ label: "Location", value: selectedContact.value?.location },
|
|
897
719
|
]);
|
|
898
|
-
const webPhoneStatusLabel = computed(() => {
|
|
899
|
-
if (webPhone.callState.value === "incoming") return "Incoming call";
|
|
900
|
-
if (webPhone.callState.value === "calling") return "Calling";
|
|
901
|
-
if (webPhone.callState.value === "active") return "In call";
|
|
902
|
-
if (webPhone.callState.value === "ending") return "Ending call";
|
|
903
|
-
if (webPhone.connectionState.value === "connecting") return "Connecting";
|
|
904
|
-
if (webPhone.connectionState.value === "registered") return "Ready";
|
|
905
|
-
if (webPhone.connectionState.value === "error") return "Connection failed";
|
|
906
|
-
return "Disconnected";
|
|
907
|
-
});
|
|
908
|
-
const webPhoneStatusClass = computed(() => {
|
|
909
|
-
if (webPhone.callState.value === "incoming") return "web-phone-status incoming";
|
|
910
|
-
if (webPhone.callState.value === "active" || webPhone.connectionState.value === "registered") return "web-phone-status ready";
|
|
911
|
-
if (webPhone.connectionState.value === "error") return "web-phone-status failed";
|
|
912
|
-
return "web-phone-status idle";
|
|
913
|
-
});
|
|
914
|
-
const webPhoneCallLabel = computed(() => {
|
|
915
|
-
if (webPhone.callState.value === "incoming") return "Incoming call";
|
|
916
|
-
if (webPhone.callState.value === "calling") return "Calling...";
|
|
917
|
-
if (webPhone.callState.value === "active") return "Call connected";
|
|
918
|
-
if (webPhone.callState.value === "ending") return "Ending call...";
|
|
919
|
-
return webPhone.isRegistered.value ? "Ready to call" : "Connect a SIP account";
|
|
920
|
-
});
|
|
921
|
-
const webPhoneCallIcon = computed(() => {
|
|
922
|
-
if (webPhone.callState.value === "incoming") return "mdi-phone-incoming";
|
|
923
|
-
if (webPhone.callState.value === "calling") return "mdi-phone-outgoing";
|
|
924
|
-
if (webPhone.callState.value === "active") return "mdi-phone-in-talk";
|
|
925
|
-
return "mdi-phone-outline";
|
|
926
|
-
});
|
|
927
720
|
|
|
928
721
|
watch(
|
|
929
722
|
() => props.site,
|
|
@@ -939,12 +732,9 @@ watch(pages, () => {
|
|
|
939
732
|
if (page.value > pages.value) page.value = pages.value;
|
|
940
733
|
});
|
|
941
734
|
|
|
942
|
-
|
|
943
|
-
()
|
|
944
|
-
|
|
945
|
-
if (state === "incoming") webPhoneDialog.value = true;
|
|
946
|
-
},
|
|
947
|
-
);
|
|
735
|
+
onUnmounted(() => {
|
|
736
|
+
clearRebootTimer();
|
|
737
|
+
});
|
|
948
738
|
|
|
949
739
|
async function reload() {
|
|
950
740
|
if (!props.site) return;
|
|
@@ -1011,14 +801,6 @@ function paginate<T>(items: T[]) {
|
|
|
1011
801
|
function toIntercomRow(reader: Record<string, any>, pjsip: Record<string, any>, status: string): IntercomRow {
|
|
1012
802
|
const peerToPeer = toFlag(getPjsipValue(pjsip, ["peer_to_peer_enabled", "peerToPeerEnabled"])) === "1";
|
|
1013
803
|
const dialMode = toText(getPjsipValue(pjsip, ["dialing_display_mode", "dialingDisplayMode"]), "0");
|
|
1014
|
-
const initialRtpPort = toText(getPjsipValue(pjsip, ["server_outbound_port", "initialRtpPort", "initial_rtp_port"]), "N/A");
|
|
1015
|
-
const rtpPortRange = getPjsipValue(pjsip, ["server_outbound_port_range"]);
|
|
1016
|
-
const explicitFinalRtpPort = getPjsipValue(pjsip, ["finalRtpPort", "final_rtp_port"]);
|
|
1017
|
-
const initialRtpPortNumber = Number(initialRtpPort);
|
|
1018
|
-
const rtpPortRangeNumber = Number(rtpPortRange);
|
|
1019
|
-
const finalRtpPort = rtpPortRange !== undefined && Number.isFinite(initialRtpPortNumber) && Number.isFinite(rtpPortRangeNumber)
|
|
1020
|
-
? String(initialRtpPortNumber + rtpPortRangeNumber)
|
|
1021
|
-
: toText(explicitFinalRtpPort, "N/A");
|
|
1022
804
|
|
|
1023
805
|
return {
|
|
1024
806
|
reader,
|
|
@@ -1026,8 +808,8 @@ function toIntercomRow(reader: Record<string, any>, pjsip: Record<string, any>,
|
|
|
1026
808
|
location: reader.location || "N/A",
|
|
1027
809
|
serverAddress: toText(getPjsipValue(pjsip, ["server_ip", "serverAddress", "server_address", "serverIp"]), "N/A"),
|
|
1028
810
|
serverPort: toText(getPjsipValue(pjsip, ["server_port", "serverPort", "port"]), "N/A"),
|
|
1029
|
-
initialRtpPort,
|
|
1030
|
-
finalRtpPort,
|
|
811
|
+
initialRtpPort: toText(getPjsipValue(pjsip, ["server_outbound_port", "initialRtpPort", "initial_rtp_port"]), "N/A"),
|
|
812
|
+
finalRtpPort: toText(getPjsipValue(pjsip, ["server_outbound_port_range", "finalRtpPort", "final_rtp_port"]), "N/A"),
|
|
1031
813
|
monitorPath: reader.monitorPath || "N/A",
|
|
1032
814
|
status,
|
|
1033
815
|
sipEnabled: toFlag(getPjsipValue(pjsip, ["enabled"])) === "1",
|
|
@@ -1098,8 +880,8 @@ function getPjsipValue(source: Record<string, any>, keys: string[]) {
|
|
|
1098
880
|
|
|
1099
881
|
function normalizeStatus(response: Record<string, any>, pjsip: Record<string, any>) {
|
|
1100
882
|
const status = Number(response?.status);
|
|
1101
|
-
if (toFlag(pjsip.enabled) === "0" || status === -1) return "Disabled";
|
|
1102
883
|
if (status === 200) return "Connected";
|
|
884
|
+
if (status === -1 || toFlag(pjsip.enabled) === "0") return "Disabled";
|
|
1103
885
|
if (status === 0 || status === 100) return "Connecting";
|
|
1104
886
|
if ([401, 403, 408, 503].includes(status)) return "Failed";
|
|
1105
887
|
return "Not connected";
|
|
@@ -1129,7 +911,7 @@ function openSipDialog(row: IntercomRow) {
|
|
|
1129
911
|
sipForm.serverIp = row.serverAddress === "N/A" ? "" : row.serverAddress;
|
|
1130
912
|
sipForm.serverPort = row.serverPort === "N/A" ? "5060" : row.serverPort;
|
|
1131
913
|
sipForm.initialRtpPort = row.initialRtpPort === "N/A" ? "10000" : row.initialRtpPort;
|
|
1132
|
-
sipForm.finalRtpPort = row.finalRtpPort === "N/A" ? "
|
|
914
|
+
sipForm.finalRtpPort = row.finalRtpPort === "N/A" ? "1000" : row.finalRtpPort;
|
|
1133
915
|
sipForm.numericExtension = row.numericExtension;
|
|
1134
916
|
sipForm.extension = row.extension === "N/A" ? "" : row.extension;
|
|
1135
917
|
sipForm.login = row.login === "N/A" ? "" : row.login;
|
|
@@ -1163,100 +945,12 @@ function openDialCodeDialog(row: IntercomRow) {
|
|
|
1163
945
|
|
|
1164
946
|
function openMakeCallDialog(row: IntercomRow) {
|
|
1165
947
|
selectedRow.value = row;
|
|
1166
|
-
makeCallForm.target =
|
|
948
|
+
makeCallForm.target = row.extension !== "N/A" ? row.extension : "";
|
|
1167
949
|
makeCallForm.status = "";
|
|
1168
950
|
makeCallForm.statusType = "info";
|
|
1169
951
|
makeCallDialog.value = true;
|
|
1170
952
|
}
|
|
1171
953
|
|
|
1172
|
-
function openWebPhoneDialog(row?: IntercomRow) {
|
|
1173
|
-
const activeRow = row || selectedRow.value || intercomRows.value[0];
|
|
1174
|
-
if (activeRow) {
|
|
1175
|
-
selectedRow.value = activeRow;
|
|
1176
|
-
if (!webPhone.isRegistered.value && activeRow.extension !== "N/A") {
|
|
1177
|
-
webPhoneForm.destination = activeRow.extension;
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
webPhoneDialog.value = true;
|
|
1181
|
-
}
|
|
1182
|
-
|
|
1183
|
-
async function connectWebPhone() {
|
|
1184
|
-
webPhoneConnecting.value = true;
|
|
1185
|
-
try {
|
|
1186
|
-
await webPhone.connect({
|
|
1187
|
-
webSocketServer: webPhoneForm.webSocketServer,
|
|
1188
|
-
aor: webPhoneForm.aor,
|
|
1189
|
-
authorizationUsername: webPhoneForm.username,
|
|
1190
|
-
authorizationPassword: webPhoneForm.password,
|
|
1191
|
-
displayName: "iService365 Web",
|
|
1192
|
-
video: webPhoneForm.video,
|
|
1193
|
-
}, {
|
|
1194
|
-
localVideo: localVideoEl.value,
|
|
1195
|
-
remoteVideo: remoteVideoEl.value,
|
|
1196
|
-
remoteAudio: remoteAudioEl.value,
|
|
1197
|
-
});
|
|
1198
|
-
showMessage("Web phone connected and ready to receive calls.", "success");
|
|
1199
|
-
} catch (error: any) {
|
|
1200
|
-
showMessage(getErrorMessage(error), "error");
|
|
1201
|
-
} finally {
|
|
1202
|
-
webPhoneConnecting.value = false;
|
|
1203
|
-
}
|
|
1204
|
-
}
|
|
1205
|
-
|
|
1206
|
-
async function disconnectWebPhone() {
|
|
1207
|
-
webPhoneConnecting.value = true;
|
|
1208
|
-
try {
|
|
1209
|
-
await webPhone.disconnect();
|
|
1210
|
-
showMessage("Web phone disconnected.", "info");
|
|
1211
|
-
} finally {
|
|
1212
|
-
webPhoneConnecting.value = false;
|
|
1213
|
-
}
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
async function startWebCall() {
|
|
1217
|
-
webPhoneActionLoading.value = true;
|
|
1218
|
-
try {
|
|
1219
|
-
await webPhone.call(webPhoneForm.destination);
|
|
1220
|
-
} catch (error: any) {
|
|
1221
|
-
showMessage(getErrorMessage(error), "error");
|
|
1222
|
-
} finally {
|
|
1223
|
-
webPhoneActionLoading.value = false;
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
|
|
1227
|
-
async function answerWebCall() {
|
|
1228
|
-
webPhoneActionLoading.value = true;
|
|
1229
|
-
try {
|
|
1230
|
-
await webPhone.answer();
|
|
1231
|
-
} catch (error: any) {
|
|
1232
|
-
showMessage(getErrorMessage(error), "error");
|
|
1233
|
-
} finally {
|
|
1234
|
-
webPhoneActionLoading.value = false;
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
|
|
1238
|
-
async function declineWebCall() {
|
|
1239
|
-
webPhoneActionLoading.value = true;
|
|
1240
|
-
try {
|
|
1241
|
-
await webPhone.decline();
|
|
1242
|
-
} catch (error: any) {
|
|
1243
|
-
showMessage(getErrorMessage(error), "error");
|
|
1244
|
-
} finally {
|
|
1245
|
-
webPhoneActionLoading.value = false;
|
|
1246
|
-
}
|
|
1247
|
-
}
|
|
1248
|
-
|
|
1249
|
-
async function hangupWebCall() {
|
|
1250
|
-
webPhoneActionLoading.value = true;
|
|
1251
|
-
try {
|
|
1252
|
-
await webPhone.hangup();
|
|
1253
|
-
} catch (error: any) {
|
|
1254
|
-
showMessage(getErrorMessage(error), "error");
|
|
1255
|
-
} finally {
|
|
1256
|
-
webPhoneActionLoading.value = false;
|
|
1257
|
-
}
|
|
1258
|
-
}
|
|
1259
|
-
|
|
1260
954
|
function openContactDialog(contact?: ContactRow) {
|
|
1261
955
|
selectedContact.value = contact || null;
|
|
1262
956
|
contactForm.id = contact?.id;
|
|
@@ -1280,34 +974,21 @@ function openDeleteContact(contact: ContactRow) {
|
|
|
1280
974
|
async function saveSipSettings() {
|
|
1281
975
|
if (!selectedRow.value) return;
|
|
1282
976
|
const isPeerToPeer = sipForm.operatingMode === "peer-to-peer";
|
|
1283
|
-
const initialRtpPort = Number(sipForm.initialRtpPort);
|
|
1284
|
-
const finalRtpPort = Number(sipForm.finalRtpPort);
|
|
1285
|
-
|
|
1286
|
-
if (
|
|
1287
|
-
!Number.isInteger(initialRtpPort) ||
|
|
1288
|
-
!Number.isInteger(finalRtpPort) ||
|
|
1289
|
-
initialRtpPort < 1 ||
|
|
1290
|
-
finalRtpPort > 65535 ||
|
|
1291
|
-
finalRtpPort <= initialRtpPort
|
|
1292
|
-
) {
|
|
1293
|
-
showMessage("Final RTP Port must be greater than Initial RTP Port and both ports must be between 1 and 65535.", "warning");
|
|
1294
|
-
return;
|
|
1295
|
-
}
|
|
1296
|
-
|
|
1297
977
|
const saved = await savePjsip(selectedRow.value.reader._id, {
|
|
1298
978
|
enabled: sipForm.enabled ? "1" : "0",
|
|
1299
979
|
server_ip: sipForm.serverIp,
|
|
1300
980
|
server_port: sipForm.serverPort,
|
|
1301
|
-
server_outbound_port:
|
|
1302
|
-
server_outbound_port_range:
|
|
981
|
+
server_outbound_port: sipForm.initialRtpPort,
|
|
982
|
+
server_outbound_port_range: sipForm.finalRtpPort,
|
|
1303
983
|
numeric_branch_enabled: sipForm.numericExtension ? "1" : "0",
|
|
1304
984
|
branch: isPeerToPeer ? "" : sipForm.extension,
|
|
1305
985
|
login: isPeerToPeer ? "" : sipForm.login,
|
|
1306
986
|
password: isPeerToPeer ? "" : sipForm.password,
|
|
1307
987
|
peer_to_peer_enabled: isPeerToPeer ? "1" : "0",
|
|
1308
|
-
});
|
|
988
|
+
}, { showSuccessMessage: false });
|
|
1309
989
|
if (saved) {
|
|
1310
990
|
sipDialog.value = false;
|
|
991
|
+
rebootConfirmDialog.value = true;
|
|
1311
992
|
}
|
|
1312
993
|
}
|
|
1313
994
|
|
|
@@ -1339,13 +1020,7 @@ async function runMakeCall() {
|
|
|
1339
1020
|
if (!selectedRow.value) return;
|
|
1340
1021
|
const target = makeCallForm.target.trim();
|
|
1341
1022
|
if (!target) {
|
|
1342
|
-
makeCallForm.status = "Please enter an
|
|
1343
|
-
makeCallForm.statusType = "warning";
|
|
1344
|
-
return;
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1347
|
-
if (!isIpAddress(target)) {
|
|
1348
|
-
makeCallForm.status = "Address to dial must be an IP address.";
|
|
1023
|
+
makeCallForm.status = "Please enter an address to dial.";
|
|
1349
1024
|
makeCallForm.statusType = "warning";
|
|
1350
1025
|
return;
|
|
1351
1026
|
}
|
|
@@ -1367,15 +1042,6 @@ async function runMakeCall() {
|
|
|
1367
1042
|
}
|
|
1368
1043
|
}
|
|
1369
1044
|
|
|
1370
|
-
function isIpAddress(value = "") {
|
|
1371
|
-
const parts = value.trim().split(".");
|
|
1372
|
-
return parts.length === 4 && parts.every((part) => {
|
|
1373
|
-
if (!/^\d{1,3}$/.test(part)) return false;
|
|
1374
|
-
const value = Number(part);
|
|
1375
|
-
return value >= 0 && value <= 255;
|
|
1376
|
-
});
|
|
1377
|
-
}
|
|
1378
|
-
|
|
1379
1045
|
async function runHangupCall() {
|
|
1380
1046
|
if (!selectedRow.value) return;
|
|
1381
1047
|
hangingUp.value = true;
|
|
@@ -1412,6 +1078,30 @@ async function savePjsip(readerId: string, payload: Record<string, string>, opti
|
|
|
1412
1078
|
}
|
|
1413
1079
|
}
|
|
1414
1080
|
|
|
1081
|
+
function startRebootWait() {
|
|
1082
|
+
rebootConfirmDialog.value = false;
|
|
1083
|
+
rebootCountdown.value = 59;
|
|
1084
|
+
rebootingDialog.value = true;
|
|
1085
|
+
clearRebootTimer();
|
|
1086
|
+
|
|
1087
|
+
rebootTimer = setInterval(() => {
|
|
1088
|
+
rebootCountdown.value -= 1;
|
|
1089
|
+
if (rebootCountdown.value <= 0) {
|
|
1090
|
+
clearRebootTimer();
|
|
1091
|
+
rebootingDialog.value = false;
|
|
1092
|
+
showMessage("Equipment reboot wait completed.", "success");
|
|
1093
|
+
reload();
|
|
1094
|
+
}
|
|
1095
|
+
}, 1000);
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
function clearRebootTimer() {
|
|
1099
|
+
if (rebootTimer) {
|
|
1100
|
+
clearInterval(rebootTimer);
|
|
1101
|
+
rebootTimer = undefined;
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1415
1105
|
async function saveContact() {
|
|
1416
1106
|
if (!contactForm.readerId || !contactForm.name.trim() || !contactForm.serverAddress.trim()) {
|
|
1417
1107
|
showMessage("Please complete the required contact fields.", "warning");
|
|
@@ -1569,23 +1259,6 @@ export default {
|
|
|
1569
1259
|
width: 160px;
|
|
1570
1260
|
}
|
|
1571
1261
|
|
|
1572
|
-
.web-call-button {
|
|
1573
|
-
min-width: 160px;
|
|
1574
|
-
background: #e5e7eb;
|
|
1575
|
-
color: #0b2c40;
|
|
1576
|
-
font-size: 12px;
|
|
1577
|
-
}
|
|
1578
|
-
|
|
1579
|
-
.web-call-button.is-connected {
|
|
1580
|
-
background: #d9f2e1;
|
|
1581
|
-
color: #16743a;
|
|
1582
|
-
}
|
|
1583
|
-
|
|
1584
|
-
.web-call-button.has-incoming-call {
|
|
1585
|
-
background: #1769aa;
|
|
1586
|
-
color: #fff;
|
|
1587
|
-
}
|
|
1588
|
-
|
|
1589
1262
|
.add-button {
|
|
1590
1263
|
min-width: 160px;
|
|
1591
1264
|
background: #dedede;
|
|
@@ -1957,6 +1630,98 @@ export default {
|
|
|
1957
1630
|
overflow: hidden;
|
|
1958
1631
|
}
|
|
1959
1632
|
|
|
1633
|
+
.reboot-dialog {
|
|
1634
|
+
position: relative;
|
|
1635
|
+
min-height: 220px;
|
|
1636
|
+
overflow: hidden;
|
|
1637
|
+
background: #fff;
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
.reboot-close {
|
|
1641
|
+
position: absolute;
|
|
1642
|
+
top: 14px;
|
|
1643
|
+
right: 14px;
|
|
1644
|
+
display: grid;
|
|
1645
|
+
width: 40px;
|
|
1646
|
+
height: 40px;
|
|
1647
|
+
padding: 0;
|
|
1648
|
+
border: 0;
|
|
1649
|
+
place-items: center;
|
|
1650
|
+
background: transparent;
|
|
1651
|
+
color: #c7c7c7;
|
|
1652
|
+
cursor: pointer;
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
.reboot-title {
|
|
1656
|
+
padding: 24px 20px 12px;
|
|
1657
|
+
color: #111;
|
|
1658
|
+
font-size: 28px;
|
|
1659
|
+
font-weight: 400;
|
|
1660
|
+
letter-spacing: 0;
|
|
1661
|
+
line-height: 1.2;
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
.reboot-message {
|
|
1665
|
+
padding: 24px 20px 28px;
|
|
1666
|
+
color: #222;
|
|
1667
|
+
font-size: 17px;
|
|
1668
|
+
line-height: 1.45;
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
.reboot-actions {
|
|
1672
|
+
display: flex;
|
|
1673
|
+
justify-content: flex-end;
|
|
1674
|
+
gap: 20px;
|
|
1675
|
+
padding: 0 20px 20px;
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
.reboot-cancel {
|
|
1679
|
+
color: #075fb7;
|
|
1680
|
+
font-size: 16px;
|
|
1681
|
+
letter-spacing: 0;
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
.reboot-ok {
|
|
1685
|
+
min-width: 64px;
|
|
1686
|
+
border-radius: 5px;
|
|
1687
|
+
background: #062f4d;
|
|
1688
|
+
color: #fff;
|
|
1689
|
+
font-size: 16px;
|
|
1690
|
+
letter-spacing: 0;
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
.rebooting-card {
|
|
1694
|
+
display: grid;
|
|
1695
|
+
min-height: 240px;
|
|
1696
|
+
grid-template-columns: minmax(0, 1.1fr) minmax(280px, 0.9fr);
|
|
1697
|
+
align-items: center;
|
|
1698
|
+
gap: 24px;
|
|
1699
|
+
padding: 20px 54px;
|
|
1700
|
+
background: #fff;
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
.wait-word {
|
|
1704
|
+
color: #347bd6;
|
|
1705
|
+
font-size: 120px;
|
|
1706
|
+
font-weight: 400;
|
|
1707
|
+
letter-spacing: 0;
|
|
1708
|
+
line-height: 0.95;
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
.rebooting-copy h3 {
|
|
1712
|
+
margin: 0 0 18px;
|
|
1713
|
+
color: #000;
|
|
1714
|
+
font-size: 30px;
|
|
1715
|
+
font-weight: 400;
|
|
1716
|
+
letter-spacing: 0;
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
.rebooting-copy p {
|
|
1720
|
+
margin: 0;
|
|
1721
|
+
color: #000;
|
|
1722
|
+
font-size: 16px;
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1960
1725
|
.call-dialog .dialog-body {
|
|
1961
1726
|
padding: 18px 24px 24px;
|
|
1962
1727
|
}
|
|
@@ -2166,209 +1931,6 @@ export default {
|
|
|
2166
1931
|
line-height: 1.45;
|
|
2167
1932
|
}
|
|
2168
1933
|
|
|
2169
|
-
.web-phone-dialog {
|
|
2170
|
-
width: 820px;
|
|
2171
|
-
max-width: 94vw;
|
|
2172
|
-
overflow: hidden;
|
|
2173
|
-
background: #fff;
|
|
2174
|
-
}
|
|
2175
|
-
|
|
2176
|
-
.web-phone-title {
|
|
2177
|
-
display: flex;
|
|
2178
|
-
align-items: center;
|
|
2179
|
-
justify-content: space-between;
|
|
2180
|
-
gap: 16px;
|
|
2181
|
-
}
|
|
2182
|
-
|
|
2183
|
-
.web-phone-status {
|
|
2184
|
-
min-width: 92px;
|
|
2185
|
-
justify-content: center;
|
|
2186
|
-
font-size: 10px;
|
|
2187
|
-
}
|
|
2188
|
-
|
|
2189
|
-
.web-phone-status.ready {
|
|
2190
|
-
background: #d9f2e1;
|
|
2191
|
-
color: #16743a;
|
|
2192
|
-
}
|
|
2193
|
-
|
|
2194
|
-
.web-phone-status.incoming {
|
|
2195
|
-
background: #dcecff;
|
|
2196
|
-
color: #125c9a;
|
|
2197
|
-
}
|
|
2198
|
-
|
|
2199
|
-
.web-phone-status.failed {
|
|
2200
|
-
background: #fce2e2;
|
|
2201
|
-
color: #a92222;
|
|
2202
|
-
}
|
|
2203
|
-
|
|
2204
|
-
.web-phone-status.idle {
|
|
2205
|
-
background: #e5e7eb;
|
|
2206
|
-
color: #5d6673;
|
|
2207
|
-
}
|
|
2208
|
-
|
|
2209
|
-
.web-phone-body {
|
|
2210
|
-
display: grid;
|
|
2211
|
-
grid-template-columns: minmax(280px, 0.9fr) minmax(320px, 1.1fr);
|
|
2212
|
-
gap: 24px;
|
|
2213
|
-
min-height: 440px;
|
|
2214
|
-
padding: 22px 24px 24px;
|
|
2215
|
-
}
|
|
2216
|
-
|
|
2217
|
-
.web-phone-settings {
|
|
2218
|
-
padding-right: 24px;
|
|
2219
|
-
border-right: 1px solid #e5e7eb;
|
|
2220
|
-
}
|
|
2221
|
-
|
|
2222
|
-
.web-phone-section-title {
|
|
2223
|
-
margin-bottom: 14px;
|
|
2224
|
-
color: #0d1720;
|
|
2225
|
-
font-size: 13px;
|
|
2226
|
-
font-weight: 700;
|
|
2227
|
-
}
|
|
2228
|
-
|
|
2229
|
-
.web-video-toggle {
|
|
2230
|
-
margin-top: 12px;
|
|
2231
|
-
}
|
|
2232
|
-
|
|
2233
|
-
.web-phone-connect {
|
|
2234
|
-
min-height: 42px;
|
|
2235
|
-
margin-top: 14px;
|
|
2236
|
-
background: #082b40;
|
|
2237
|
-
color: #fff;
|
|
2238
|
-
}
|
|
2239
|
-
|
|
2240
|
-
.web-phone-disconnect {
|
|
2241
|
-
min-height: 42px;
|
|
2242
|
-
margin-top: 14px;
|
|
2243
|
-
border-color: #aeb8c2;
|
|
2244
|
-
color: #344054;
|
|
2245
|
-
}
|
|
2246
|
-
|
|
2247
|
-
.web-phone-console {
|
|
2248
|
-
display: flex;
|
|
2249
|
-
min-width: 0;
|
|
2250
|
-
flex-direction: column;
|
|
2251
|
-
}
|
|
2252
|
-
|
|
2253
|
-
.web-phone-media {
|
|
2254
|
-
position: relative;
|
|
2255
|
-
display: grid;
|
|
2256
|
-
min-height: 260px;
|
|
2257
|
-
overflow: hidden;
|
|
2258
|
-
place-items: center;
|
|
2259
|
-
border: 1px solid #d7dde4;
|
|
2260
|
-
border-radius: 6px;
|
|
2261
|
-
background: #102a3b;
|
|
2262
|
-
}
|
|
2263
|
-
|
|
2264
|
-
.remote-video {
|
|
2265
|
-
display: none;
|
|
2266
|
-
width: 100%;
|
|
2267
|
-
height: 100%;
|
|
2268
|
-
object-fit: cover;
|
|
2269
|
-
}
|
|
2270
|
-
|
|
2271
|
-
.web-phone-media.has-video .remote-video {
|
|
2272
|
-
display: block;
|
|
2273
|
-
}
|
|
2274
|
-
|
|
2275
|
-
.local-video {
|
|
2276
|
-
position: absolute;
|
|
2277
|
-
right: 12px;
|
|
2278
|
-
bottom: 12px;
|
|
2279
|
-
display: none;
|
|
2280
|
-
width: 108px;
|
|
2281
|
-
aspect-ratio: 3 / 4;
|
|
2282
|
-
border: 2px solid #fff;
|
|
2283
|
-
border-radius: 4px;
|
|
2284
|
-
background: #263b49;
|
|
2285
|
-
object-fit: cover;
|
|
2286
|
-
}
|
|
2287
|
-
|
|
2288
|
-
.web-phone-media.has-video .local-video {
|
|
2289
|
-
display: block;
|
|
2290
|
-
}
|
|
2291
|
-
|
|
2292
|
-
.audio-call-state {
|
|
2293
|
-
display: flex;
|
|
2294
|
-
align-items: center;
|
|
2295
|
-
justify-content: center;
|
|
2296
|
-
padding: 28px;
|
|
2297
|
-
color: #fff;
|
|
2298
|
-
flex-direction: column;
|
|
2299
|
-
gap: 10px;
|
|
2300
|
-
text-align: center;
|
|
2301
|
-
}
|
|
2302
|
-
|
|
2303
|
-
.audio-call-state strong {
|
|
2304
|
-
font-size: 16px;
|
|
2305
|
-
font-weight: 600;
|
|
2306
|
-
}
|
|
2307
|
-
|
|
2308
|
-
.audio-call-state span {
|
|
2309
|
-
max-width: 100%;
|
|
2310
|
-
overflow: hidden;
|
|
2311
|
-
color: #c9d7e1;
|
|
2312
|
-
font-size: 11px;
|
|
2313
|
-
text-overflow: ellipsis;
|
|
2314
|
-
white-space: nowrap;
|
|
2315
|
-
}
|
|
2316
|
-
|
|
2317
|
-
.web-phone-dial-row {
|
|
2318
|
-
display: grid;
|
|
2319
|
-
grid-template-columns: minmax(0, 1fr) 42px;
|
|
2320
|
-
gap: 8px;
|
|
2321
|
-
align-items: start;
|
|
2322
|
-
}
|
|
2323
|
-
|
|
2324
|
-
.start-web-call,
|
|
2325
|
-
.hangup-web-call {
|
|
2326
|
-
width: 42px;
|
|
2327
|
-
height: 38px;
|
|
2328
|
-
border-radius: 4px;
|
|
2329
|
-
background: #2a9b50;
|
|
2330
|
-
color: #fff;
|
|
2331
|
-
}
|
|
2332
|
-
|
|
2333
|
-
.hangup-web-call,
|
|
2334
|
-
.reject-call {
|
|
2335
|
-
background: #c9293d;
|
|
2336
|
-
}
|
|
2337
|
-
|
|
2338
|
-
.incoming-call-actions {
|
|
2339
|
-
display: grid;
|
|
2340
|
-
grid-template-columns: 1fr 1fr;
|
|
2341
|
-
gap: 10px;
|
|
2342
|
-
margin-top: 14px;
|
|
2343
|
-
}
|
|
2344
|
-
|
|
2345
|
-
.answer-call,
|
|
2346
|
-
.reject-call {
|
|
2347
|
-
min-height: 42px;
|
|
2348
|
-
color: #fff;
|
|
2349
|
-
}
|
|
2350
|
-
|
|
2351
|
-
.answer-call {
|
|
2352
|
-
background: #2a9b50;
|
|
2353
|
-
}
|
|
2354
|
-
|
|
2355
|
-
.active-call-controls {
|
|
2356
|
-
display: flex;
|
|
2357
|
-
justify-content: center;
|
|
2358
|
-
gap: 12px;
|
|
2359
|
-
margin-top: 16px;
|
|
2360
|
-
}
|
|
2361
|
-
|
|
2362
|
-
.active-call-controls .v-btn {
|
|
2363
|
-
width: 46px;
|
|
2364
|
-
height: 46px;
|
|
2365
|
-
}
|
|
2366
|
-
|
|
2367
|
-
.web-phone-error {
|
|
2368
|
-
margin-top: 14px;
|
|
2369
|
-
font-size: 11px;
|
|
2370
|
-
}
|
|
2371
|
-
|
|
2372
1934
|
.call-two-col {
|
|
2373
1935
|
gap: 12px;
|
|
2374
1936
|
}
|
|
@@ -2502,22 +2064,13 @@ export default {
|
|
|
2502
2064
|
max-width: none;
|
|
2503
2065
|
}
|
|
2504
2066
|
|
|
2505
|
-
.
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
.web-phone-body {
|
|
2510
|
-
grid-template-columns: minmax(0, 1fr);
|
|
2511
|
-
max-height: 78vh;
|
|
2512
|
-
overflow: auto;
|
|
2067
|
+
.rebooting-card {
|
|
2068
|
+
grid-template-columns: 1fr;
|
|
2069
|
+
padding: 28px;
|
|
2513
2070
|
}
|
|
2514
2071
|
|
|
2515
|
-
.
|
|
2516
|
-
|
|
2517
|
-
padding-bottom: 20px;
|
|
2518
|
-
border-right: 0;
|
|
2519
|
-
border-bottom: 1px solid #e5e7eb;
|
|
2072
|
+
.wait-word {
|
|
2073
|
+
font-size: 80px;
|
|
2520
2074
|
}
|
|
2521
|
-
|
|
2522
2075
|
}
|
|
2523
2076
|
</style>
|