@snugdesk/avaya-ipo-widget 0.2.3 → 0.2.4
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/fesm2022/snugdesk-avaya-ipo-widget.mjs +532 -171
- package/fesm2022/snugdesk-avaya-ipo-widget.mjs.map +1 -1
- package/index.d.ts +95 -24
- package/package.json +1 -1
- package/src/assets/css/intl-tel-input-dropdown.css +0 -0
- package/src/assets/images/bg-app_color_line.gif +0 -0
- package/src/assets/images/icons/sd-backspace.png +0 -0
- package/src/assets/images/icons/sd-call_failed.gif +0 -0
- package/src/assets/images/logo-avaya_small_color.svg +0 -20
- package/src/assets/images/logo-avaya_small_gray.svg +0 -16
|
@@ -23,29 +23,35 @@ import { PhoneNumberUtil, PhoneNumberFormat } from 'google-libphonenumber';
|
|
|
23
23
|
const APP_CONFIG = {
|
|
24
24
|
// Widget build version — shown in the footer so you can confirm which build is loaded.
|
|
25
25
|
// Keep this in sync with the "version" field in projects/avaya-ipo-widget/package.json.
|
|
26
|
-
WIDGET_VERSION: '0.2.
|
|
26
|
+
WIDGET_VERSION: '0.2.4',
|
|
27
27
|
ASSET_CDN_BASE_URL: 'https://d39qei1249155h.cloudfront.net/avaya-ipo-widget',
|
|
28
28
|
AWL_SDK_URL: 'https://d39qei1249155h.cloudfront.net/avaya-ipo-widget/assets/js/awl.min.js',
|
|
29
29
|
AGENT_STATUS_API_URL: 'https://v2xla9n0t6.execute-api.us-west-2.amazonaws.com/dev',
|
|
30
|
-
CALL_HISTORY_API_URL: 'https://ktgxyy2x2h.execute-api.us-west-2.amazonaws.com/avayaCallHistory',
|
|
31
30
|
// Snugdesk backend (SnugdeskApiV1) endpoint that upserts a call as a real Interaction so it shows
|
|
32
31
|
// up in the same call history as every other channel in snugdesk-app.
|
|
33
32
|
INTERACTION_CALL_LOG_API_URL: 'https://api.snugdesk.com/avaya-ipo/call-log',
|
|
34
33
|
// Stores the full call recording in the backend and links it to the Interaction, triggering the
|
|
35
34
|
// server-side Gemini transcription/summary pipeline.
|
|
36
35
|
INTERACTION_CALL_RECORDING_API_URL: 'https://api.snugdesk.com/avaya-ipo/call-recording',
|
|
37
|
-
|
|
36
|
+
// How recordings reach the backend:
|
|
37
|
+
// 'complete' — one upload at call end via INTERACTION_CALL_RECORDING_API_URL (original flow)
|
|
38
|
+
// 'chunked' — stream 5s chunks over RECORDING_SOCKET_WS_URL during the call; the backend
|
|
39
|
+
// (avaya-ipo-recording-socket Lambda) stitches on every socket close, so a dead tab
|
|
40
|
+
// loses at most the last few seconds. Overridable per-embed via the
|
|
41
|
+
// [recordingUploadMode] input on the widget tag.
|
|
42
|
+
RECORDING_UPLOAD_MODE: 'complete',
|
|
43
|
+
// API Gateway WebSocket fronting the avaya-ipo-recording-socket Lambda ($connect/$disconnect/$default).
|
|
44
|
+
// From the snugdesk-backend-api stack output: AvayaIpoRecordingSocketUrl.
|
|
45
|
+
RECORDING_SOCKET_WS_URL: 'wss://lmarx4fzy3.execute-api.ap-south-1.amazonaws.com/production',
|
|
46
|
+
// Live in-call transcription (UI kept; backend integration planned — dev endpoint for now).
|
|
38
47
|
TRANSCRIPTION_WS_URL: 'ws://localhost:3100/live-stt/stream',
|
|
39
48
|
TRANSCRIPTION_LANGUAGE: 'auto',
|
|
40
49
|
TRANSCRIPTION_MODEL: 'nova-2',
|
|
41
|
-
POSTCALL_TRANSCRIBE_API_URL: 'https://nqfo27pht1.execute-api.us-east-1.amazonaws.com/Stage/ai-transcription/post-call-analysis',
|
|
42
50
|
COUNTRY_ENDPOINT: 'https://d39qei1249155h.cloudfront.net/avaya-ipo-widget/assets/metadata/countries.json',
|
|
43
51
|
PHONE_NUMBER_LOOKUP_BASE: 'https://api.snugdesk.com/phone-numbers',
|
|
44
52
|
PHONE_NUMBER_HLR_BASE: 'https://api.snugdesk.com/phone-numbers/hlr',
|
|
45
53
|
PHONE_LOOKUP_COUNTRY_CODE: '91',
|
|
46
|
-
ENTITY_SEARCH_BASE: 'https://search.snugdesk.com/entities',
|
|
47
54
|
OPENSEARCH_ENTITIES_URL: 'https://api.snugdesk.com/opensearch/entities',
|
|
48
|
-
PUSH_API_BASE: 'https://<your-api-id>.execute-api.<region>.amazonaws.com',
|
|
49
55
|
DIAL_OUT_PREFIX: '0',
|
|
50
56
|
};
|
|
51
57
|
const assetUrl = (path) => {
|
|
@@ -214,14 +220,41 @@ class AvayaIPOService {
|
|
|
214
220
|
this.ringAudio.loop = true;
|
|
215
221
|
this.ringAudio.currentTime = 0;
|
|
216
222
|
this.ringAudio.play().catch(() => {
|
|
217
|
-
//
|
|
223
|
+
// Autoplay blocked — the page has had no user gesture yet (common with auto-login).
|
|
224
|
+
// Ring as soon as the user interacts, if the call is still ringing by then.
|
|
225
|
+
console.warn('[avaya-ipo] Ring tone blocked by autoplay policy; will ring on first user gesture');
|
|
226
|
+
this.armRingRetryOnGesture();
|
|
218
227
|
});
|
|
219
228
|
}
|
|
220
229
|
catch {
|
|
221
230
|
// ignore play errors
|
|
222
231
|
}
|
|
223
232
|
}
|
|
233
|
+
ringRetryHandler = null;
|
|
234
|
+
armRingRetryOnGesture() {
|
|
235
|
+
if (this.ringRetryHandler || typeof document === 'undefined') {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const handler = () => {
|
|
239
|
+
this.disarmRingRetry();
|
|
240
|
+
// Only ring if the incoming call is still pending.
|
|
241
|
+
if (this.lastIncomingCall && this.ringAudio) {
|
|
242
|
+
this.ringAudio.play().catch(() => { });
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
this.ringRetryHandler = handler;
|
|
246
|
+
['click', 'keydown', 'touchstart'].forEach((type) => document.addEventListener(type, handler, { once: true, capture: true }));
|
|
247
|
+
}
|
|
248
|
+
disarmRingRetry() {
|
|
249
|
+
if (!this.ringRetryHandler || typeof document === 'undefined') {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const handler = this.ringRetryHandler;
|
|
253
|
+
this.ringRetryHandler = null;
|
|
254
|
+
['click', 'keydown', 'touchstart'].forEach((type) => document.removeEventListener(type, handler, { capture: true }));
|
|
255
|
+
}
|
|
224
256
|
stopRingTone() {
|
|
257
|
+
this.disarmRingRetry();
|
|
225
258
|
if (!this.ringAudio) {
|
|
226
259
|
return;
|
|
227
260
|
}
|
|
@@ -1075,6 +1108,7 @@ class EntitiesSearchService {
|
|
|
1075
1108
|
http;
|
|
1076
1109
|
authenticationService;
|
|
1077
1110
|
nameCache = new Map();
|
|
1111
|
+
entityIdCache = new Map();
|
|
1078
1112
|
constructor(http, authenticationService) {
|
|
1079
1113
|
this.http = http;
|
|
1080
1114
|
this.authenticationService = authenticationService;
|
|
@@ -1098,20 +1132,7 @@ class EntitiesSearchService {
|
|
|
1098
1132
|
],
|
|
1099
1133
|
query: {
|
|
1100
1134
|
bool: {
|
|
1101
|
-
filter: [
|
|
1102
|
-
{ term: { is_blocked: false } },
|
|
1103
|
-
{ term: { tenant_id: tenantId } },
|
|
1104
|
-
{
|
|
1105
|
-
bool: {
|
|
1106
|
-
should: [
|
|
1107
|
-
{ exists: { field: 'phone_numbers' } },
|
|
1108
|
-
{ exists: { field: 'phone' } },
|
|
1109
|
-
{ exists: { field: 'normalizedPhoneNumber' } },
|
|
1110
|
-
],
|
|
1111
|
-
minimum_should_match: 1,
|
|
1112
|
-
},
|
|
1113
|
-
},
|
|
1114
|
-
],
|
|
1135
|
+
filter: [{ term: { tenant_id: tenantId } }],
|
|
1115
1136
|
},
|
|
1116
1137
|
},
|
|
1117
1138
|
};
|
|
@@ -1229,19 +1250,28 @@ class EntitiesSearchService {
|
|
|
1229
1250
|
return this.nameCache.get(key) || null;
|
|
1230
1251
|
}
|
|
1231
1252
|
async lookupNameByPhone(tenantId, phoneNumber, size = 25) {
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1253
|
+
return (await this.lookupEntityByPhone(tenantId, phoneNumber, size)).name;
|
|
1254
|
+
}
|
|
1255
|
+
/** Resolve the entity (id + name) matching a phone number; both are cached per tenant+number. */
|
|
1256
|
+
async lookupEntityByPhone(tenantId, phoneNumber, size = 25) {
|
|
1257
|
+
const key = this.cacheKey(tenantId, phoneNumber);
|
|
1258
|
+
const cachedName = this.nameCache.get(key) || null;
|
|
1259
|
+
const cachedId = this.entityIdCache.get(key) || null;
|
|
1260
|
+
if (cachedName || cachedId) {
|
|
1261
|
+
return { id: cachedId, name: cachedName };
|
|
1235
1262
|
}
|
|
1236
1263
|
const res = await this.searchByPhone(tenantId, phoneNumber, size);
|
|
1237
1264
|
const hit = res?.hits?.hits?.[0];
|
|
1238
|
-
const
|
|
1239
|
-
const
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1265
|
+
const source = hit?._source || {};
|
|
1266
|
+
const name = typeof source.name === 'string' ? source.name.trim() : '';
|
|
1267
|
+
const id = (source.id || hit?._id || '').toString().trim();
|
|
1268
|
+
if (name) {
|
|
1269
|
+
this.nameCache.set(key, name);
|
|
1243
1270
|
}
|
|
1244
|
-
|
|
1271
|
+
if (id) {
|
|
1272
|
+
this.entityIdCache.set(key, id);
|
|
1273
|
+
}
|
|
1274
|
+
return { id: id || null, name: name || null };
|
|
1245
1275
|
}
|
|
1246
1276
|
postEntities(body) {
|
|
1247
1277
|
const token = this.authenticationService.getAuthSessionToken();
|
|
@@ -1266,19 +1296,47 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImpor
|
|
|
1266
1296
|
args: [{ providedIn: 'root' }]
|
|
1267
1297
|
}], ctorParameters: () => [{ type: i1.HttpClient }, { type: AuthenticationService }] });
|
|
1268
1298
|
|
|
1299
|
+
/**
|
|
1300
|
+
* Shows the same AI call analysis as snugdesk-app's conversation view: the data is read
|
|
1301
|
+
* from the Interaction record (metadata.aiAnalysis), which the server-side pipeline fills
|
|
1302
|
+
* after the recording is uploaded — no client-side transcription is run here.
|
|
1303
|
+
*/
|
|
1269
1304
|
class CallDetailsComponent {
|
|
1270
|
-
|
|
1305
|
+
appSync;
|
|
1271
1306
|
call;
|
|
1272
1307
|
transcriptLines = [];
|
|
1308
|
+
conversationSummary = [];
|
|
1309
|
+
nextActions = [];
|
|
1310
|
+
keyConcerns = [];
|
|
1311
|
+
callType = '';
|
|
1312
|
+
customerIntent = '';
|
|
1313
|
+
customerSentiment = '';
|
|
1314
|
+
aiConfidenceScore = null;
|
|
1273
1315
|
summaryText = '';
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1316
|
+
aiProcessingStatus = '';
|
|
1317
|
+
hasAnalysis = false;
|
|
1318
|
+
isRefreshing = false;
|
|
1319
|
+
refreshError = '';
|
|
1277
1320
|
isTranscriptFullscreen = false;
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1321
|
+
GET_INTERACTION_QUERY = `query GetInteraction($id: ID!) {
|
|
1322
|
+
getInteraction(id: $id) {
|
|
1323
|
+
id
|
|
1324
|
+
metadata
|
|
1325
|
+
summary
|
|
1326
|
+
aiProcessingStatus
|
|
1327
|
+
recording { location }
|
|
1328
|
+
}
|
|
1329
|
+
}`;
|
|
1330
|
+
constructor(appSync) {
|
|
1331
|
+
this.appSync = appSync;
|
|
1332
|
+
}
|
|
1333
|
+
ngOnChanges(changes) {
|
|
1334
|
+
if (changes['call']) {
|
|
1335
|
+
this.parseAnalysisFromCall();
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
get isAnalysisPending() {
|
|
1339
|
+
return ['PENDING', 'IN_PROGRESS'].includes((this.aiProcessingStatus || '').toUpperCase());
|
|
1282
1340
|
}
|
|
1283
1341
|
getBadge() {
|
|
1284
1342
|
const base = this.call?.phoneNumber?.country?.code ||
|
|
@@ -1304,38 +1362,107 @@ class CallDetailsComponent {
|
|
|
1304
1362
|
const normalized = raw.toLowerCase();
|
|
1305
1363
|
if (normalized === 'customer') {
|
|
1306
1364
|
const name = this.getDisplayName();
|
|
1307
|
-
return name && name !== 'Unknown' ? name : '
|
|
1365
|
+
return name && name !== 'Unknown' ? name : 'Customer';
|
|
1308
1366
|
}
|
|
1309
1367
|
if (normalized === 'agent') {
|
|
1310
1368
|
return 'Agent';
|
|
1311
1369
|
}
|
|
1312
1370
|
return raw || 'Unknown';
|
|
1313
1371
|
}
|
|
1314
|
-
|
|
1315
|
-
|
|
1372
|
+
getSentimentEmoji() {
|
|
1373
|
+
const value = (this.customerSentiment || '').toLowerCase();
|
|
1374
|
+
if (value.includes('angry') || value.includes('frustrat'))
|
|
1375
|
+
return '😠';
|
|
1376
|
+
if (value.includes('calm'))
|
|
1377
|
+
return '😌';
|
|
1378
|
+
if (value.includes('happy') || value.includes('positive') || value.includes('satisf'))
|
|
1379
|
+
return '😄';
|
|
1380
|
+
if (value.includes('confus'))
|
|
1381
|
+
return '😕';
|
|
1382
|
+
if (value.includes('neutral'))
|
|
1383
|
+
return '😐';
|
|
1384
|
+
if (value.includes('negative') || value.includes('upset'))
|
|
1385
|
+
return '🙁';
|
|
1386
|
+
return '';
|
|
1387
|
+
}
|
|
1388
|
+
/** Re-read the Interaction record (the AI pipeline fills metadata.aiAnalysis asynchronously). */
|
|
1389
|
+
async refreshAnalysis() {
|
|
1390
|
+
const id = this.call?.id;
|
|
1391
|
+
if (!id || this.isRefreshing) {
|
|
1316
1392
|
return;
|
|
1317
1393
|
}
|
|
1318
|
-
this.
|
|
1319
|
-
this.
|
|
1394
|
+
this.isRefreshing = true;
|
|
1395
|
+
this.refreshError = '';
|
|
1320
1396
|
try {
|
|
1321
|
-
const
|
|
1322
|
-
const
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1397
|
+
const res = await this.appSync.query(this.GET_INTERACTION_QUERY, { id });
|
|
1398
|
+
const interaction = res?.data?.getInteraction;
|
|
1399
|
+
if (interaction) {
|
|
1400
|
+
this.call = {
|
|
1401
|
+
...this.call,
|
|
1402
|
+
summary: interaction.summary || this.call?.summary || '',
|
|
1403
|
+
metadata: this.safeParse(interaction.metadata) || this.call?.metadata,
|
|
1404
|
+
aiProcessingStatus: interaction.aiProcessingStatus || '',
|
|
1405
|
+
fullCallRecordingUrl: interaction.recording?.location || this.call?.fullCallRecordingUrl || '',
|
|
1406
|
+
};
|
|
1407
|
+
this.parseAnalysisFromCall();
|
|
1408
|
+
}
|
|
1330
1409
|
}
|
|
1331
1410
|
catch (err) {
|
|
1332
|
-
console.error('[CallDetails]
|
|
1333
|
-
this.
|
|
1411
|
+
console.error('[CallDetails] Failed to refresh interaction', err);
|
|
1412
|
+
this.refreshError = 'Failed to refresh analysis. Please try again.';
|
|
1334
1413
|
}
|
|
1335
1414
|
finally {
|
|
1336
|
-
this.
|
|
1415
|
+
this.isRefreshing = false;
|
|
1337
1416
|
}
|
|
1338
1417
|
}
|
|
1418
|
+
toggleTranscriptFullscreen() {
|
|
1419
|
+
this.isTranscriptFullscreen = !this.isTranscriptFullscreen;
|
|
1420
|
+
}
|
|
1421
|
+
/** Pull the structured AI analysis off the interaction's metadata — same shape snugdesk-app reads. */
|
|
1422
|
+
parseAnalysisFromCall() {
|
|
1423
|
+
const metadata = this.safeParse(this.call?.metadata) || {};
|
|
1424
|
+
const ai = metadata.aiAnalysis || {};
|
|
1425
|
+
this.summaryText = this.normalizeText(this.call?.summary);
|
|
1426
|
+
this.aiProcessingStatus = (this.call?.aiProcessingStatus || '').toString();
|
|
1427
|
+
this.conversationSummary = this.normalizeBulletItems(ai.conversationSummary);
|
|
1428
|
+
this.nextActions = this.normalizeBulletItems(ai.nextAction);
|
|
1429
|
+
this.keyConcerns = this.normalizeBulletItems(ai.keyConcerns);
|
|
1430
|
+
this.callType = this.normalizeText(ai.callType);
|
|
1431
|
+
this.customerIntent = this.normalizeText(ai.customerIntent);
|
|
1432
|
+
this.customerSentiment = this.normalizeText(ai.customerSentiment);
|
|
1433
|
+
const score = Number(ai.aiConfidenceScore);
|
|
1434
|
+
this.aiConfidenceScore = Number.isFinite(score) ? Math.round(score) : null;
|
|
1435
|
+
this.transcriptLines = this.normalizeTranscript(ai.transcript);
|
|
1436
|
+
this.hasAnalysis =
|
|
1437
|
+
this.transcriptLines.length > 0 ||
|
|
1438
|
+
this.conversationSummary.length > 0 ||
|
|
1439
|
+
!!this.callType ||
|
|
1440
|
+
!!this.customerIntent ||
|
|
1441
|
+
!!this.customerSentiment ||
|
|
1442
|
+
this.keyConcerns.length > 0 ||
|
|
1443
|
+
this.nextActions.length > 0;
|
|
1444
|
+
}
|
|
1445
|
+
normalizeText(value) {
|
|
1446
|
+
if (value === null || value === undefined)
|
|
1447
|
+
return '';
|
|
1448
|
+
return value.toString().trim();
|
|
1449
|
+
}
|
|
1450
|
+
normalizeBulletItems(input) {
|
|
1451
|
+
if (!input)
|
|
1452
|
+
return [];
|
|
1453
|
+
if (Array.isArray(input)) {
|
|
1454
|
+
return input
|
|
1455
|
+
.map((item) => typeof item === 'string' ? item.trim() : this.normalizeText(item?.text ?? item))
|
|
1456
|
+
.filter(Boolean);
|
|
1457
|
+
}
|
|
1458
|
+
if (typeof input === 'string') {
|
|
1459
|
+
return input
|
|
1460
|
+
.split(/\r?\n|•/)
|
|
1461
|
+
.map((line) => line.replace(/^\s*[-*\d.)]+\s*/, '').trim())
|
|
1462
|
+
.filter(Boolean);
|
|
1463
|
+
}
|
|
1464
|
+
return [];
|
|
1465
|
+
}
|
|
1339
1466
|
normalizeTranscript(input) {
|
|
1340
1467
|
if (!input) {
|
|
1341
1468
|
return [];
|
|
@@ -1345,44 +1472,26 @@ class CallDetailsComponent {
|
|
|
1345
1472
|
.split(/\r?\n/)
|
|
1346
1473
|
.map((line) => line.trim())
|
|
1347
1474
|
.filter(Boolean)
|
|
1348
|
-
.map((line) =>
|
|
1349
|
-
const parsed = this.parseSpeakerLine(line);
|
|
1350
|
-
return { speaker: parsed.speaker, text: parsed.text };
|
|
1351
|
-
});
|
|
1475
|
+
.map((line) => this.parseSpeakerLine(line));
|
|
1352
1476
|
}
|
|
1353
1477
|
if (Array.isArray(input)) {
|
|
1354
1478
|
return input
|
|
1355
1479
|
.map((entry) => {
|
|
1356
1480
|
if (typeof entry === 'string') {
|
|
1357
|
-
|
|
1358
|
-
return { speaker: parsed.speaker, text: parsed.text };
|
|
1481
|
+
return this.parseSpeakerLine(entry);
|
|
1359
1482
|
}
|
|
1360
1483
|
if (entry && typeof entry === 'object') {
|
|
1361
1484
|
return {
|
|
1362
|
-
speaker: entry.speaker,
|
|
1363
|
-
text: entry.text,
|
|
1485
|
+
speaker: entry.speaker || entry.role,
|
|
1486
|
+
text: entry.text ?? entry.message ?? entry.utterance ?? entry.content,
|
|
1364
1487
|
};
|
|
1365
1488
|
}
|
|
1366
1489
|
return null;
|
|
1367
1490
|
})
|
|
1368
|
-
.filter(
|
|
1491
|
+
.filter((line) => !!line && !!line.text);
|
|
1369
1492
|
}
|
|
1370
1493
|
return [];
|
|
1371
1494
|
}
|
|
1372
|
-
getBehaviorEmoji() {
|
|
1373
|
-
const value = (this.customerBehavior || '').toLowerCase();
|
|
1374
|
-
if (value.includes('angry'))
|
|
1375
|
-
return '😠';
|
|
1376
|
-
if (value.includes('calm'))
|
|
1377
|
-
return '😌';
|
|
1378
|
-
if (value.includes('happy'))
|
|
1379
|
-
return '😄';
|
|
1380
|
-
if (value.includes('confused'))
|
|
1381
|
-
return '😕';
|
|
1382
|
-
if (value.includes('neutral'))
|
|
1383
|
-
return '😐';
|
|
1384
|
-
return '';
|
|
1385
|
-
}
|
|
1386
1495
|
parseSpeakerLine(line) {
|
|
1387
1496
|
const match = line.match(/^\s*(agent|customer)\s*:\s*(.*)$/i);
|
|
1388
1497
|
if (match) {
|
|
@@ -1390,36 +1499,25 @@ class CallDetailsComponent {
|
|
|
1390
1499
|
}
|
|
1391
1500
|
return { text: line.trim() };
|
|
1392
1501
|
}
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
return name;
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
1406
|
-
catch {
|
|
1407
|
-
const parts = url.split('/').filter(Boolean);
|
|
1408
|
-
const name = parts[parts.length - 1];
|
|
1409
|
-
if (name) {
|
|
1410
|
-
return name;
|
|
1411
|
-
}
|
|
1412
|
-
}
|
|
1502
|
+
safeParse(value) {
|
|
1503
|
+
if (!value)
|
|
1504
|
+
return null;
|
|
1505
|
+
if (typeof value === 'object')
|
|
1506
|
+
return value;
|
|
1507
|
+
try {
|
|
1508
|
+
return JSON.parse(value);
|
|
1509
|
+
}
|
|
1510
|
+
catch {
|
|
1511
|
+
return null;
|
|
1413
1512
|
}
|
|
1414
|
-
return 'call-recording.wav';
|
|
1415
1513
|
}
|
|
1416
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: CallDetailsComponent, deps: [{ token: i1.
|
|
1417
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.9", type: CallDetailsComponent, isStandalone: false, selector: "app-call-details", inputs: { call: "call" }, ngImport: i0, template: "<div class=\"avaya-ipo-call-profile\">\n <div class=\"avaya-ipo-profile-header\">\n <div class=\"avaya-ipo-avatar\">\n <svg viewBox=\"0 0 448 512\" width=\"28\" height=\"28\" fill=\"currentColor\" aria-hidden=\"true\">\n <path\n d=\"M224 256A128 128 0 1 0 224 0a128 128 0 1 0 0 256zm-45.7 48C79.8 304 0 383.8 0 482.3C0 498.7 13.3 512 29.7 512H418.3c16.4 0 29.7-13.3 29.7-29.7C448 383.8 368.2 304 269.7 304H178.3z\" />\n </svg>\n </div>\n <div class=\"avaya-ipo-profile-info\">\n <div class=\"avaya-ipo-primary-number\">\n {{ getDisplayName() }}\n </div>\n <div class=\"avaya-ipo-secondary-info\">\n {{ getPhone() || 'Unknown' }}\n </div>\n <div class=\"avaya-ipo-meta-line\">\n <span>{{ call?.timestamp | date : 'medium' }}</span>\n <span class=\"avaya-ipo-divider\">|</span>\n @if (call?.disconnectSide === 'CALLER' && (call?.duration ?? 0) === 0) {\n <span class=\"avaya-ipo-divider\">|</span>\n <span class=\"avaya-ipo-missed-badge\">\n <svg viewBox=\"0 0 640 512\" width=\"12px\" aria-hidden=\"true\" style=\"fill: currentColor;\">\n <path d=\"M232 0c9.7 0 18.5 5.8 22.2 14.8s1.7 19.3-5.2 26.2l-39 39 58.7 58.7C282.3 152.4 300.8 160 320 160s37.7-7.6 51.3-21.3L503 7c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9L405.3 172.7C382.6 195.3 352 208 320 208s-62.6-12.7-85.3-35.3L176 113.9l-39 39c-6.9 6.9-17.2 8.9-26.2 5.2s-14.8-12.5-14.8-22.2L96 24c0-13.3 10.7-24 24-24L232 0zM51.4 489.9l-35.4-62c-9.7-16.9-8.3-38.1 5.5-51.7C72.6 325.9 178.1 256 320 256s247.4 69.9 298.5 120.2c13.9 13.6 15.2 34.8 5.5 51.7l-35.4 62c-7.4 12.9-22.7 19.1-37 14.8L438.8 470.8c-13.5-4.1-22.8-16.5-22.8-30.6l0-56.2c-62.3-20.8-129.7-20.8-192 0l0 56.2c0 14.1-9.3 26.6-22.8 30.6L88.4 504.7c-14.3 4.3-29.6-1.8-37-14.8z\" />\n </svg>\n <span>Missed Call</span>\n </span>\n }\n </div>\n </div>\n </div>\n\n <div class=\"avaya-ipo-detail-card avaya-ipo-summary-card\">\n <div class=\"avaya-ipo-detail-row\">\n <div class=\"avaya-ipo-label\">Duration</div>\n <div class=\"avaya-ipo-value\">{{ call?.duration ?? 0 }}s</div>\n </div>\n <div class=\"avaya-ipo-detail-row\">\n <div class=\"avaya-ipo-label\">Disconnect Side</div>\n <div class=\"avaya-ipo-value\">{{ call?.disconnectSide || 'N/A' }}</div>\n </div>\n <div class=\"avaya-ipo-detail-row\">\n <div class=\"avaya-ipo-label\">Country</div>\n <div class=\"avaya-ipo-value\">\n {{\n call?.phoneNumber?.country?.nameEn ||\n call?.phoneNumber?.lookupPhoneNumber?.country ||\n 'N/A'\n }}\n </div>\n </div>\n <div class=\"avaya-ipo-detail-row\">\n <div class=\"avaya-ipo-label\">Provider</div>\n <div class=\"avaya-ipo-value\">\n {{\n call?.phoneNumber?.lookupPhoneNumber?.['prefix-network'] ||\n call?.phoneNumber?.lookupPhoneNumber?.provider ||\n 'N/A'\n }}\n </div>\n </div>\n </div>\n\n <div class=\"avaya-ipo-detail-card avaya-ipo-transcription-card\" [class.avaya-ipo-fullscreen]=\"isTranscriptFullscreen\">\n <div class=\"avaya-ipo-detail-title\">Call Recording</div>\n <audio\n *ngIf=\"call?.fullCallRecordingUrl; else noRecording\"\n controls\n [src]=\"call?.fullCallRecordingUrl\"\n ></audio>\n <ng-template #noRecording>\n @if ((call?.duration ?? 0) > 0) {\n <button class=\"avaya-ipo-disabled-btn\" disabled>Processing</button>\n } @else {\n <button class=\"avaya-ipo-disabled-btn\" disabled>Recording not available</button>\n }\n </ng-template>\n </div>\n\n @if (!hasGeneratedTranscript) {\n <div class=\"avaya-ipo-transcription-actions avaya-ipo-outside\">\n <button class=\"avaya-ipo-action-btn\" type=\"button\" [disabled]=\"isTranscribing\" (click)=\"runTranscription()\">\n <span class=\"avaya-ipo-btn-content\">\n @if (isTranscribing) {\n <span class=\"avaya-ipo-btn-spinner\" aria-hidden=\"true\"></span>\n }\n <span>{{ isTranscribing ? 'Generating...' : 'Run AI Call Analysis' }}</span>\n </span>\n </button>\n @if (transcriptionError) {\n <div class=\"avaya-ipo-status-error\">{{ transcriptionError }}</div>\n }\n </div>\n }\n\n <div class=\"avaya-ipo-detail-card\">\n <div class=\"avaya-ipo-detail-title\">Call Summary</div>\n @if (customerBehavior) {\n <div class=\"avaya-ipo-summary-sentiment\">\n Customer Behavior: {{ customerBehavior }}\n @if (getBehaviorEmoji()) {\n <span class=\"avaya-ipo-summary-emoji\">{{ getBehaviorEmoji() }}</span>\n }\n </div>\n }\n @if (summaryText) {\n <div class=\"avaya-ipo-summary-text\">{{ summaryText }}</div>\n } @else {\n <div class=\"avaya-ipo-placeholder\">No summary available.</div>\n }\n </div>\n\n <div class=\"avaya-ipo-detail-card avaya-ipo-transcription-card\" [class.avaya-ipo-fullscreen]=\"isTranscriptFullscreen\">\n <div class=\"avaya-ipo-detail-title-row\">\n <div class=\"avaya-ipo-detail-title\">AI Call Transcription</div>\n @if (transcriptLines?.length) {\n <button\n class=\"avaya-ipo-chat-thread-btn\"\n type=\"button\"\n (click)=\"toggleTranscriptFullscreen()\"\n [attr.aria-label]=\"isTranscriptFullscreen ? 'Exit full screen' : 'Enter full screen'\"\n >\n @if (isTranscriptFullscreen) {\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path\n fill=\"currentColor\"\n d=\"M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z\"\n />\n </svg>\n } @else {\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path\n fill=\"currentColor\"\n d=\"M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z\"\n />\n </svg>\n }\n </button>\n }\n </div>\n @if (call?.fullCallRecordingUrl) {\n @if (transcriptLines?.length) {\n <div class=\"avaya-ipo-chat-thread\">\n @for (line of transcriptLines; track $index) {\n <div class=\"avaya-ipo-chat-bubble\" [class.avaya-ipo-agent]=\"(line.speaker || '').toLowerCase() === 'agent'\">\n <div class=\"avaya-ipo-chat-speaker\">{{ getTranscriptSpeaker(line) }}</div>\n <div class=\"avaya-ipo-chat-text\">{{ line.text || '' }}</div>\n </div>\n }\n </div>\n } @else if (isTranscribing) {\n <div class=\"avaya-ipo-transcription-skeleton\">\n @for (item of [1,2,3,4,5]; track item) {\n <div class=\"avaya-ipo-transcription-skeleton-line\"></div>\n }\n </div>\n } @else {\n <div class=\"avaya-ipo-placeholder\">No transcription available.</div>\n }\n\n } @else {\n <div class=\"avaya-ipo-placeholder\">No recording available for transcription.</div>\n }\n </div>\n</div>\n", styles: [":host{display:block;width:100%;box-sizing:border-box}.avaya-ipo-call-profile{display:flex;flex-direction:column;gap:14px;font-family:Open Sans,Helvetica Neue,Arial,sans-serif;color:var(--sd-text, #333)}.avaya-ipo-profile-header{display:flex;gap:12px;align-items:center;padding:12px;border-radius:var(--sd-radius-lg, 14px);background:var(--sd-surface-muted, #f7f7f7)}.avaya-ipo-avatar{width:52px;height:52px;border-radius:var(--sd-radius-lg, 14px);background:var(--sd-avatar, #eceff3);display:flex;align-items:center;justify-content:center;font-weight:800;font-size:14px;color:var(--sd-text, #333)}.avaya-ipo-profile-info{display:flex;flex-direction:column;gap:6px}.avaya-ipo-primary-number{font-size:16px;font-weight:800;color:var(--sd-text, #333)}.avaya-ipo-secondary-info{font-size:12px;color:var(--sd-text-muted, #5f6368)}.avaya-ipo-meta-line{display:flex;gap:6px;align-items:center;font-size:12px;color:var(--sd-text-muted, #5f6368);flex-wrap:wrap}.avaya-ipo-meta-line .avaya-ipo-divider{color:var(--sd-text-subtle, #9ca3af)}.avaya-ipo-meta-line .avaya-ipo-dir.avaya-ipo-inbound{color:var(--sd-call-accept, #1aa251)}.avaya-ipo-meta-line .avaya-ipo-dir.avaya-ipo-outbound{color:var(--sd-text-muted, #5f6368)}.avaya-ipo-meta-line .avaya-ipo-missed-badge{color:var(--sd-call-end, #e5484d);font-weight:700}.avaya-ipo-detail-card{display:flex;flex-direction:column;gap:10px;padding:12px;border-radius:var(--sd-radius-lg, 14px);background:var(--sd-card, #fff);border:1px solid var(--sd-border, #e3e3e3)}.avaya-ipo-summary-card{order:1}.avaya-ipo-transcription-card{order:2}.avaya-ipo-detail-card audio{width:100%}.avaya-ipo-disabled-btn{width:100%;border:1px solid var(--sd-border, #e3e3e3);background:var(--sd-surface-muted, #f7f7f7);color:var(--sd-text-subtle, #9ca3af);padding:10px;border-radius:var(--sd-radius, 10px);font-weight:600;cursor:not-allowed}.avaya-ipo-detail-title{font-weight:800;color:var(--sd-text, #333);font-size:14px}.avaya-ipo-detail-title-row{display:flex;align-items:center;justify-content:space-between}.avaya-ipo-transcription-actions{display:flex;flex-direction:column;gap:8px}.avaya-ipo-transcription-actions.avaya-ipo-outside{padding:0 4px}.avaya-ipo-detail-card.avaya-ipo-is-inactive{opacity:.45;filter:grayscale(.2);pointer-events:none}.avaya-ipo-action-btn{border:none;background:var(--sd-accent, #ff6633);color:var(--sd-accent-contrast, #fff);padding:10px 14px;border-radius:var(--sd-radius, 10px);font-weight:700;cursor:pointer;transition:background .15s ease,box-shadow .15s ease}.avaya-ipo-btn-content{display:inline-flex;align-items:center;gap:8px}.avaya-ipo-btn-spinner{width:14px;height:14px;border-radius:var(--sd-radius-pill, 999px);border:2px solid color-mix(in srgb,var(--sd-accent-contrast, #fff) 35%,transparent);border-top-color:var(--sd-accent-contrast, #fff);animation:spin .8s linear infinite}.avaya-ipo-action-btn:hover:not(:disabled){background:var(--sd-accent-strong, #ff5805)}.avaya-ipo-action-btn:focus-visible{outline:none;box-shadow:var(--sd-focus-ring, 0 0 0 3px rgba(255, 102, 51, .22))}.avaya-ipo-action-btn:disabled{background:var(--sd-surface-sunken, #e8e8e8);color:var(--sd-text-subtle, #9ca3af);cursor:not-allowed}.avaya-ipo-status-error{color:var(--sd-call-end, #e5484d);font-size:12px;font-weight:600}.avaya-ipo-transcription-skeleton{display:flex;flex-direction:column;gap:10px}.avaya-ipo-transcription-skeleton-line{height:10px;border-radius:var(--sd-radius-pill, 999px);background:linear-gradient(90deg,var(--sd-surface-sunken, #e8e8e8) 0%,var(--sd-surface-muted, #f7f7f7) 45%,var(--sd-surface-sunken, #e8e8e8) 100%);background-size:200% 100%;animation:skeleton-shine 1.2s ease-in-out infinite}.avaya-ipo-transcription-skeleton-line:nth-child(2){width:85%}.avaya-ipo-transcription-skeleton-line:nth-child(3){width:90%}.avaya-ipo-transcription-skeleton-line:nth-child(4){width:70%}.avaya-ipo-transcription-skeleton-line:nth-child(5){width:60%}.avaya-ipo-chat-thread{display:flex;flex-direction:column;gap:8px}.avaya-ipo-chat-thread-btn{border:1px solid var(--sd-border, #e3e3e3);background:var(--sd-surface, #fff);color:var(--sd-text, #333);padding:6px;border-radius:var(--sd-radius-sm, 8px);font-weight:700;cursor:pointer;font-size:12px;width:32px;height:32px;display:inline-flex;align-items:center;justify-content:center;transition:background .15s ease,border-color .15s ease}.avaya-ipo-chat-thread-btn:hover{background:var(--sd-surface-muted, #f7f7f7)}.avaya-ipo-chat-thread-btn svg{width:16px;height:16px}.avaya-ipo-transcription-card.avaya-ipo-fullscreen{position:absolute;inset:0;background:var(--sd-surface, #fff);padding:16px;z-index:1000;animation:transcript-expand .25s ease;border-radius:0}:host-context(.avaya-ipo-mode-standalone) .avaya-ipo-transcription-card.avaya-ipo-fullscreen{position:fixed}.avaya-ipo-transcription-card.avaya-ipo-fullscreen .avaya-ipo-detail-title-row{position:sticky;top:0;background:var(--sd-surface, #fff);z-index:2}.avaya-ipo-transcription-card.avaya-ipo-fullscreen .avaya-ipo-chat-thread{flex:1 1 auto;overflow-y:auto;padding-bottom:8px}.avaya-ipo-detail-card{transition:transform .25s ease,opacity .25s ease}.avaya-ipo-transcription-card:not(.avaya-ipo-fullscreen){animation:transcript-collapse .2s ease}.avaya-ipo-chat-bubble{align-self:flex-start;max-width:86%;background:var(--sd-surface-muted, #f7f7f7);border-radius:var(--sd-radius, 10px);padding:8px 10px;border:1px solid var(--sd-border, #e3e3e3)}.avaya-ipo-chat-bubble.avaya-ipo-agent{align-self:flex-end;background:var(--sd-accent-soft, #fff1ea);border-color:color-mix(in srgb,var(--sd-accent, #ff6633) 35%,transparent)}.avaya-ipo-chat-speaker{font-size:11px;font-weight:700;color:var(--sd-text-muted, #5f6368);text-transform:capitalize;margin-bottom:4px}.avaya-ipo-chat-text,.avaya-ipo-summary-text{font-size:13px;color:var(--sd-text, #333);line-height:1.4}.avaya-ipo-summary-sentiment{font-size:12px;font-weight:700;color:var(--sd-text, #333);margin-bottom:6px}.avaya-ipo-summary-emoji{margin-left:6px;font-size:14px}.avaya-ipo-summary-grid,.avaya-ipo-qa-grid{display:grid;gap:8px}.avaya-ipo-summary-card,.avaya-ipo-qa-card{border:1px solid var(--sd-border, #e3e3e3);border-radius:var(--sd-radius, 10px);padding:10px;background:var(--sd-surface-muted, #f7f7f7)}.avaya-ipo-summary-title,.avaya-ipo-qa-title{font-weight:700;color:var(--sd-text, #333);font-size:12px;margin-bottom:6px;text-transform:capitalize}.avaya-ipo-summary-text,.avaya-ipo-qa-answer{font-size:13px;color:var(--sd-text, #333);line-height:1.4}.avaya-ipo-qa-reason{margin-top:6px;font-size:12px;color:var(--sd-text-muted, #5f6368);line-height:1.4}.avaya-ipo-sentiment-score{color:var(--sd-text-muted, #5f6368);font-size:12px}@keyframes transcript-expand{0%{transform:scale(.98);opacity:.6}to{transform:scale(1);opacity:1}}@keyframes transcript-collapse{0%{transform:scale(1.02);opacity:.6}to{transform:scale(1);opacity:1}}@keyframes spin{to{transform:rotate(360deg)}}@keyframes skeleton-shine{0%{background-position:200% 0}to{background-position:-200% 0}}.avaya-ipo-placeholder{font-size:13px;color:var(--sd-text-muted, #5f6368)}.avaya-ipo-detail-row{display:flex;justify-content:space-between;align-items:center}.avaya-ipo-label{font-weight:700;color:var(--sd-text-muted, #5f6368);font-size:13px}.avaya-ipo-value{color:var(--sd-text, #333);text-align:right;font-weight:600;font-size:13px}.avaya-ipo-value.avaya-ipo-mono{font-family:SFMono-Regular,Menlo,Consolas,monospace;font-size:12px;color:var(--sd-text-muted, #5f6368)}\n"], dependencies: [{ kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i2.DatePipe, name: "date" }] });
|
|
1514
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: CallDetailsComponent, deps: [{ token: i1$1.AppSyncHelperService }], target: i0.ɵɵFactoryTarget.Component });
|
|
1515
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.9", type: CallDetailsComponent, isStandalone: false, selector: "app-call-details", inputs: { call: "call" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"avaya-ipo-call-profile\">\n <div class=\"avaya-ipo-profile-header\">\n <div class=\"avaya-ipo-avatar\">\n <svg viewBox=\"0 0 448 512\" width=\"28\" height=\"28\" fill=\"currentColor\" aria-hidden=\"true\">\n <path\n d=\"M224 256A128 128 0 1 0 224 0a128 128 0 1 0 0 256zm-45.7 48C79.8 304 0 383.8 0 482.3C0 498.7 13.3 512 29.7 512H418.3c16.4 0 29.7-13.3 29.7-29.7C448 383.8 368.2 304 269.7 304H178.3z\" />\n </svg>\n </div>\n <div class=\"avaya-ipo-profile-info\">\n <div class=\"avaya-ipo-primary-number\">\n {{ getDisplayName() }}\n </div>\n <div class=\"avaya-ipo-secondary-info\">\n {{ getPhone() || 'Unknown' }}\n </div>\n <div class=\"avaya-ipo-meta-line\">\n <span>{{ call?.timestamp | date : 'medium' }}</span>\n <span class=\"avaya-ipo-divider\">|</span>\n @if (call?.disconnectSide === 'CALLER' && (call?.duration ?? 0) === 0) {\n <span class=\"avaya-ipo-divider\">|</span>\n <span class=\"avaya-ipo-missed-badge\">\n <svg viewBox=\"0 0 640 512\" width=\"12px\" aria-hidden=\"true\" style=\"fill: currentColor;\">\n <path d=\"M232 0c9.7 0 18.5 5.8 22.2 14.8s1.7 19.3-5.2 26.2l-39 39 58.7 58.7C282.3 152.4 300.8 160 320 160s37.7-7.6 51.3-21.3L503 7c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9L405.3 172.7C382.6 195.3 352 208 320 208s-62.6-12.7-85.3-35.3L176 113.9l-39 39c-6.9 6.9-17.2 8.9-26.2 5.2s-14.8-12.5-14.8-22.2L96 24c0-13.3 10.7-24 24-24L232 0zM51.4 489.9l-35.4-62c-9.7-16.9-8.3-38.1 5.5-51.7C72.6 325.9 178.1 256 320 256s247.4 69.9 298.5 120.2c13.9 13.6 15.2 34.8 5.5 51.7l-35.4 62c-7.4 12.9-22.7 19.1-37 14.8L438.8 470.8c-13.5-4.1-22.8-16.5-22.8-30.6l0-56.2c-62.3-20.8-129.7-20.8-192 0l0 56.2c0 14.1-9.3 26.6-22.8 30.6L88.4 504.7c-14.3 4.3-29.6-1.8-37-14.8z\" />\n </svg>\n <span>Missed Call</span>\n </span>\n }\n </div>\n </div>\n </div>\n\n <div class=\"avaya-ipo-detail-card avaya-ipo-summary-card\">\n <div class=\"avaya-ipo-detail-row\">\n <div class=\"avaya-ipo-label\">Duration</div>\n <div class=\"avaya-ipo-value\">{{ call?.duration ?? 0 }}s</div>\n </div>\n <div class=\"avaya-ipo-detail-row\">\n <div class=\"avaya-ipo-label\">Disconnect Side</div>\n <div class=\"avaya-ipo-value\">{{ call?.disconnectSide || 'N/A' }}</div>\n </div>\n </div>\n\n <div class=\"avaya-ipo-detail-card\">\n <div class=\"avaya-ipo-detail-title\">Call Recording</div>\n <audio\n *ngIf=\"call?.fullCallRecordingUrl; else noRecording\"\n controls\n [src]=\"call?.fullCallRecordingUrl\"\n ></audio>\n <ng-template #noRecording>\n @if ((call?.duration ?? 0) > 0) {\n <button class=\"avaya-ipo-disabled-btn\" disabled>Processing</button>\n } @else {\n <button class=\"avaya-ipo-disabled-btn\" disabled>Recording not available</button>\n }\n </ng-template>\n </div>\n\n <!-- AI Call Analysis \u2014 read from the Interaction record (metadata.aiAnalysis), like snugdesk-app -->\n <div class=\"avaya-ipo-detail-card\">\n <div class=\"avaya-ipo-detail-title-row\">\n <div class=\"avaya-ipo-detail-title\">AI Call Analysis</div>\n <button\n class=\"avaya-ipo-chat-thread-btn\"\n type=\"button\"\n (click)=\"refreshAnalysis()\"\n [disabled]=\"isRefreshing\"\n aria-label=\"Refresh analysis\"\n title=\"Refresh analysis\"\n >\n @if (isRefreshing) {\n <span class=\"avaya-ipo-btn-spinner\" aria-hidden=\"true\"></span>\n } @else {\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" aria-hidden=\"true\" focusable=\"false\">\n <path fill=\"currentColor\"\n d=\"M17.65 6.35A7.96 7.96 0 0 0 12 4a8 8 0 1 0 7.73 10h-2.08A6 6 0 1 1 12 6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z\" />\n </svg>\n }\n </button>\n </div>\n\n @if (hasAnalysis) {\n @if (aiConfidenceScore !== null) {\n <div class=\"avaya-ipo-detail-row\">\n <div class=\"avaya-ipo-label\">AI Confidence</div>\n <div class=\"avaya-ipo-value\">{{ aiConfidenceScore }}%</div>\n </div>\n }\n @if (callType) {\n <div class=\"avaya-ipo-detail-row\">\n <div class=\"avaya-ipo-label\">Call Type</div>\n <div class=\"avaya-ipo-value\">{{ callType }}</div>\n </div>\n }\n @if (customerSentiment) {\n <div class=\"avaya-ipo-detail-row\">\n <div class=\"avaya-ipo-label\">Sentiment</div>\n <div class=\"avaya-ipo-value\">\n {{ customerSentiment }}\n @if (getSentimentEmoji()) {\n <span class=\"avaya-ipo-summary-emoji\">{{ getSentimentEmoji() }}</span>\n }\n </div>\n </div>\n }\n @if (customerIntent) {\n <div class=\"avaya-ipo-detail-row\">\n <div class=\"avaya-ipo-label\">Customer Intent</div>\n <div class=\"avaya-ipo-value\">{{ customerIntent }}</div>\n </div>\n }\n @if (conversationSummary.length) {\n <div class=\"avaya-ipo-analysis-section\">\n <div class=\"avaya-ipo-analysis-heading\">Conversation Summary</div>\n <ul class=\"avaya-ipo-analysis-list\">\n @for (item of conversationSummary; track $index) {\n <li>{{ item }}</li>\n }\n </ul>\n </div>\n }\n @if (keyConcerns.length) {\n <div class=\"avaya-ipo-analysis-section\">\n <div class=\"avaya-ipo-analysis-heading\">Key Concerns</div>\n <ul class=\"avaya-ipo-analysis-list\">\n @for (item of keyConcerns; track $index) {\n <li>{{ item }}</li>\n }\n </ul>\n </div>\n }\n @if (nextActions.length) {\n <div class=\"avaya-ipo-analysis-section\">\n <div class=\"avaya-ipo-analysis-heading\">Next Actions</div>\n <ul class=\"avaya-ipo-analysis-list\">\n @for (item of nextActions; track $index) {\n <li>{{ item }}</li>\n }\n </ul>\n </div>\n }\n } @else if (isAnalysisPending || isRefreshing) {\n <div class=\"avaya-ipo-transcription-skeleton\">\n @for (item of [1,2,3]; track item) {\n <div class=\"avaya-ipo-transcription-skeleton-line\"></div>\n }\n </div>\n <div class=\"avaya-ipo-placeholder\">AI analysis is being generated\u2026 use refresh to check again.</div>\n } @else if (summaryText) {\n <div class=\"avaya-ipo-summary-text\">{{ summaryText }}</div>\n } @else {\n <div class=\"avaya-ipo-placeholder\">No AI analysis available for this call yet.</div>\n }\n @if (refreshError) {\n <div class=\"avaya-ipo-status-error\">{{ refreshError }}</div>\n }\n </div>\n\n <div class=\"avaya-ipo-detail-card avaya-ipo-transcription-card\" [class.avaya-ipo-fullscreen]=\"isTranscriptFullscreen\">\n <div class=\"avaya-ipo-detail-title-row\">\n <div class=\"avaya-ipo-detail-title\">Transcript</div>\n @if (transcriptLines.length) {\n <button\n class=\"avaya-ipo-chat-thread-btn\"\n type=\"button\"\n (click)=\"toggleTranscriptFullscreen()\"\n [attr.aria-label]=\"isTranscriptFullscreen ? 'Exit full screen' : 'Enter full screen'\"\n >\n @if (isTranscriptFullscreen) {\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path\n fill=\"currentColor\"\n d=\"M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z\"\n />\n </svg>\n } @else {\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path\n fill=\"currentColor\"\n d=\"M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z\"\n />\n </svg>\n }\n </button>\n }\n </div>\n @if (transcriptLines.length) {\n <div class=\"avaya-ipo-chat-thread\">\n @for (line of transcriptLines; track $index) {\n <div class=\"avaya-ipo-chat-bubble\" [class.avaya-ipo-agent]=\"(line.speaker || '').toLowerCase() === 'agent'\">\n <div class=\"avaya-ipo-chat-speaker\">{{ getTranscriptSpeaker(line) }}</div>\n <div class=\"avaya-ipo-chat-text\">{{ line.text || '' }}</div>\n </div>\n }\n </div>\n } @else if (isAnalysisPending || isRefreshing) {\n <div class=\"avaya-ipo-transcription-skeleton\">\n @for (item of [1,2,3,4,5]; track item) {\n <div class=\"avaya-ipo-transcription-skeleton-line\"></div>\n }\n </div>\n } @else {\n <div class=\"avaya-ipo-placeholder\">No transcript available.</div>\n }\n </div>\n</div>\n", styles: [":host{display:block;width:100%;box-sizing:border-box}.avaya-ipo-call-profile{display:flex;flex-direction:column;gap:14px;font-family:Open Sans,Helvetica Neue,Arial,sans-serif;color:var(--sd-text, #333)}.avaya-ipo-profile-header{display:flex;gap:12px;align-items:center;padding:12px;border-radius:var(--sd-radius-lg, 14px);background:var(--sd-surface-muted, #f7f7f7)}.avaya-ipo-avatar{width:52px;height:52px;border-radius:var(--sd-radius-lg, 14px);background:var(--sd-avatar, #eceff3);display:flex;align-items:center;justify-content:center;font-weight:800;font-size:14px;color:var(--sd-text, #333)}.avaya-ipo-profile-info{display:flex;flex-direction:column;gap:6px}.avaya-ipo-primary-number{font-size:16px;font-weight:800;color:var(--sd-text, #333)}.avaya-ipo-secondary-info{font-size:12px;color:var(--sd-text-muted, #5f6368)}.avaya-ipo-meta-line{display:flex;gap:6px;align-items:center;font-size:12px;color:var(--sd-text-muted, #5f6368);flex-wrap:wrap}.avaya-ipo-meta-line .avaya-ipo-divider{color:var(--sd-text-subtle, #9ca3af)}.avaya-ipo-meta-line .avaya-ipo-missed-badge{color:var(--sd-call-end, #e5484d);font-weight:700}.avaya-ipo-detail-card{display:flex;flex-direction:column;gap:10px;padding:12px;border-radius:var(--sd-radius-lg, 14px);background:var(--sd-card, #fff);border:1px solid var(--sd-border, #e3e3e3)}.avaya-ipo-detail-card audio{width:100%}.avaya-ipo-disabled-btn{width:100%;border:1px solid var(--sd-border, #e3e3e3);background:var(--sd-surface-muted, #f7f7f7);color:var(--sd-text-subtle, #9ca3af);padding:10px;border-radius:var(--sd-radius, 10px);font-weight:600;cursor:not-allowed}.avaya-ipo-detail-title{font-weight:800;color:var(--sd-text, #333);font-size:14px}.avaya-ipo-detail-title-row{display:flex;align-items:center;justify-content:space-between}.avaya-ipo-btn-spinner{width:14px;height:14px;border-radius:var(--sd-radius-pill, 999px);border:2px solid color-mix(in srgb,var(--sd-accent-contrast, #fff) 35%,transparent);border-top-color:var(--sd-accent-contrast, #fff);animation:spin .8s linear infinite}.avaya-ipo-status-error{color:var(--sd-call-end, #e5484d);font-size:12px;font-weight:600}.avaya-ipo-transcription-skeleton{display:flex;flex-direction:column;gap:10px}.avaya-ipo-transcription-skeleton-line{height:10px;border-radius:var(--sd-radius-pill, 999px);background:linear-gradient(90deg,var(--sd-surface-sunken, #e8e8e8) 0%,var(--sd-surface-muted, #f7f7f7) 45%,var(--sd-surface-sunken, #e8e8e8) 100%);background-size:200% 100%;animation:skeleton-shine 1.2s ease-in-out infinite}.avaya-ipo-transcription-skeleton-line:nth-child(2){width:85%}.avaya-ipo-transcription-skeleton-line:nth-child(3){width:90%}.avaya-ipo-transcription-skeleton-line:nth-child(4){width:70%}.avaya-ipo-transcription-skeleton-line:nth-child(5){width:60%}.avaya-ipo-chat-thread{display:flex;flex-direction:column;gap:8px}.avaya-ipo-chat-thread-btn{border:1px solid var(--sd-border, #e3e3e3);background:var(--sd-surface, #fff);color:var(--sd-text, #333);padding:6px;border-radius:var(--sd-radius-sm, 8px);font-weight:700;cursor:pointer;font-size:12px;width:32px;height:32px;display:inline-flex;align-items:center;justify-content:center;transition:background .15s ease,border-color .15s ease}.avaya-ipo-chat-thread-btn:hover{background:var(--sd-surface-muted, #f7f7f7)}.avaya-ipo-chat-thread-btn svg{width:16px;height:16px}.avaya-ipo-transcription-card.avaya-ipo-fullscreen{position:absolute;inset:0;background:var(--sd-surface, #fff);padding:16px;z-index:1000;animation:transcript-expand .25s ease;border-radius:0}:host-context(.avaya-ipo-mode-standalone) .avaya-ipo-transcription-card.avaya-ipo-fullscreen{position:fixed}.avaya-ipo-transcription-card.avaya-ipo-fullscreen .avaya-ipo-detail-title-row{position:sticky;top:0;background:var(--sd-surface, #fff);z-index:2}.avaya-ipo-transcription-card.avaya-ipo-fullscreen .avaya-ipo-chat-thread{flex:1 1 auto;overflow-y:auto;padding-bottom:8px}.avaya-ipo-detail-card{transition:transform .25s ease,opacity .25s ease}.avaya-ipo-transcription-card:not(.avaya-ipo-fullscreen){animation:transcript-collapse .2s ease}.avaya-ipo-chat-bubble{align-self:flex-start;max-width:86%;background:var(--sd-surface-muted, #f7f7f7);border-radius:var(--sd-radius, 10px);padding:8px 10px;border:1px solid var(--sd-border, #e3e3e3)}.avaya-ipo-chat-bubble.avaya-ipo-agent{align-self:flex-end;background:var(--sd-accent-soft, #fff1ea);border-color:color-mix(in srgb,var(--sd-accent, #ff6633) 35%,transparent)}.avaya-ipo-chat-speaker{font-size:11px;font-weight:700;color:var(--sd-text-muted, #5f6368);text-transform:capitalize;margin-bottom:4px}.avaya-ipo-chat-text,.avaya-ipo-summary-text{font-size:13px;color:var(--sd-text, #333);line-height:1.4}.avaya-ipo-summary-emoji{margin-left:6px;font-size:14px}.avaya-ipo-summary-card{border:1px solid var(--sd-border, #e3e3e3);border-radius:var(--sd-radius, 10px);padding:10px;background:var(--sd-surface-muted, #f7f7f7)}@keyframes transcript-expand{0%{transform:scale(.98);opacity:.6}to{transform:scale(1);opacity:1}}@keyframes transcript-collapse{0%{transform:scale(1.02);opacity:.6}to{transform:scale(1);opacity:1}}@keyframes spin{to{transform:rotate(360deg)}}@keyframes skeleton-shine{0%{background-position:200% 0}to{background-position:-200% 0}}.avaya-ipo-placeholder{font-size:13px;color:var(--sd-text-muted, #5f6368)}.avaya-ipo-analysis-section{margin-top:10px}.avaya-ipo-analysis-heading{font-size:12px;font-weight:700;color:var(--sd-text-muted, #5f6368);text-transform:uppercase;letter-spacing:.4px;margin-bottom:4px}.avaya-ipo-analysis-list{margin:0;padding-left:18px;display:flex;flex-direction:column;gap:4px}.avaya-ipo-analysis-list li{font-size:13px;color:var(--sd-text, #333);line-height:1.45}.avaya-ipo-detail-row{display:flex;justify-content:space-between;align-items:center}.avaya-ipo-label{font-weight:700;color:var(--sd-text-muted, #5f6368);font-size:13px}.avaya-ipo-value{color:var(--sd-text, #333);text-align:right;font-weight:600;font-size:13px}\n"], dependencies: [{ kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i2.DatePipe, name: "date" }] });
|
|
1418
1516
|
}
|
|
1419
1517
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: CallDetailsComponent, decorators: [{
|
|
1420
1518
|
type: Component,
|
|
1421
|
-
args: [{ selector: 'app-call-details', standalone: false, template: "<div class=\"avaya-ipo-call-profile\">\n <div class=\"avaya-ipo-profile-header\">\n <div class=\"avaya-ipo-avatar\">\n <svg viewBox=\"0 0 448 512\" width=\"28\" height=\"28\" fill=\"currentColor\" aria-hidden=\"true\">\n <path\n d=\"M224 256A128 128 0 1 0 224 0a128 128 0 1 0 0 256zm-45.7 48C79.8 304 0 383.8 0 482.3C0 498.7 13.3 512 29.7 512H418.3c16.4 0 29.7-13.3 29.7-29.7C448 383.8 368.2 304 269.7 304H178.3z\" />\n </svg>\n </div>\n <div class=\"avaya-ipo-profile-info\">\n <div class=\"avaya-ipo-primary-number\">\n {{ getDisplayName() }}\n </div>\n <div class=\"avaya-ipo-secondary-info\">\n {{ getPhone() || 'Unknown' }}\n </div>\n <div class=\"avaya-ipo-meta-line\">\n <span>{{ call?.timestamp | date : 'medium' }}</span>\n <span class=\"avaya-ipo-divider\">|</span>\n @if (call?.disconnectSide === 'CALLER' && (call?.duration ?? 0) === 0) {\n <span class=\"avaya-ipo-divider\">|</span>\n <span class=\"avaya-ipo-missed-badge\">\n <svg viewBox=\"0 0 640 512\" width=\"12px\" aria-hidden=\"true\" style=\"fill: currentColor;\">\n <path d=\"M232 0c9.7 0 18.5 5.8 22.2 14.8s1.7 19.3-5.2 26.2l-39 39 58.7 58.7C282.3 152.4 300.8 160 320 160s37.7-7.6 51.3-21.3L503 7c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9L405.3 172.7C382.6 195.3 352 208 320 208s-62.6-12.7-85.3-35.3L176 113.9l-39 39c-6.9 6.9-17.2 8.9-26.2 5.2s-14.8-12.5-14.8-22.2L96 24c0-13.3 10.7-24 24-24L232 0zM51.4 489.9l-35.4-62c-9.7-16.9-8.3-38.1 5.5-51.7C72.6 325.9 178.1 256 320 256s247.4 69.9 298.5 120.2c13.9 13.6 15.2 34.8 5.5 51.7l-35.4 62c-7.4 12.9-22.7 19.1-37 14.8L438.8 470.8c-13.5-4.1-22.8-16.5-22.8-30.6l0-56.2c-62.3-20.8-129.7-20.8-192 0l0 56.2c0 14.1-9.3 26.6-22.8 30.6L88.4 504.7c-14.3 4.3-29.6-1.8-37-14.8z\" />\n </svg>\n <span>Missed Call</span>\n </span>\n }\n </div>\n </div>\n </div>\n\n <div class=\"avaya-ipo-detail-card avaya-ipo-summary-card\">\n <div class=\"avaya-ipo-detail-row\">\n <div class=\"avaya-ipo-label\">Duration</div>\n <div class=\"avaya-ipo-value\">{{ call?.duration ?? 0 }}s</div>\n </div>\n <div class=\"avaya-ipo-detail-row\">\n <div class=\"avaya-ipo-label\">Disconnect Side</div>\n <div class=\"avaya-ipo-value\">{{ call?.disconnectSide || 'N/A' }}</div>\n </div>\n <div class=\"avaya-ipo-detail-row\">\n <div class=\"avaya-ipo-label\">Country</div>\n <div class=\"avaya-ipo-value\">\n {{\n call?.phoneNumber?.country?.nameEn ||\n call?.phoneNumber?.lookupPhoneNumber?.country ||\n 'N/A'\n }}\n </div>\n </div>\n <div class=\"avaya-ipo-detail-row\">\n <div class=\"avaya-ipo-label\">Provider</div>\n <div class=\"avaya-ipo-value\">\n {{\n call?.phoneNumber?.lookupPhoneNumber?.['prefix-network'] ||\n call?.phoneNumber?.lookupPhoneNumber?.provider ||\n 'N/A'\n }}\n </div>\n </div>\n </div>\n\n <div class=\"avaya-ipo-detail-card avaya-ipo-transcription-card\" [class.avaya-ipo-fullscreen]=\"isTranscriptFullscreen\">\n <div class=\"avaya-ipo-detail-title\">Call Recording</div>\n <audio\n *ngIf=\"call?.fullCallRecordingUrl; else noRecording\"\n controls\n [src]=\"call?.fullCallRecordingUrl\"\n ></audio>\n <ng-template #noRecording>\n @if ((call?.duration ?? 0) > 0) {\n <button class=\"avaya-ipo-disabled-btn\" disabled>Processing</button>\n } @else {\n <button class=\"avaya-ipo-disabled-btn\" disabled>Recording not available</button>\n }\n </ng-template>\n </div>\n\n @if (!hasGeneratedTranscript) {\n <div class=\"avaya-ipo-transcription-actions avaya-ipo-outside\">\n <button class=\"avaya-ipo-action-btn\" type=\"button\" [disabled]=\"isTranscribing\" (click)=\"runTranscription()\">\n <span class=\"avaya-ipo-btn-content\">\n @if (isTranscribing) {\n <span class=\"avaya-ipo-btn-spinner\" aria-hidden=\"true\"></span>\n }\n <span>{{ isTranscribing ? 'Generating...' : 'Run AI Call Analysis' }}</span>\n </span>\n </button>\n @if (transcriptionError) {\n <div class=\"avaya-ipo-status-error\">{{ transcriptionError }}</div>\n }\n </div>\n }\n\n <div class=\"avaya-ipo-detail-card\">\n <div class=\"avaya-ipo-detail-title\">Call Summary</div>\n @if (customerBehavior) {\n <div class=\"avaya-ipo-summary-sentiment\">\n Customer Behavior: {{ customerBehavior }}\n @if (getBehaviorEmoji()) {\n <span class=\"avaya-ipo-summary-emoji\">{{ getBehaviorEmoji() }}</span>\n }\n </div>\n }\n @if (summaryText) {\n <div class=\"avaya-ipo-summary-text\">{{ summaryText }}</div>\n } @else {\n <div class=\"avaya-ipo-placeholder\">No summary available.</div>\n }\n </div>\n\n <div class=\"avaya-ipo-detail-card avaya-ipo-transcription-card\" [class.avaya-ipo-fullscreen]=\"isTranscriptFullscreen\">\n <div class=\"avaya-ipo-detail-title-row\">\n <div class=\"avaya-ipo-detail-title\">AI Call Transcription</div>\n @if (transcriptLines?.length) {\n <button\n class=\"avaya-ipo-chat-thread-btn\"\n type=\"button\"\n (click)=\"toggleTranscriptFullscreen()\"\n [attr.aria-label]=\"isTranscriptFullscreen ? 'Exit full screen' : 'Enter full screen'\"\n >\n @if (isTranscriptFullscreen) {\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path\n fill=\"currentColor\"\n d=\"M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z\"\n />\n </svg>\n } @else {\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path\n fill=\"currentColor\"\n d=\"M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z\"\n />\n </svg>\n }\n </button>\n }\n </div>\n @if (call?.fullCallRecordingUrl) {\n @if (transcriptLines?.length) {\n <div class=\"avaya-ipo-chat-thread\">\n @for (line of transcriptLines; track $index) {\n <div class=\"avaya-ipo-chat-bubble\" [class.avaya-ipo-agent]=\"(line.speaker || '').toLowerCase() === 'agent'\">\n <div class=\"avaya-ipo-chat-speaker\">{{ getTranscriptSpeaker(line) }}</div>\n <div class=\"avaya-ipo-chat-text\">{{ line.text || '' }}</div>\n </div>\n }\n </div>\n } @else if (isTranscribing) {\n <div class=\"avaya-ipo-transcription-skeleton\">\n @for (item of [1,2,3,4,5]; track item) {\n <div class=\"avaya-ipo-transcription-skeleton-line\"></div>\n }\n </div>\n } @else {\n <div class=\"avaya-ipo-placeholder\">No transcription available.</div>\n }\n\n } @else {\n <div class=\"avaya-ipo-placeholder\">No recording available for transcription.</div>\n }\n </div>\n</div>\n", styles: [":host{display:block;width:100%;box-sizing:border-box}.avaya-ipo-call-profile{display:flex;flex-direction:column;gap:14px;font-family:Open Sans,Helvetica Neue,Arial,sans-serif;color:var(--sd-text, #333)}.avaya-ipo-profile-header{display:flex;gap:12px;align-items:center;padding:12px;border-radius:var(--sd-radius-lg, 14px);background:var(--sd-surface-muted, #f7f7f7)}.avaya-ipo-avatar{width:52px;height:52px;border-radius:var(--sd-radius-lg, 14px);background:var(--sd-avatar, #eceff3);display:flex;align-items:center;justify-content:center;font-weight:800;font-size:14px;color:var(--sd-text, #333)}.avaya-ipo-profile-info{display:flex;flex-direction:column;gap:6px}.avaya-ipo-primary-number{font-size:16px;font-weight:800;color:var(--sd-text, #333)}.avaya-ipo-secondary-info{font-size:12px;color:var(--sd-text-muted, #5f6368)}.avaya-ipo-meta-line{display:flex;gap:6px;align-items:center;font-size:12px;color:var(--sd-text-muted, #5f6368);flex-wrap:wrap}.avaya-ipo-meta-line .avaya-ipo-divider{color:var(--sd-text-subtle, #9ca3af)}.avaya-ipo-meta-line .avaya-ipo-dir.avaya-ipo-inbound{color:var(--sd-call-accept, #1aa251)}.avaya-ipo-meta-line .avaya-ipo-dir.avaya-ipo-outbound{color:var(--sd-text-muted, #5f6368)}.avaya-ipo-meta-line .avaya-ipo-missed-badge{color:var(--sd-call-end, #e5484d);font-weight:700}.avaya-ipo-detail-card{display:flex;flex-direction:column;gap:10px;padding:12px;border-radius:var(--sd-radius-lg, 14px);background:var(--sd-card, #fff);border:1px solid var(--sd-border, #e3e3e3)}.avaya-ipo-summary-card{order:1}.avaya-ipo-transcription-card{order:2}.avaya-ipo-detail-card audio{width:100%}.avaya-ipo-disabled-btn{width:100%;border:1px solid var(--sd-border, #e3e3e3);background:var(--sd-surface-muted, #f7f7f7);color:var(--sd-text-subtle, #9ca3af);padding:10px;border-radius:var(--sd-radius, 10px);font-weight:600;cursor:not-allowed}.avaya-ipo-detail-title{font-weight:800;color:var(--sd-text, #333);font-size:14px}.avaya-ipo-detail-title-row{display:flex;align-items:center;justify-content:space-between}.avaya-ipo-transcription-actions{display:flex;flex-direction:column;gap:8px}.avaya-ipo-transcription-actions.avaya-ipo-outside{padding:0 4px}.avaya-ipo-detail-card.avaya-ipo-is-inactive{opacity:.45;filter:grayscale(.2);pointer-events:none}.avaya-ipo-action-btn{border:none;background:var(--sd-accent, #ff6633);color:var(--sd-accent-contrast, #fff);padding:10px 14px;border-radius:var(--sd-radius, 10px);font-weight:700;cursor:pointer;transition:background .15s ease,box-shadow .15s ease}.avaya-ipo-btn-content{display:inline-flex;align-items:center;gap:8px}.avaya-ipo-btn-spinner{width:14px;height:14px;border-radius:var(--sd-radius-pill, 999px);border:2px solid color-mix(in srgb,var(--sd-accent-contrast, #fff) 35%,transparent);border-top-color:var(--sd-accent-contrast, #fff);animation:spin .8s linear infinite}.avaya-ipo-action-btn:hover:not(:disabled){background:var(--sd-accent-strong, #ff5805)}.avaya-ipo-action-btn:focus-visible{outline:none;box-shadow:var(--sd-focus-ring, 0 0 0 3px rgba(255, 102, 51, .22))}.avaya-ipo-action-btn:disabled{background:var(--sd-surface-sunken, #e8e8e8);color:var(--sd-text-subtle, #9ca3af);cursor:not-allowed}.avaya-ipo-status-error{color:var(--sd-call-end, #e5484d);font-size:12px;font-weight:600}.avaya-ipo-transcription-skeleton{display:flex;flex-direction:column;gap:10px}.avaya-ipo-transcription-skeleton-line{height:10px;border-radius:var(--sd-radius-pill, 999px);background:linear-gradient(90deg,var(--sd-surface-sunken, #e8e8e8) 0%,var(--sd-surface-muted, #f7f7f7) 45%,var(--sd-surface-sunken, #e8e8e8) 100%);background-size:200% 100%;animation:skeleton-shine 1.2s ease-in-out infinite}.avaya-ipo-transcription-skeleton-line:nth-child(2){width:85%}.avaya-ipo-transcription-skeleton-line:nth-child(3){width:90%}.avaya-ipo-transcription-skeleton-line:nth-child(4){width:70%}.avaya-ipo-transcription-skeleton-line:nth-child(5){width:60%}.avaya-ipo-chat-thread{display:flex;flex-direction:column;gap:8px}.avaya-ipo-chat-thread-btn{border:1px solid var(--sd-border, #e3e3e3);background:var(--sd-surface, #fff);color:var(--sd-text, #333);padding:6px;border-radius:var(--sd-radius-sm, 8px);font-weight:700;cursor:pointer;font-size:12px;width:32px;height:32px;display:inline-flex;align-items:center;justify-content:center;transition:background .15s ease,border-color .15s ease}.avaya-ipo-chat-thread-btn:hover{background:var(--sd-surface-muted, #f7f7f7)}.avaya-ipo-chat-thread-btn svg{width:16px;height:16px}.avaya-ipo-transcription-card.avaya-ipo-fullscreen{position:absolute;inset:0;background:var(--sd-surface, #fff);padding:16px;z-index:1000;animation:transcript-expand .25s ease;border-radius:0}:host-context(.avaya-ipo-mode-standalone) .avaya-ipo-transcription-card.avaya-ipo-fullscreen{position:fixed}.avaya-ipo-transcription-card.avaya-ipo-fullscreen .avaya-ipo-detail-title-row{position:sticky;top:0;background:var(--sd-surface, #fff);z-index:2}.avaya-ipo-transcription-card.avaya-ipo-fullscreen .avaya-ipo-chat-thread{flex:1 1 auto;overflow-y:auto;padding-bottom:8px}.avaya-ipo-detail-card{transition:transform .25s ease,opacity .25s ease}.avaya-ipo-transcription-card:not(.avaya-ipo-fullscreen){animation:transcript-collapse .2s ease}.avaya-ipo-chat-bubble{align-self:flex-start;max-width:86%;background:var(--sd-surface-muted, #f7f7f7);border-radius:var(--sd-radius, 10px);padding:8px 10px;border:1px solid var(--sd-border, #e3e3e3)}.avaya-ipo-chat-bubble.avaya-ipo-agent{align-self:flex-end;background:var(--sd-accent-soft, #fff1ea);border-color:color-mix(in srgb,var(--sd-accent, #ff6633) 35%,transparent)}.avaya-ipo-chat-speaker{font-size:11px;font-weight:700;color:var(--sd-text-muted, #5f6368);text-transform:capitalize;margin-bottom:4px}.avaya-ipo-chat-text,.avaya-ipo-summary-text{font-size:13px;color:var(--sd-text, #333);line-height:1.4}.avaya-ipo-summary-sentiment{font-size:12px;font-weight:700;color:var(--sd-text, #333);margin-bottom:6px}.avaya-ipo-summary-emoji{margin-left:6px;font-size:14px}.avaya-ipo-summary-grid,.avaya-ipo-qa-grid{display:grid;gap:8px}.avaya-ipo-summary-card,.avaya-ipo-qa-card{border:1px solid var(--sd-border, #e3e3e3);border-radius:var(--sd-radius, 10px);padding:10px;background:var(--sd-surface-muted, #f7f7f7)}.avaya-ipo-summary-title,.avaya-ipo-qa-title{font-weight:700;color:var(--sd-text, #333);font-size:12px;margin-bottom:6px;text-transform:capitalize}.avaya-ipo-summary-text,.avaya-ipo-qa-answer{font-size:13px;color:var(--sd-text, #333);line-height:1.4}.avaya-ipo-qa-reason{margin-top:6px;font-size:12px;color:var(--sd-text-muted, #5f6368);line-height:1.4}.avaya-ipo-sentiment-score{color:var(--sd-text-muted, #5f6368);font-size:12px}@keyframes transcript-expand{0%{transform:scale(.98);opacity:.6}to{transform:scale(1);opacity:1}}@keyframes transcript-collapse{0%{transform:scale(1.02);opacity:.6}to{transform:scale(1);opacity:1}}@keyframes spin{to{transform:rotate(360deg)}}@keyframes skeleton-shine{0%{background-position:200% 0}to{background-position:-200% 0}}.avaya-ipo-placeholder{font-size:13px;color:var(--sd-text-muted, #5f6368)}.avaya-ipo-detail-row{display:flex;justify-content:space-between;align-items:center}.avaya-ipo-label{font-weight:700;color:var(--sd-text-muted, #5f6368);font-size:13px}.avaya-ipo-value{color:var(--sd-text, #333);text-align:right;font-weight:600;font-size:13px}.avaya-ipo-value.avaya-ipo-mono{font-family:SFMono-Regular,Menlo,Consolas,monospace;font-size:12px;color:var(--sd-text-muted, #5f6368)}\n"] }]
|
|
1422
|
-
}], ctorParameters: () => [{ type: i1.
|
|
1519
|
+
args: [{ selector: 'app-call-details', standalone: false, template: "<div class=\"avaya-ipo-call-profile\">\n <div class=\"avaya-ipo-profile-header\">\n <div class=\"avaya-ipo-avatar\">\n <svg viewBox=\"0 0 448 512\" width=\"28\" height=\"28\" fill=\"currentColor\" aria-hidden=\"true\">\n <path\n d=\"M224 256A128 128 0 1 0 224 0a128 128 0 1 0 0 256zm-45.7 48C79.8 304 0 383.8 0 482.3C0 498.7 13.3 512 29.7 512H418.3c16.4 0 29.7-13.3 29.7-29.7C448 383.8 368.2 304 269.7 304H178.3z\" />\n </svg>\n </div>\n <div class=\"avaya-ipo-profile-info\">\n <div class=\"avaya-ipo-primary-number\">\n {{ getDisplayName() }}\n </div>\n <div class=\"avaya-ipo-secondary-info\">\n {{ getPhone() || 'Unknown' }}\n </div>\n <div class=\"avaya-ipo-meta-line\">\n <span>{{ call?.timestamp | date : 'medium' }}</span>\n <span class=\"avaya-ipo-divider\">|</span>\n @if (call?.disconnectSide === 'CALLER' && (call?.duration ?? 0) === 0) {\n <span class=\"avaya-ipo-divider\">|</span>\n <span class=\"avaya-ipo-missed-badge\">\n <svg viewBox=\"0 0 640 512\" width=\"12px\" aria-hidden=\"true\" style=\"fill: currentColor;\">\n <path d=\"M232 0c9.7 0 18.5 5.8 22.2 14.8s1.7 19.3-5.2 26.2l-39 39 58.7 58.7C282.3 152.4 300.8 160 320 160s37.7-7.6 51.3-21.3L503 7c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9L405.3 172.7C382.6 195.3 352 208 320 208s-62.6-12.7-85.3-35.3L176 113.9l-39 39c-6.9 6.9-17.2 8.9-26.2 5.2s-14.8-12.5-14.8-22.2L96 24c0-13.3 10.7-24 24-24L232 0zM51.4 489.9l-35.4-62c-9.7-16.9-8.3-38.1 5.5-51.7C72.6 325.9 178.1 256 320 256s247.4 69.9 298.5 120.2c13.9 13.6 15.2 34.8 5.5 51.7l-35.4 62c-7.4 12.9-22.7 19.1-37 14.8L438.8 470.8c-13.5-4.1-22.8-16.5-22.8-30.6l0-56.2c-62.3-20.8-129.7-20.8-192 0l0 56.2c0 14.1-9.3 26.6-22.8 30.6L88.4 504.7c-14.3 4.3-29.6-1.8-37-14.8z\" />\n </svg>\n <span>Missed Call</span>\n </span>\n }\n </div>\n </div>\n </div>\n\n <div class=\"avaya-ipo-detail-card avaya-ipo-summary-card\">\n <div class=\"avaya-ipo-detail-row\">\n <div class=\"avaya-ipo-label\">Duration</div>\n <div class=\"avaya-ipo-value\">{{ call?.duration ?? 0 }}s</div>\n </div>\n <div class=\"avaya-ipo-detail-row\">\n <div class=\"avaya-ipo-label\">Disconnect Side</div>\n <div class=\"avaya-ipo-value\">{{ call?.disconnectSide || 'N/A' }}</div>\n </div>\n </div>\n\n <div class=\"avaya-ipo-detail-card\">\n <div class=\"avaya-ipo-detail-title\">Call Recording</div>\n <audio\n *ngIf=\"call?.fullCallRecordingUrl; else noRecording\"\n controls\n [src]=\"call?.fullCallRecordingUrl\"\n ></audio>\n <ng-template #noRecording>\n @if ((call?.duration ?? 0) > 0) {\n <button class=\"avaya-ipo-disabled-btn\" disabled>Processing</button>\n } @else {\n <button class=\"avaya-ipo-disabled-btn\" disabled>Recording not available</button>\n }\n </ng-template>\n </div>\n\n <!-- AI Call Analysis \u2014 read from the Interaction record (metadata.aiAnalysis), like snugdesk-app -->\n <div class=\"avaya-ipo-detail-card\">\n <div class=\"avaya-ipo-detail-title-row\">\n <div class=\"avaya-ipo-detail-title\">AI Call Analysis</div>\n <button\n class=\"avaya-ipo-chat-thread-btn\"\n type=\"button\"\n (click)=\"refreshAnalysis()\"\n [disabled]=\"isRefreshing\"\n aria-label=\"Refresh analysis\"\n title=\"Refresh analysis\"\n >\n @if (isRefreshing) {\n <span class=\"avaya-ipo-btn-spinner\" aria-hidden=\"true\"></span>\n } @else {\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" aria-hidden=\"true\" focusable=\"false\">\n <path fill=\"currentColor\"\n d=\"M17.65 6.35A7.96 7.96 0 0 0 12 4a8 8 0 1 0 7.73 10h-2.08A6 6 0 1 1 12 6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z\" />\n </svg>\n }\n </button>\n </div>\n\n @if (hasAnalysis) {\n @if (aiConfidenceScore !== null) {\n <div class=\"avaya-ipo-detail-row\">\n <div class=\"avaya-ipo-label\">AI Confidence</div>\n <div class=\"avaya-ipo-value\">{{ aiConfidenceScore }}%</div>\n </div>\n }\n @if (callType) {\n <div class=\"avaya-ipo-detail-row\">\n <div class=\"avaya-ipo-label\">Call Type</div>\n <div class=\"avaya-ipo-value\">{{ callType }}</div>\n </div>\n }\n @if (customerSentiment) {\n <div class=\"avaya-ipo-detail-row\">\n <div class=\"avaya-ipo-label\">Sentiment</div>\n <div class=\"avaya-ipo-value\">\n {{ customerSentiment }}\n @if (getSentimentEmoji()) {\n <span class=\"avaya-ipo-summary-emoji\">{{ getSentimentEmoji() }}</span>\n }\n </div>\n </div>\n }\n @if (customerIntent) {\n <div class=\"avaya-ipo-detail-row\">\n <div class=\"avaya-ipo-label\">Customer Intent</div>\n <div class=\"avaya-ipo-value\">{{ customerIntent }}</div>\n </div>\n }\n @if (conversationSummary.length) {\n <div class=\"avaya-ipo-analysis-section\">\n <div class=\"avaya-ipo-analysis-heading\">Conversation Summary</div>\n <ul class=\"avaya-ipo-analysis-list\">\n @for (item of conversationSummary; track $index) {\n <li>{{ item }}</li>\n }\n </ul>\n </div>\n }\n @if (keyConcerns.length) {\n <div class=\"avaya-ipo-analysis-section\">\n <div class=\"avaya-ipo-analysis-heading\">Key Concerns</div>\n <ul class=\"avaya-ipo-analysis-list\">\n @for (item of keyConcerns; track $index) {\n <li>{{ item }}</li>\n }\n </ul>\n </div>\n }\n @if (nextActions.length) {\n <div class=\"avaya-ipo-analysis-section\">\n <div class=\"avaya-ipo-analysis-heading\">Next Actions</div>\n <ul class=\"avaya-ipo-analysis-list\">\n @for (item of nextActions; track $index) {\n <li>{{ item }}</li>\n }\n </ul>\n </div>\n }\n } @else if (isAnalysisPending || isRefreshing) {\n <div class=\"avaya-ipo-transcription-skeleton\">\n @for (item of [1,2,3]; track item) {\n <div class=\"avaya-ipo-transcription-skeleton-line\"></div>\n }\n </div>\n <div class=\"avaya-ipo-placeholder\">AI analysis is being generated\u2026 use refresh to check again.</div>\n } @else if (summaryText) {\n <div class=\"avaya-ipo-summary-text\">{{ summaryText }}</div>\n } @else {\n <div class=\"avaya-ipo-placeholder\">No AI analysis available for this call yet.</div>\n }\n @if (refreshError) {\n <div class=\"avaya-ipo-status-error\">{{ refreshError }}</div>\n }\n </div>\n\n <div class=\"avaya-ipo-detail-card avaya-ipo-transcription-card\" [class.avaya-ipo-fullscreen]=\"isTranscriptFullscreen\">\n <div class=\"avaya-ipo-detail-title-row\">\n <div class=\"avaya-ipo-detail-title\">Transcript</div>\n @if (transcriptLines.length) {\n <button\n class=\"avaya-ipo-chat-thread-btn\"\n type=\"button\"\n (click)=\"toggleTranscriptFullscreen()\"\n [attr.aria-label]=\"isTranscriptFullscreen ? 'Exit full screen' : 'Enter full screen'\"\n >\n @if (isTranscriptFullscreen) {\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path\n fill=\"currentColor\"\n d=\"M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z\"\n />\n </svg>\n } @else {\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path\n fill=\"currentColor\"\n d=\"M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z\"\n />\n </svg>\n }\n </button>\n }\n </div>\n @if (transcriptLines.length) {\n <div class=\"avaya-ipo-chat-thread\">\n @for (line of transcriptLines; track $index) {\n <div class=\"avaya-ipo-chat-bubble\" [class.avaya-ipo-agent]=\"(line.speaker || '').toLowerCase() === 'agent'\">\n <div class=\"avaya-ipo-chat-speaker\">{{ getTranscriptSpeaker(line) }}</div>\n <div class=\"avaya-ipo-chat-text\">{{ line.text || '' }}</div>\n </div>\n }\n </div>\n } @else if (isAnalysisPending || isRefreshing) {\n <div class=\"avaya-ipo-transcription-skeleton\">\n @for (item of [1,2,3,4,5]; track item) {\n <div class=\"avaya-ipo-transcription-skeleton-line\"></div>\n }\n </div>\n } @else {\n <div class=\"avaya-ipo-placeholder\">No transcript available.</div>\n }\n </div>\n</div>\n", styles: [":host{display:block;width:100%;box-sizing:border-box}.avaya-ipo-call-profile{display:flex;flex-direction:column;gap:14px;font-family:Open Sans,Helvetica Neue,Arial,sans-serif;color:var(--sd-text, #333)}.avaya-ipo-profile-header{display:flex;gap:12px;align-items:center;padding:12px;border-radius:var(--sd-radius-lg, 14px);background:var(--sd-surface-muted, #f7f7f7)}.avaya-ipo-avatar{width:52px;height:52px;border-radius:var(--sd-radius-lg, 14px);background:var(--sd-avatar, #eceff3);display:flex;align-items:center;justify-content:center;font-weight:800;font-size:14px;color:var(--sd-text, #333)}.avaya-ipo-profile-info{display:flex;flex-direction:column;gap:6px}.avaya-ipo-primary-number{font-size:16px;font-weight:800;color:var(--sd-text, #333)}.avaya-ipo-secondary-info{font-size:12px;color:var(--sd-text-muted, #5f6368)}.avaya-ipo-meta-line{display:flex;gap:6px;align-items:center;font-size:12px;color:var(--sd-text-muted, #5f6368);flex-wrap:wrap}.avaya-ipo-meta-line .avaya-ipo-divider{color:var(--sd-text-subtle, #9ca3af)}.avaya-ipo-meta-line .avaya-ipo-missed-badge{color:var(--sd-call-end, #e5484d);font-weight:700}.avaya-ipo-detail-card{display:flex;flex-direction:column;gap:10px;padding:12px;border-radius:var(--sd-radius-lg, 14px);background:var(--sd-card, #fff);border:1px solid var(--sd-border, #e3e3e3)}.avaya-ipo-detail-card audio{width:100%}.avaya-ipo-disabled-btn{width:100%;border:1px solid var(--sd-border, #e3e3e3);background:var(--sd-surface-muted, #f7f7f7);color:var(--sd-text-subtle, #9ca3af);padding:10px;border-radius:var(--sd-radius, 10px);font-weight:600;cursor:not-allowed}.avaya-ipo-detail-title{font-weight:800;color:var(--sd-text, #333);font-size:14px}.avaya-ipo-detail-title-row{display:flex;align-items:center;justify-content:space-between}.avaya-ipo-btn-spinner{width:14px;height:14px;border-radius:var(--sd-radius-pill, 999px);border:2px solid color-mix(in srgb,var(--sd-accent-contrast, #fff) 35%,transparent);border-top-color:var(--sd-accent-contrast, #fff);animation:spin .8s linear infinite}.avaya-ipo-status-error{color:var(--sd-call-end, #e5484d);font-size:12px;font-weight:600}.avaya-ipo-transcription-skeleton{display:flex;flex-direction:column;gap:10px}.avaya-ipo-transcription-skeleton-line{height:10px;border-radius:var(--sd-radius-pill, 999px);background:linear-gradient(90deg,var(--sd-surface-sunken, #e8e8e8) 0%,var(--sd-surface-muted, #f7f7f7) 45%,var(--sd-surface-sunken, #e8e8e8) 100%);background-size:200% 100%;animation:skeleton-shine 1.2s ease-in-out infinite}.avaya-ipo-transcription-skeleton-line:nth-child(2){width:85%}.avaya-ipo-transcription-skeleton-line:nth-child(3){width:90%}.avaya-ipo-transcription-skeleton-line:nth-child(4){width:70%}.avaya-ipo-transcription-skeleton-line:nth-child(5){width:60%}.avaya-ipo-chat-thread{display:flex;flex-direction:column;gap:8px}.avaya-ipo-chat-thread-btn{border:1px solid var(--sd-border, #e3e3e3);background:var(--sd-surface, #fff);color:var(--sd-text, #333);padding:6px;border-radius:var(--sd-radius-sm, 8px);font-weight:700;cursor:pointer;font-size:12px;width:32px;height:32px;display:inline-flex;align-items:center;justify-content:center;transition:background .15s ease,border-color .15s ease}.avaya-ipo-chat-thread-btn:hover{background:var(--sd-surface-muted, #f7f7f7)}.avaya-ipo-chat-thread-btn svg{width:16px;height:16px}.avaya-ipo-transcription-card.avaya-ipo-fullscreen{position:absolute;inset:0;background:var(--sd-surface, #fff);padding:16px;z-index:1000;animation:transcript-expand .25s ease;border-radius:0}:host-context(.avaya-ipo-mode-standalone) .avaya-ipo-transcription-card.avaya-ipo-fullscreen{position:fixed}.avaya-ipo-transcription-card.avaya-ipo-fullscreen .avaya-ipo-detail-title-row{position:sticky;top:0;background:var(--sd-surface, #fff);z-index:2}.avaya-ipo-transcription-card.avaya-ipo-fullscreen .avaya-ipo-chat-thread{flex:1 1 auto;overflow-y:auto;padding-bottom:8px}.avaya-ipo-detail-card{transition:transform .25s ease,opacity .25s ease}.avaya-ipo-transcription-card:not(.avaya-ipo-fullscreen){animation:transcript-collapse .2s ease}.avaya-ipo-chat-bubble{align-self:flex-start;max-width:86%;background:var(--sd-surface-muted, #f7f7f7);border-radius:var(--sd-radius, 10px);padding:8px 10px;border:1px solid var(--sd-border, #e3e3e3)}.avaya-ipo-chat-bubble.avaya-ipo-agent{align-self:flex-end;background:var(--sd-accent-soft, #fff1ea);border-color:color-mix(in srgb,var(--sd-accent, #ff6633) 35%,transparent)}.avaya-ipo-chat-speaker{font-size:11px;font-weight:700;color:var(--sd-text-muted, #5f6368);text-transform:capitalize;margin-bottom:4px}.avaya-ipo-chat-text,.avaya-ipo-summary-text{font-size:13px;color:var(--sd-text, #333);line-height:1.4}.avaya-ipo-summary-emoji{margin-left:6px;font-size:14px}.avaya-ipo-summary-card{border:1px solid var(--sd-border, #e3e3e3);border-radius:var(--sd-radius, 10px);padding:10px;background:var(--sd-surface-muted, #f7f7f7)}@keyframes transcript-expand{0%{transform:scale(.98);opacity:.6}to{transform:scale(1);opacity:1}}@keyframes transcript-collapse{0%{transform:scale(1.02);opacity:.6}to{transform:scale(1);opacity:1}}@keyframes spin{to{transform:rotate(360deg)}}@keyframes skeleton-shine{0%{background-position:200% 0}to{background-position:-200% 0}}.avaya-ipo-placeholder{font-size:13px;color:var(--sd-text-muted, #5f6368)}.avaya-ipo-analysis-section{margin-top:10px}.avaya-ipo-analysis-heading{font-size:12px;font-weight:700;color:var(--sd-text-muted, #5f6368);text-transform:uppercase;letter-spacing:.4px;margin-bottom:4px}.avaya-ipo-analysis-list{margin:0;padding-left:18px;display:flex;flex-direction:column;gap:4px}.avaya-ipo-analysis-list li{font-size:13px;color:var(--sd-text, #333);line-height:1.45}.avaya-ipo-detail-row{display:flex;justify-content:space-between;align-items:center}.avaya-ipo-label{font-weight:700;color:var(--sd-text-muted, #5f6368);font-size:13px}.avaya-ipo-value{color:var(--sd-text, #333);text-align:right;font-weight:600;font-size:13px}\n"] }]
|
|
1520
|
+
}], ctorParameters: () => [{ type: i1$1.AppSyncHelperService }], propDecorators: { call: [{
|
|
1423
1521
|
type: Input
|
|
1424
1522
|
}] } });
|
|
1425
1523
|
|
|
@@ -1452,6 +1550,7 @@ class CallHistoryComponent {
|
|
|
1452
1550
|
last_status
|
|
1453
1551
|
metadata
|
|
1454
1552
|
summary
|
|
1553
|
+
aiProcessingStatus
|
|
1455
1554
|
startedAt
|
|
1456
1555
|
completedAt
|
|
1457
1556
|
duration
|
|
@@ -1559,7 +1658,9 @@ class CallHistoryComponent {
|
|
|
1559
1658
|
disconnectSide,
|
|
1560
1659
|
timestamp: tsSec ? new Date(tsSec * 1000).toISOString() : new Date().toISOString(),
|
|
1561
1660
|
summary: interaction.summary || '',
|
|
1562
|
-
|
|
1661
|
+
metadata,
|
|
1662
|
+
aiProcessingStatus: interaction.aiProcessingStatus || '',
|
|
1663
|
+
fullCallRecordingUrl: interaction.recording?.location || metadata.phoneData?.AudioFile || '',
|
|
1563
1664
|
phoneNumber: {
|
|
1564
1665
|
normalizedPhoneNumber: customerNumber,
|
|
1565
1666
|
name: name || undefined,
|
|
@@ -1682,33 +1783,6 @@ class CallHistoryComponent {
|
|
|
1682
1783
|
}
|
|
1683
1784
|
return undefined;
|
|
1684
1785
|
}
|
|
1685
|
-
formatDialOutFromInput(value) {
|
|
1686
|
-
return formatDialOutNumber(value);
|
|
1687
|
-
}
|
|
1688
|
-
getTenantIdFromSession() {
|
|
1689
|
-
const raw = sessionStorage.getItem('sd-tenant');
|
|
1690
|
-
if (!raw)
|
|
1691
|
-
return undefined;
|
|
1692
|
-
try {
|
|
1693
|
-
const parsed = JSON.parse(raw);
|
|
1694
|
-
return parsed?.id || parsed?.tenantId || undefined;
|
|
1695
|
-
}
|
|
1696
|
-
catch {
|
|
1697
|
-
return undefined;
|
|
1698
|
-
}
|
|
1699
|
-
}
|
|
1700
|
-
getUserIdFromSession() {
|
|
1701
|
-
const raw = sessionStorage.getItem('sd-user');
|
|
1702
|
-
if (!raw)
|
|
1703
|
-
return undefined;
|
|
1704
|
-
try {
|
|
1705
|
-
const parsed = JSON.parse(raw);
|
|
1706
|
-
return parsed?.id || parsed?.userId || undefined;
|
|
1707
|
-
}
|
|
1708
|
-
catch {
|
|
1709
|
-
return undefined;
|
|
1710
|
-
}
|
|
1711
|
-
}
|
|
1712
1786
|
getPhoneDisplay(item) {
|
|
1713
1787
|
return (item?.phoneNumber?.name ||
|
|
1714
1788
|
item?.lookupPhoneNumber?.name ||
|
|
@@ -2272,15 +2346,46 @@ class RecordingManagerService {
|
|
|
2272
2346
|
MIME_TYPE_PREFERRED = 'audio/webm;codecs=opus';
|
|
2273
2347
|
MIME_TYPE_FALLBACK = 'audio/webm';
|
|
2274
2348
|
// Record one continuous file for the whole call; a timeslice just flushes data periodically so the
|
|
2275
|
-
// final Blob is complete.
|
|
2276
|
-
//
|
|
2349
|
+
// final Blob is complete. In 'complete' mode the full recording is uploaded once at call end; in
|
|
2350
|
+
// 'chunked' mode each timeslice is also streamed live over the recording WebSocket so the backend
|
|
2351
|
+
// can stitch a file even if this tab dies mid-call.
|
|
2277
2352
|
TIMESLICE_MS = 1000;
|
|
2353
|
+
// API Gateway WebSockets enforce a 32 KB FRAME limit — oversized frames kill the connection (1009).
|
|
2354
|
+
// Three-second slices at a capped bitrate stay comfortably under it; anything bigger is split into
|
|
2355
|
+
// frame-safe segments before sending (see MAX_WS_DATA_CHARS).
|
|
2356
|
+
CHUNKED_TIMESLICE_MS = 3000;
|
|
2357
|
+
// Opus voice at 32 kbps is transparent for calls and keeps a 3s slice ≈ 12 KB raw / 16 KB base64.
|
|
2358
|
+
CHUNKED_AUDIO_BPS = 32000;
|
|
2359
|
+
// Base64 payload cap per websocket message (multiple of 4 so each slice decodes independently);
|
|
2360
|
+
// leaves ample headroom inside the 32 KB frame limit including the JSON envelope.
|
|
2361
|
+
MAX_WS_DATA_CHARS = 24000;
|
|
2278
2362
|
AUDIO_CONTEXT_CLOSE_DELAY_MS = 500;
|
|
2363
|
+
SOCKET_RECONNECT_DELAY_MS = 3000;
|
|
2364
|
+
// Pause between the last flushed chunk and the call-completed message: chunk messages invoke the
|
|
2365
|
+
// backend Lambda concurrently, so the final parts may still be mid-write to S3 when the stitch
|
|
2366
|
+
// would otherwise run. This settle window lets them land first.
|
|
2367
|
+
CALL_COMPLETED_DELAY_MS = 2000;
|
|
2368
|
+
// Stop redialing a broken socket after this many consecutive failures — the in-memory chunks and
|
|
2369
|
+
// the REST fallback still save the recording at call end, so endless connection spam buys nothing.
|
|
2370
|
+
SOCKET_MAX_RECONNECT_ATTEMPTS = 10;
|
|
2371
|
+
// Give the final flush + call-completed message a moment before force-closing the socket.
|
|
2372
|
+
SOCKET_FINALIZE_GRACE_MS = 1500;
|
|
2279
2373
|
RECORDING_API_URL = APP_CONFIG.INTERACTION_CALL_RECORDING_API_URL;
|
|
2374
|
+
RECORDING_WS_URL = APP_CONFIG.RECORDING_SOCKET_WS_URL;
|
|
2280
2375
|
constructor(http) {
|
|
2281
2376
|
this.http = http;
|
|
2282
2377
|
}
|
|
2283
|
-
startRecording(localMediaStream, remoteMediaStream, metadata) {
|
|
2378
|
+
startRecording(localMediaStream, remoteMediaStream, metadata, mode = APP_CONFIG.RECORDING_UPLOAD_MODE) {
|
|
2379
|
+
// Restart for the SAME call in chunked mode (e.g. remote stream arrived late, or the call
|
|
2380
|
+
// re-fired CONNECTED): keep the session — one socket and one seq counter per call — and only
|
|
2381
|
+
// swap the audio plumbing. Tearing down would open a second socket and prematurely finalize.
|
|
2382
|
+
const existing = this.sessions.get(metadata.callId);
|
|
2383
|
+
if (existing && existing.mode === 'chunked' && mode === 'chunked' && !existing.stopping) {
|
|
2384
|
+
if (localMediaStream && remoteMediaStream) {
|
|
2385
|
+
return this.rebindMedia(existing, localMediaStream, remoteMediaStream);
|
|
2386
|
+
}
|
|
2387
|
+
return existing;
|
|
2388
|
+
}
|
|
2284
2389
|
this.stopRecording(metadata.callId);
|
|
2285
2390
|
if (!localMediaStream || !remoteMediaStream) {
|
|
2286
2391
|
console.warn(`[${metadata.callId}] Skipped start recording: missing remote or local stream`);
|
|
@@ -2295,12 +2400,10 @@ class RecordingManagerService {
|
|
|
2295
2400
|
}
|
|
2296
2401
|
const audioContext = new AudioContext();
|
|
2297
2402
|
const recordingStream = audioContext.createMediaStreamDestination();
|
|
2298
|
-
audioContext
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
.createMediaStreamSource(remoteMediaStream)
|
|
2303
|
-
.connect(recordingStream);
|
|
2403
|
+
const localSource = audioContext.createMediaStreamSource(localMediaStream);
|
|
2404
|
+
localSource.connect(recordingStream);
|
|
2405
|
+
const remoteSource = audioContext.createMediaStreamSource(remoteMediaStream);
|
|
2406
|
+
remoteSource.connect(recordingStream);
|
|
2304
2407
|
const mimeType = MediaRecorder.isTypeSupported(this.MIME_TYPE_PREFERRED)
|
|
2305
2408
|
? this.MIME_TYPE_PREFERRED
|
|
2306
2409
|
: this.MIME_TYPE_FALLBACK;
|
|
@@ -2313,13 +2416,36 @@ class RecordingManagerService {
|
|
|
2313
2416
|
recorder: null,
|
|
2314
2417
|
mimeType,
|
|
2315
2418
|
uploaded: false,
|
|
2419
|
+
mode,
|
|
2420
|
+
socket: null,
|
|
2421
|
+
seq: 0,
|
|
2422
|
+
pendingChunks: [],
|
|
2423
|
+
sentAny: false,
|
|
2424
|
+
reconnectAttempts: 0,
|
|
2425
|
+
sourceNodes: [localSource, remoteSource],
|
|
2426
|
+
streamCursor: 0,
|
|
2427
|
+
pumping: false,
|
|
2428
|
+
everConnected: false,
|
|
2429
|
+
completedSent: false,
|
|
2316
2430
|
};
|
|
2317
2431
|
this.sessions.set(metadata.callId, session);
|
|
2318
|
-
|
|
2432
|
+
if (mode === 'chunked') {
|
|
2433
|
+
this.openRecordingSocket(session);
|
|
2434
|
+
}
|
|
2435
|
+
const recorder = new MediaRecorder(recordingStream.stream, mode === 'chunked'
|
|
2436
|
+
? { mimeType, audioBitsPerSecond: this.CHUNKED_AUDIO_BPS }
|
|
2437
|
+
: { mimeType });
|
|
2319
2438
|
session.recorder = recorder;
|
|
2320
2439
|
recorder.ondataavailable = (evt) => {
|
|
2321
2440
|
if (evt.data && evt.data.size > 0) {
|
|
2441
|
+
// Keep the local copy in BOTH modes: it's the upload source in 'complete' mode and the
|
|
2442
|
+
// fallback in 'chunked' mode if the socket never works.
|
|
2322
2443
|
session.chunks.push(evt.data);
|
|
2444
|
+
if (session.mode === 'chunked') {
|
|
2445
|
+
// The pump streams chunks strictly in order from session.chunks — it also lets a
|
|
2446
|
+
// reconnect rewind and re-send the whole call (the backend deletes parts on every close).
|
|
2447
|
+
void this.pumpChunks(session);
|
|
2448
|
+
}
|
|
2323
2449
|
}
|
|
2324
2450
|
};
|
|
2325
2451
|
recorder.onerror = (err) => {
|
|
@@ -2328,8 +2454,8 @@ class RecordingManagerService {
|
|
|
2328
2454
|
recorder.onstop = () => {
|
|
2329
2455
|
void this.finalizeRecording(session);
|
|
2330
2456
|
};
|
|
2331
|
-
recorder.start(this.TIMESLICE_MS);
|
|
2332
|
-
console.debug(`[${metadata.callId}] MediaRecorder started (
|
|
2457
|
+
recorder.start(mode === 'chunked' ? this.CHUNKED_TIMESLICE_MS : this.TIMESLICE_MS);
|
|
2458
|
+
console.debug(`[${metadata.callId}] MediaRecorder started (${mode} mode)`);
|
|
2333
2459
|
return session;
|
|
2334
2460
|
}
|
|
2335
2461
|
getSession(callId) {
|
|
@@ -2365,6 +2491,10 @@ class RecordingManagerService {
|
|
|
2365
2491
|
if (session.uploaded)
|
|
2366
2492
|
return;
|
|
2367
2493
|
session.uploaded = true;
|
|
2494
|
+
if (session.mode === 'chunked') {
|
|
2495
|
+
await this.finalizeChunked(session);
|
|
2496
|
+
return;
|
|
2497
|
+
}
|
|
2368
2498
|
if (!session.chunks.length) {
|
|
2369
2499
|
console.warn(`[${session.metadata.callId}] No recording data captured`);
|
|
2370
2500
|
return;
|
|
@@ -2380,6 +2510,220 @@ class RecordingManagerService {
|
|
|
2380
2510
|
console.error(`[${session.metadata.callId}] ❌ recording upload failed`, err);
|
|
2381
2511
|
}
|
|
2382
2512
|
}
|
|
2513
|
+
// ── Chunked mode: stream slices over the recording WebSocket ─────────────────────────────────
|
|
2514
|
+
/**
|
|
2515
|
+
* Rewire fresh local/remote streams into the SAME recording destination. The MediaRecorder (and
|
|
2516
|
+
* therefore the single WebM byte stream, the socket and the seq counter) continue uninterrupted —
|
|
2517
|
+
* restarting the recorder would inject a second WebM header mid-file and break playback of the
|
|
2518
|
+
* stitched recording.
|
|
2519
|
+
*/
|
|
2520
|
+
rebindMedia(session, localMediaStream, remoteMediaStream) {
|
|
2521
|
+
const { callId } = session.metadata;
|
|
2522
|
+
console.debug(`[${callId}] Rewiring recording sources (recorder, socket and seq preserved)`);
|
|
2523
|
+
for (const node of session.sourceNodes) {
|
|
2524
|
+
try {
|
|
2525
|
+
node.disconnect();
|
|
2526
|
+
}
|
|
2527
|
+
catch { /* already disconnected */ }
|
|
2528
|
+
}
|
|
2529
|
+
const ctx = session.audioContext;
|
|
2530
|
+
if (ctx.state === 'suspended') {
|
|
2531
|
+
void ctx.resume();
|
|
2532
|
+
}
|
|
2533
|
+
const localSource = ctx.createMediaStreamSource(localMediaStream);
|
|
2534
|
+
localSource.connect(session.recordingStream);
|
|
2535
|
+
const remoteSource = ctx.createMediaStreamSource(remoteMediaStream);
|
|
2536
|
+
remoteSource.connect(session.recordingStream);
|
|
2537
|
+
session.sourceNodes = [localSource, remoteSource];
|
|
2538
|
+
return session;
|
|
2539
|
+
}
|
|
2540
|
+
openRecordingSocket(session) {
|
|
2541
|
+
const { metadata } = session;
|
|
2542
|
+
// One socket per call: never open a second connection while one is open or still connecting.
|
|
2543
|
+
if (session.socket &&
|
|
2544
|
+
(session.socket.readyState === WebSocket.OPEN ||
|
|
2545
|
+
session.socket.readyState === WebSocket.CONNECTING)) {
|
|
2546
|
+
return;
|
|
2547
|
+
}
|
|
2548
|
+
if (session.reconnectTimer) {
|
|
2549
|
+
clearTimeout(session.reconnectTimer);
|
|
2550
|
+
session.reconnectTimer = undefined;
|
|
2551
|
+
}
|
|
2552
|
+
const token = (typeof sessionStorage !== 'undefined' && sessionStorage.getItem('auth_sessionToken')) || '';
|
|
2553
|
+
const url = `${this.RECORDING_WS_URL}?` +
|
|
2554
|
+
`token=${encodeURIComponent(token)}` +
|
|
2555
|
+
`&callId=${encodeURIComponent(metadata.callId)}` +
|
|
2556
|
+
`&tenantId=${encodeURIComponent(metadata.tenantId)}` +
|
|
2557
|
+
(metadata.entityId ? `&entityId=${encodeURIComponent(metadata.entityId)}` : '') +
|
|
2558
|
+
`&contentType=${encodeURIComponent(session.mimeType)}`;
|
|
2559
|
+
console.debug(`[${metadata.callId}] Opening recording socket (entityId=${metadata.entityId || 'NONE — chunks will land under the NIL entity folder'})`);
|
|
2560
|
+
try {
|
|
2561
|
+
const socket = new WebSocket(url);
|
|
2562
|
+
session.socket = socket;
|
|
2563
|
+
socket.onopen = () => {
|
|
2564
|
+
session.reconnectAttempts = 0;
|
|
2565
|
+
if (session.everConnected) {
|
|
2566
|
+
// Reconnect after a drop: the backend stitched AND deleted the parts on $disconnect, so
|
|
2567
|
+
// rewind and re-stream the entire call from memory — the next stitch then has everything.
|
|
2568
|
+
console.debug(`[${metadata.callId}] Recording socket reconnected — re-streaming ${session.chunks.length} slices`);
|
|
2569
|
+
session.streamCursor = 0;
|
|
2570
|
+
session.seq = 0;
|
|
2571
|
+
session.pendingChunks = [];
|
|
2572
|
+
void this.pumpChunks(session);
|
|
2573
|
+
}
|
|
2574
|
+
else {
|
|
2575
|
+
console.debug(`[${metadata.callId}] Recording socket connected`);
|
|
2576
|
+
session.everConnected = true;
|
|
2577
|
+
this.flushPendingChunks(session);
|
|
2578
|
+
void this.pumpChunks(session);
|
|
2579
|
+
}
|
|
2580
|
+
};
|
|
2581
|
+
socket.onerror = (err) => {
|
|
2582
|
+
console.warn(`[${metadata.callId}] Recording socket error`, err);
|
|
2583
|
+
};
|
|
2584
|
+
socket.onclose = () => {
|
|
2585
|
+
// Ignore close events from sockets we've already replaced — only the current one counts.
|
|
2586
|
+
if (session.socket !== socket) {
|
|
2587
|
+
return;
|
|
2588
|
+
}
|
|
2589
|
+
session.socket = null;
|
|
2590
|
+
// Reconnect while the call is still recording — the backend stitches on every close, so a
|
|
2591
|
+
// blip costs nothing; the next close re-stitches the fuller file.
|
|
2592
|
+
if (!session.stopping && this.sessions.get(metadata.callId) === session) {
|
|
2593
|
+
session.reconnectAttempts += 1;
|
|
2594
|
+
if (session.reconnectAttempts > this.SOCKET_MAX_RECONNECT_ATTEMPTS) {
|
|
2595
|
+
console.warn(`[${metadata.callId}] Recording socket gave up after ${this.SOCKET_MAX_RECONNECT_ATTEMPTS} attempts; ` +
|
|
2596
|
+
`recording continues in memory and uploads at call end`);
|
|
2597
|
+
return;
|
|
2598
|
+
}
|
|
2599
|
+
if (session.reconnectTimer) {
|
|
2600
|
+
clearTimeout(session.reconnectTimer);
|
|
2601
|
+
}
|
|
2602
|
+
session.reconnectTimer = setTimeout(() => this.openRecordingSocket(session), this.SOCKET_RECONNECT_DELAY_MS);
|
|
2603
|
+
}
|
|
2604
|
+
};
|
|
2605
|
+
}
|
|
2606
|
+
catch (err) {
|
|
2607
|
+
console.warn(`[${metadata.callId}] Failed to open recording socket`, err);
|
|
2608
|
+
session.socket = null;
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
/** Stream session.chunks in strict order; safe to call repeatedly (single active pump). */
|
|
2612
|
+
async pumpChunks(session) {
|
|
2613
|
+
if (session.pumping || session.completedSent) {
|
|
2614
|
+
return;
|
|
2615
|
+
}
|
|
2616
|
+
session.pumping = true;
|
|
2617
|
+
try {
|
|
2618
|
+
while (session.streamCursor < session.chunks.length && !session.completedSent) {
|
|
2619
|
+
const blob = session.chunks[session.streamCursor];
|
|
2620
|
+
session.streamCursor += 1;
|
|
2621
|
+
await this.streamChunk(session, blob);
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2624
|
+
finally {
|
|
2625
|
+
session.pumping = false;
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
async streamChunk(session, chunk) {
|
|
2629
|
+
if (session.completedSent) {
|
|
2630
|
+
// Never send chunks after call-completed: the backend has already stitched and retired the
|
|
2631
|
+
// connection; a straggler would just become an orphaned part.
|
|
2632
|
+
return;
|
|
2633
|
+
}
|
|
2634
|
+
try {
|
|
2635
|
+
const dataUrl = await this.convertBlobToBase64(chunk);
|
|
2636
|
+
const data = dataUrl.split(',')[1] || '';
|
|
2637
|
+
if (!data)
|
|
2638
|
+
return;
|
|
2639
|
+
// Split into frame-safe segments: API Gateway kills connections on frames > 32 KB. Slicing
|
|
2640
|
+
// base64 at 4-char boundaries keeps every segment independently decodable, and each segment
|
|
2641
|
+
// carries its own seq — server-side concatenation in seq order reproduces the exact bytes.
|
|
2642
|
+
for (let i = 0; i < data.length; i += this.MAX_WS_DATA_CHARS) {
|
|
2643
|
+
const segment = data.slice(i, i + this.MAX_WS_DATA_CHARS);
|
|
2644
|
+
session.seq += 1;
|
|
2645
|
+
const message = JSON.stringify({ action: 'chunk', seq: session.seq, data: segment });
|
|
2646
|
+
if (session.socket && session.socket.readyState === WebSocket.OPEN) {
|
|
2647
|
+
session.socket.send(message);
|
|
2648
|
+
session.sentAny = true;
|
|
2649
|
+
}
|
|
2650
|
+
else {
|
|
2651
|
+
// Socket down — keep the message (seq already assigned, so order survives a reconnect).
|
|
2652
|
+
session.pendingChunks.push(message);
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
catch (err) {
|
|
2657
|
+
console.warn(`[${session.metadata.callId}] Failed to stream recording chunk`, err);
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
flushPendingChunks(session) {
|
|
2661
|
+
if (!session.socket || session.socket.readyState !== WebSocket.OPEN)
|
|
2662
|
+
return;
|
|
2663
|
+
while (session.pendingChunks.length) {
|
|
2664
|
+
session.socket.send(session.pendingChunks.shift());
|
|
2665
|
+
session.sentAny = true;
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
async finalizeChunked(session) {
|
|
2669
|
+
const { metadata } = session;
|
|
2670
|
+
if (session.reconnectTimer) {
|
|
2671
|
+
clearTimeout(session.reconnectTimer);
|
|
2672
|
+
session.reconnectTimer = undefined;
|
|
2673
|
+
}
|
|
2674
|
+
// Wait (briefly) for the pump to drain so the call's tail audio is part of the stitch.
|
|
2675
|
+
const drainDeadline = Date.now() + 2000;
|
|
2676
|
+
while ((session.pumping || session.streamCursor < session.chunks.length) &&
|
|
2677
|
+
Date.now() < drainDeadline) {
|
|
2678
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
2679
|
+
}
|
|
2680
|
+
const socket = session.socket;
|
|
2681
|
+
if (socket && socket.readyState === WebSocket.OPEN) {
|
|
2682
|
+
try {
|
|
2683
|
+
this.flushPendingChunks(session);
|
|
2684
|
+
session.completedSent = true; // no further chunks may be queued from here on
|
|
2685
|
+
// Let the already-sent chunk writes settle server-side before asking for the stitch.
|
|
2686
|
+
await new Promise((resolve) => setTimeout(resolve, this.CALL_COMPLETED_DELAY_MS));
|
|
2687
|
+
socket.send(JSON.stringify({ action: 'call-completed' }));
|
|
2688
|
+
console.debug(`[${metadata.callId}] ✅ recording finalized over socket (${session.seq} chunks)`);
|
|
2689
|
+
}
|
|
2690
|
+
catch (err) {
|
|
2691
|
+
console.warn(`[${metadata.callId}] Failed to send call-completed`, err);
|
|
2692
|
+
}
|
|
2693
|
+
setTimeout(() => {
|
|
2694
|
+
try {
|
|
2695
|
+
socket.close();
|
|
2696
|
+
}
|
|
2697
|
+
catch { /* already closed */ }
|
|
2698
|
+
}, this.SOCKET_FINALIZE_GRACE_MS);
|
|
2699
|
+
session.chunks = [];
|
|
2700
|
+
return;
|
|
2701
|
+
}
|
|
2702
|
+
if (session.sentAny) {
|
|
2703
|
+
// Some chunks made it — the backend's $disconnect stitch already saved (or will save) those.
|
|
2704
|
+
// Don't double-upload; the lost tail is at most a few seconds.
|
|
2705
|
+
console.warn(`[${metadata.callId}] Socket down at finalize; backend stitches what was streamed`);
|
|
2706
|
+
session.chunks = [];
|
|
2707
|
+
return;
|
|
2708
|
+
}
|
|
2709
|
+
// Streaming never worked at all — fall back to the original complete upload so the recording
|
|
2710
|
+
// is not lost just because the socket couldn't connect.
|
|
2711
|
+
console.warn(`[${metadata.callId}] Recording socket never connected; falling back to complete upload`);
|
|
2712
|
+
if (!session.chunks.length) {
|
|
2713
|
+
console.warn(`[${metadata.callId}] No recording data captured`);
|
|
2714
|
+
return;
|
|
2715
|
+
}
|
|
2716
|
+
const blob = new Blob(session.chunks, { type: session.mimeType });
|
|
2717
|
+
session.chunks = [];
|
|
2718
|
+
const filename = this.generateFilename(metadata.callId, metadata.agentId, session.mimeType);
|
|
2719
|
+
try {
|
|
2720
|
+
await this.uploadCallRecording(metadata, blob, filename, session.mimeType);
|
|
2721
|
+
console.debug(`[${metadata.callId}] ✅ uploaded recording (fallback) -> ${filename}`);
|
|
2722
|
+
}
|
|
2723
|
+
catch (err) {
|
|
2724
|
+
console.error(`[${metadata.callId}] ❌ fallback recording upload failed`, err);
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2383
2727
|
async uploadCallRecording(metadata, file, filename, mimeType) {
|
|
2384
2728
|
try {
|
|
2385
2729
|
const base64 = await this.convertBlobToBase64(file);
|
|
@@ -2899,11 +3243,11 @@ class PhoneIdleComponent {
|
|
|
2899
3243
|
this.keyboardInput.emit(ev);
|
|
2900
3244
|
}
|
|
2901
3245
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: PhoneIdleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2902
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.9", type: PhoneIdleComponent, isStandalone: false, selector: "app-phone-idle", inputs: { dialForm: "dialForm", contacts: "contacts", showDirectoryPhonebook: "showDirectoryPhonebook", isRinging: "isRinging", CountryISO: "CountryISO", SearchCountryField: "SearchCountryField" }, outputs: { toggleDirectory: "toggleDirectory", clear: "clear", makeCall: "makeCall", keypadInput: "keypadInput", keyboardInput: "keyboardInput" }, ngImport: i0, template: "<form class=\"avaya-ipo-phone-shell avaya-ipo-classic\" [formGroup]=\"dialForm\" (ngSubmit)=\"onMakeCall()\">\n <div class=\"avaya-ipo-input-row avaya-ipo-classic-input\">\n <ngx-intl-tel-input\n formControlName=\"phoneNumber\"\n name=\"phoneNumber\"\n [cssClass]=\"'avaya-ipo-phone_number_sd'\"\n [preferredCountries]=\"['in']\"\n [customPlaceholder]=\"'Phone Number'\"\n [enableAutoCountrySelect]=\"false\"\n [enablePlaceholder]=\"false\"\n [searchCountryFlag]=\"true\"\n [searchCountryField]=\"[SearchCountryField.Iso2, SearchCountryField.Name]\"\n [selectFirstCountry]=\"false\"\n [selectedCountryISO]=\"CountryISO.India\"\n [maxLength]=\"15\"\n [phoneValidation]=\"true\"\n [separateDialCode]=\"true\"\n (keydown)=\"onKeyboardInput($event)\"\n >\n </ngx-intl-tel-input>\n <button type=\"button\" class=\"avaya-ipo-clear-btn\" (click)=\"onClear()\" aria-label=\"Backspace\" title=\"Backspace\">\n <svg viewBox=\"0 0 24 24\" width=\"20\" height=\"20\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\n <path d=\"M22 3H7c-.69 0-1.23.35-1.59.88L0 12l5.41 8.12c.36.53.9.88 1.59.88h15c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-3 12.59L17.59 17 14 13.41 10.41 17 9 15.59 12.59 12 9 8.41 10.41 7 14 10.59 17.59 7 19 8.41 15.41 12 19 15.59z\" />\n </svg>\n </button>\n </div>\n\n <div class=\"avaya-ipo-dial-cluster\">\n <div class=\"avaya-ipo-keypad avaya-ipo-classic-grid\">\n <button type=\"button\" (click)=\"onKeypadInput('1')\">\n <span class=\"avaya-ipo-digit\">1</span>\n <span class=\"avaya-ipo-letters\"> </span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('2')\">\n <span class=\"avaya-ipo-digit\">2</span>\n <span class=\"avaya-ipo-letters\">ABC</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('3')\">\n <span class=\"avaya-ipo-digit\">3</span>\n <span class=\"avaya-ipo-letters\">DEF</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('4')\">\n <span class=\"avaya-ipo-digit\">4</span>\n <span class=\"avaya-ipo-letters\">GHI</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('5')\">\n <span class=\"avaya-ipo-digit\">5</span>\n <span class=\"avaya-ipo-letters\">JKL</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('6')\">\n <span class=\"avaya-ipo-digit\">6</span>\n <span class=\"avaya-ipo-letters\">MNO</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('7')\">\n <span class=\"avaya-ipo-digit\">7</span>\n <span class=\"avaya-ipo-letters\">PQRS</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('8')\">\n <span class=\"avaya-ipo-digit\">8</span>\n <span class=\"avaya-ipo-letters\">TUV</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('9')\">\n <span class=\"avaya-ipo-digit\">9</span>\n <span class=\"avaya-ipo-letters\">WXYZ</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('*')\">\n <span class=\"avaya-ipo-digit\">*</span>\n <span class=\"avaya-ipo-letters\"> </span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('0')\">\n <span class=\"avaya-ipo-digit\">0</span>\n <span class=\"avaya-ipo-letters\">+</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('#')\">\n <span class=\"avaya-ipo-digit\">#</span>\n <span class=\"avaya-ipo-letters\"> </span>\n </button>\n </div>\n\n <div class=\"avaya-ipo-actions\">\n <button type=\"submit\" class=\"avaya-ipo-call-btn-main\" [disabled]=\"!this.dialForm.valid\">\n <svg viewBox=\"0 0 512 512\" width=\"22px\" height=\"22px\" fill=\"currentColor\" aria-hidden=\"true\">\n <path\n d=\"M164.9 24.6c-7.7-18.6-28-28.5-47.4-23.2l-88 24C12.1 30.2 0 46 0 64C0 311.4 200.6 512 448 512c18 0 33.8-12.1 38.6-29.5l24-88c5.3-19.4-4.6-39.7-23.2-47.4l-96-40c-16.3-6.8-35.2-2.1-46.3 11.6L304.7 368C234.3 334.7 177.3 277.7 144 207.3L193.3 167c13.7-11.2 18.4-30 11.6-46.3l-40-96z\" />\n </svg>\n </button>\n </div>\n </div>\n</form>\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0}.avaya-ipo-phone-shell.avaya-ipo-classic{--key-size: clamp(40px, min(24cqw, 18cqh - 30px), 88px);--key-gap: calc(var(--key-size) * .16);display:flex;flex-direction:column;align-items:center;justify-content:flex-start;gap:clamp(35px,4.5cqw,30px);background:transparent;width:100%;height:100%;flex:1 1 auto;min-height:0;padding:46px 15px clamp(12px,3cqw,20px);box-sizing:border-box}.avaya-ipo-dial-cluster{flex:1 1 auto;min-height:0;width:100%;display:flex;flex-direction:column;align-items:center;justify-content:flex-start;gap:clamp(10px,2.6cqw,18px)}.avaya-ipo-classic-input{width:100%;display:flex;align-items:center;padding:0;box-sizing:border-box;position:relative;border:1.5px solid var(--sd-input-border);border-radius:var(--sd-radius);background:var(--sd-input);box-shadow:var(--sd-shadow-sm);margin-bottom:0;transition:border-color .18s ease,box-shadow .18s ease}.avaya-ipo-classic-input:focus-within{border-color:var(--sd-accent);box-shadow:var(--sd-focus-ring)}.avaya-ipo-phonebook-btn{border:none;background:transparent;color:var(--sd-text-muted);width:32px;height:32px;border-radius:var(--sd-radius-sm);display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;cursor:pointer}.avaya-ipo-phonebook-btn:hover{background:var(--sd-surface-muted)}.avaya-ipo-phonebook-btn:focus{outline:none;box-shadow:none}.avaya-ipo-phonebook-btn:focus-visible{outline:none;box-shadow:var(--sd-focus-ring)}.avaya-ipo-clear-btn{position:absolute;right:clamp(5px,1.6cqw,9px);top:50%;transform:translateY(-50%);width:34px;height:34px;display:inline-flex;align-items:center;justify-content:center;border:none;background:transparent;color:var(--sd-text-subtle);border-radius:50%;padding:0;cursor:pointer;transition:background .15s ease,color .15s ease,transform .1s ease}.avaya-ipo-clear-btn:hover{background:var(--sd-surface-muted);color:var(--sd-accent)}.avaya-ipo-clear-btn:active{transform:translateY(-50%) scale(.9)}.avaya-ipo-clear-btn:focus{outline:none;box-shadow:none}.avaya-ipo-clear-btn:focus-visible{outline:none;box-shadow:var(--sd-focus-ring)}.avaya-ipo-clear-btn svg{display:block}.avaya-ipo-classic-input .avaya-ipo-phone_number_sd{width:100%}.avaya-ipo-classic-input .avaya-ipo-phone_number_sd input{width:100%;border:none!important;box-shadow:none!important;background:transparent!important;padding:clamp(8px,calc(var(--key-size) * .16),15px) 46px clamp(8px,calc(var(--key-size) * .16),15px) 0!important;font-size:clamp(12px,calc(var(--key-size) * .27),20px)!important;font-weight:600;letter-spacing:.4px;color:var(--sd-text)!important}.avaya-ipo-classic-input .avaya-ipo-phone_number_sd input::placeholder{font-weight:400;letter-spacing:normal;color:var(--sd-text-subtle)}:host ::ng-deep .avaya-ipo-classic-input .iti{width:100%}:host ::ng-deep .avaya-ipo-classic-input .iti__selected-flag{padding:0 10px 0 14px;border-right:1px solid var(--sd-border);border-radius:calc(var(--sd-radius) - 2px) 0 0 calc(var(--sd-radius) - 2px);background:transparent;outline:none;transition:background .15s ease}:host ::ng-deep .avaya-ipo-classic-input .iti--allow-dropdown .iti__flag-container:hover .iti__selected-flag{background:var(--sd-surface-muted)}:host ::ng-deep .avaya-ipo-classic-input .selected-dial-code{margin-left:6px;font-size:clamp(12px,calc(var(--key-size) * .18),16px);font-weight:600;color:var(--sd-text)}:host ::ng-deep .avaya-ipo-classic-input .iti__selected-flag:focus,:host ::ng-deep .avaya-ipo-classic-input .iti__tel-input:focus,.avaya-ipo-classic-input .avaya-ipo-phone_number_sd input:focus{outline:none;box-shadow:none}:host ::ng-deep .avaya-ipo-classic-input .iti__tel-input{background:transparent!important;border:none!important;box-shadow:none!important;height:auto;padding:clamp(8px,calc(var(--key-size) * .16),15px) 46px clamp(8px,calc(var(--key-size) * .16),15px) 0;font-size:clamp(12px,calc(var(--key-size) * .27),20px);font-weight:600;letter-spacing:.4px;color:var(--sd-text)}:host ::ng-deep .avaya-ipo-classic-input .iti.separate-dial-code .iti__tel-input{padding-left:102px!important}:host ::ng-deep .avaya-ipo-classic-input .iti__dropdown-content,:host ::ng-deep .avaya-ipo-classic-input .dropdown-menu{z-index:30;min-width:248px;padding:6px;background:var(--sd-surface);border:1px solid var(--sd-border);border-radius:var(--sd-radius);box-shadow:var(--sd-shadow-lg)}:host ::ng-deep .avaya-ipo-classic-input .iti__country-list{max-height:220px;overflow-y:auto;margin:0;padding:0;border:none;box-shadow:none;background:transparent}:host ::ng-deep .avaya-ipo-classic-input .search-container{position:sticky;top:0;z-index:1;padding:0 0 6px;background:var(--sd-surface)}:host ::ng-deep .avaya-ipo-classic-input #country-search-box{width:100%;box-sizing:border-box;margin:0!important;padding:8px 12px!important;font-size:14px!important;font-weight:400!important;letter-spacing:normal!important;color:var(--sd-text)!important;background:var(--sd-input)!important;border:1px solid var(--sd-input-border)!important;border-radius:var(--sd-radius-sm)!important;box-shadow:none!important;height:auto!important;transition:border-color .15s ease,box-shadow .15s ease}:host ::ng-deep .avaya-ipo-classic-input #country-search-box:focus{outline:none!important;border-color:var(--sd-accent)!important;box-shadow:var(--sd-focus-ring)!important}:host ::ng-deep .avaya-ipo-classic-input #country-search-box::placeholder{color:var(--sd-text-subtle)!important}:host ::ng-deep .avaya-ipo-classic-input .iti__country{padding:7px 8px;border-radius:var(--sd-radius-sm);font-size:14px;color:var(--sd-text)}:host ::ng-deep .avaya-ipo-classic-input .iti__country.iti__highlight,:host ::ng-deep .avaya-ipo-classic-input .iti__country:hover{background:var(--sd-surface-muted)}:host ::ng-deep .avaya-ipo-classic-input .iti__country .iti__dial-code{color:var(--sd-text-subtle)}:host ::ng-deep .avaya-ipo-classic-input .iti__divider{border-top:1px solid var(--sd-border);margin:4px 0}.avaya-ipo-classic-grid{width:auto;margin:0 auto;display:grid;grid-template-columns:repeat(3,var(--key-size));gap:var(--key-gap);justify-content:center;flex:0 1 auto}.avaya-ipo-classic-grid button{width:var(--key-size);height:var(--key-size);border-radius:50%;border:1.6px solid var(--sd-border-strong);background:transparent;display:flex;flex-direction:column;align-items:center;justify-content:center;cursor:pointer}.avaya-ipo-classic-grid button:focus{outline:none;box-shadow:none}.avaya-ipo-classic-grid button:focus-visible{outline:none;box-shadow:var(--sd-focus-ring)}.avaya-ipo-classic-grid button:active{background:var(--sd-surface-muted)}.avaya-ipo-classic-grid .avaya-ipo-digit{font-size:clamp(15px,calc(var(--key-size) * .34),26px);font-weight:400;color:var(--sd-text);line-height:1.1}.avaya-ipo-classic-grid .avaya-ipo-letters{font-size:clamp(7px,calc(var(--key-size) * .12),10px);letter-spacing:.8px;color:var(--sd-text-subtle);margin-top:2px}.avaya-ipo-actions{display:flex;justify-content:center;width:100%;margin-top:0}.avaya-ipo-call-btn-main{width:var(--key-size);height:var(--key-size);border-radius:50%;border:none;background:var(--sd-call-accept);color:var(--sd-accent-contrast);display:inline-flex;align-items:center;justify-content:center;box-shadow:var(--sd-shadow-md);cursor:pointer}.avaya-ipo-call-btn-main:disabled{opacity:.4;cursor:not-allowed}\n"], dependencies: [{ kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i2$1.NgxIntlTelInputComponent, selector: "ngx-intl-tel-input", inputs: ["value", "preferredCountries", "enablePlaceholder", "customPlaceholder", "numberFormat", "cssClass", "onlyCountries", "enableAutoCountrySelect", "searchCountryFlag", "searchCountryField", "searchCountryPlaceholder", "maxLength", "selectFirstCountry", "selectedCountryISO", "phoneValidation", "inputId", "separateDialCode"], outputs: ["countryChange"] }, { kind: "directive", type: i2$1.NativeElementInjectorDirective, selector: "[ngModel], [formControl], [formControlName]" }] });
|
|
3246
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.9", type: PhoneIdleComponent, isStandalone: false, selector: "app-phone-idle", inputs: { dialForm: "dialForm", contacts: "contacts", showDirectoryPhonebook: "showDirectoryPhonebook", isRinging: "isRinging", CountryISO: "CountryISO", SearchCountryField: "SearchCountryField" }, outputs: { toggleDirectory: "toggleDirectory", clear: "clear", makeCall: "makeCall", keypadInput: "keypadInput", keyboardInput: "keyboardInput" }, ngImport: i0, template: "<form class=\"avaya-ipo-phone-shell avaya-ipo-classic\" [formGroup]=\"dialForm\" (ngSubmit)=\"onMakeCall()\">\n <div class=\"avaya-ipo-input-row avaya-ipo-classic-input\">\n <ngx-intl-tel-input\n formControlName=\"phoneNumber\"\n name=\"phoneNumber\"\n [cssClass]=\"'avaya-ipo-phone_number_sd'\"\n [preferredCountries]=\"['in']\"\n [customPlaceholder]=\"'Phone Number'\"\n [enableAutoCountrySelect]=\"false\"\n [enablePlaceholder]=\"false\"\n [searchCountryFlag]=\"true\"\n [searchCountryField]=\"[SearchCountryField.Iso2, SearchCountryField.Name]\"\n [selectFirstCountry]=\"false\"\n [selectedCountryISO]=\"CountryISO.India\"\n [maxLength]=\"15\"\n [phoneValidation]=\"true\"\n [separateDialCode]=\"true\"\n (keydown)=\"onKeyboardInput($event)\"\n >\n </ngx-intl-tel-input>\n <button type=\"button\" class=\"avaya-ipo-clear-btn\" (click)=\"onClear()\" aria-label=\"Backspace\" title=\"Backspace\">\n <svg viewBox=\"0 0 24 24\" width=\"20\" height=\"20\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\n <path d=\"M22 3H7c-.69 0-1.23.35-1.59.88L0 12l5.41 8.12c.36.53.9.88 1.59.88h15c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-3 12.59L17.59 17 14 13.41 10.41 17 9 15.59 12.59 12 9 8.41 10.41 7 14 10.59 17.59 7 19 8.41 15.41 12 19 15.59z\" />\n </svg>\n </button>\n </div>\n\n <div class=\"avaya-ipo-dial-cluster\">\n <div class=\"avaya-ipo-keypad avaya-ipo-classic-grid\">\n <button type=\"button\" (click)=\"onKeypadInput('1')\">\n <span class=\"avaya-ipo-digit\">1</span>\n <span class=\"avaya-ipo-letters\"> </span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('2')\">\n <span class=\"avaya-ipo-digit\">2</span>\n <span class=\"avaya-ipo-letters\">ABC</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('3')\">\n <span class=\"avaya-ipo-digit\">3</span>\n <span class=\"avaya-ipo-letters\">DEF</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('4')\">\n <span class=\"avaya-ipo-digit\">4</span>\n <span class=\"avaya-ipo-letters\">GHI</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('5')\">\n <span class=\"avaya-ipo-digit\">5</span>\n <span class=\"avaya-ipo-letters\">JKL</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('6')\">\n <span class=\"avaya-ipo-digit\">6</span>\n <span class=\"avaya-ipo-letters\">MNO</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('7')\">\n <span class=\"avaya-ipo-digit\">7</span>\n <span class=\"avaya-ipo-letters\">PQRS</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('8')\">\n <span class=\"avaya-ipo-digit\">8</span>\n <span class=\"avaya-ipo-letters\">TUV</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('9')\">\n <span class=\"avaya-ipo-digit\">9</span>\n <span class=\"avaya-ipo-letters\">WXYZ</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('*')\">\n <span class=\"avaya-ipo-digit\">*</span>\n <span class=\"avaya-ipo-letters\"> </span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('0')\">\n <span class=\"avaya-ipo-digit\">0</span>\n <span class=\"avaya-ipo-letters\">+</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('#')\">\n <span class=\"avaya-ipo-digit\">#</span>\n <span class=\"avaya-ipo-letters\"> </span>\n </button>\n </div>\n\n <div class=\"avaya-ipo-actions\">\n <button type=\"submit\" class=\"avaya-ipo-call-btn-main\" [disabled]=\"!this.dialForm.valid\">\n <svg viewBox=\"0 0 512 512\" width=\"22px\" height=\"22px\" fill=\"currentColor\" aria-hidden=\"true\">\n <path\n d=\"M164.9 24.6c-7.7-18.6-28-28.5-47.4-23.2l-88 24C12.1 30.2 0 46 0 64C0 311.4 200.6 512 448 512c18 0 33.8-12.1 38.6-29.5l24-88c5.3-19.4-4.6-39.7-23.2-47.4l-96-40c-16.3-6.8-35.2-2.1-46.3 11.6L304.7 368C234.3 334.7 177.3 277.7 144 207.3L193.3 167c13.7-11.2 18.4-30 11.6-46.3l-40-96z\" />\n </svg>\n </button>\n </div>\n </div>\n</form>\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0}.avaya-ipo-phone-shell.avaya-ipo-classic{--key-size: clamp(40px, min(24cqw, 18cqh - 30px), 88px);--key-gap: calc(var(--key-size) * .16);display:flex;flex-direction:column;align-items:center;justify-content:flex-start;gap:clamp(35px,4.5cqw,30px);background:transparent;width:100%;height:100%;flex:1 1 auto;min-height:0;padding:46px 15px clamp(12px,3cqw,20px);box-sizing:border-box}.avaya-ipo-dial-cluster{flex:1 1 auto;min-height:0;width:100%;display:flex;flex-direction:column;align-items:center;justify-content:flex-start;gap:clamp(10px,2.6cqw,18px)}.avaya-ipo-classic-input{width:100%;display:flex;align-items:center;padding:0;box-sizing:border-box;position:relative;border:1.5px solid var(--sd-input-border);border-radius:var(--sd-radius);background:var(--sd-input);box-shadow:var(--sd-shadow-sm);margin-bottom:0;transition:border-color .18s ease,box-shadow .18s ease}.avaya-ipo-classic-input:focus-within{border-color:var(--sd-accent);box-shadow:var(--sd-focus-ring)}.avaya-ipo-clear-btn{position:absolute;right:clamp(5px,1.6cqw,9px);top:50%;transform:translateY(-50%);width:34px;height:34px;display:inline-flex;align-items:center;justify-content:center;border:none;background:transparent;color:var(--sd-text-subtle);border-radius:50%;padding:0;cursor:pointer;transition:background .15s ease,color .15s ease,transform .1s ease}.avaya-ipo-clear-btn:hover{background:var(--sd-surface-muted);color:var(--sd-accent)}.avaya-ipo-clear-btn:active{transform:translateY(-50%) scale(.9)}.avaya-ipo-clear-btn:focus{outline:none;box-shadow:none}.avaya-ipo-clear-btn:focus-visible{outline:none;box-shadow:var(--sd-focus-ring)}.avaya-ipo-clear-btn svg{display:block}.avaya-ipo-classic-input .avaya-ipo-phone_number_sd{width:100%}.avaya-ipo-classic-input .avaya-ipo-phone_number_sd input{width:100%;border:none!important;box-shadow:none!important;background:transparent!important;padding:clamp(8px,calc(var(--key-size) * .16),15px) 46px clamp(8px,calc(var(--key-size) * .16),15px) 0!important;font-size:clamp(12px,calc(var(--key-size) * .27),20px)!important;font-weight:600;letter-spacing:.4px;color:var(--sd-text)!important}.avaya-ipo-classic-input .avaya-ipo-phone_number_sd input::placeholder{font-weight:400;letter-spacing:normal;color:var(--sd-text-subtle)}:host ::ng-deep .avaya-ipo-classic-input .iti{width:100%}:host ::ng-deep .avaya-ipo-classic-input .iti__selected-flag{padding:0 10px 0 14px;border-right:1px solid var(--sd-border);border-radius:calc(var(--sd-radius) - 2px) 0 0 calc(var(--sd-radius) - 2px);background:transparent;outline:none;transition:background .15s ease}:host ::ng-deep .avaya-ipo-classic-input .iti--allow-dropdown .iti__flag-container:hover .iti__selected-flag{background:var(--sd-surface-muted)}:host ::ng-deep .avaya-ipo-classic-input .selected-dial-code{margin-left:6px;font-size:clamp(12px,calc(var(--key-size) * .18),16px);font-weight:600;color:var(--sd-text)}:host ::ng-deep .avaya-ipo-classic-input .iti__selected-flag:focus,:host ::ng-deep .avaya-ipo-classic-input .iti__tel-input:focus,.avaya-ipo-classic-input .avaya-ipo-phone_number_sd input:focus{outline:none;box-shadow:none}:host ::ng-deep .avaya-ipo-classic-input .iti__tel-input{background:transparent!important;border:none!important;box-shadow:none!important;height:auto;padding:clamp(8px,calc(var(--key-size) * .16),15px) 46px clamp(8px,calc(var(--key-size) * .16),15px) 0;font-size:clamp(12px,calc(var(--key-size) * .27),20px);font-weight:600;letter-spacing:.4px;color:var(--sd-text)}:host ::ng-deep .avaya-ipo-classic-input .iti.separate-dial-code .iti__tel-input{padding-left:102px!important}:host ::ng-deep .avaya-ipo-classic-input .iti__dropdown-content,:host ::ng-deep .avaya-ipo-classic-input .dropdown-menu{z-index:30;min-width:248px;padding:6px;background:var(--sd-surface);border:1px solid var(--sd-border);border-radius:var(--sd-radius);box-shadow:var(--sd-shadow-lg)}:host ::ng-deep .avaya-ipo-classic-input .iti__country-list{max-height:220px;overflow-y:auto;margin:0;padding:0;border:none;box-shadow:none;background:transparent}:host ::ng-deep .avaya-ipo-classic-input .search-container{position:sticky;top:0;z-index:1;padding:0 0 6px;background:var(--sd-surface)}:host ::ng-deep .avaya-ipo-classic-input #country-search-box{width:100%;box-sizing:border-box;margin:0!important;padding:8px 12px!important;font-size:14px!important;font-weight:400!important;letter-spacing:normal!important;color:var(--sd-text)!important;background:var(--sd-input)!important;border:1px solid var(--sd-input-border)!important;border-radius:var(--sd-radius-sm)!important;box-shadow:none!important;height:auto!important;transition:border-color .15s ease,box-shadow .15s ease}:host ::ng-deep .avaya-ipo-classic-input #country-search-box:focus{outline:none!important;border-color:var(--sd-accent)!important;box-shadow:var(--sd-focus-ring)!important}:host ::ng-deep .avaya-ipo-classic-input #country-search-box::placeholder{color:var(--sd-text-subtle)!important}:host ::ng-deep .avaya-ipo-classic-input .iti__country{padding:7px 8px;border-radius:var(--sd-radius-sm);font-size:14px;color:var(--sd-text)}:host ::ng-deep .avaya-ipo-classic-input .iti__country.iti__highlight,:host ::ng-deep .avaya-ipo-classic-input .iti__country:hover{background:var(--sd-surface-muted)}:host ::ng-deep .avaya-ipo-classic-input .iti__country .iti__dial-code{color:var(--sd-text-subtle)}:host ::ng-deep .avaya-ipo-classic-input .iti__divider{border-top:1px solid var(--sd-border);margin:4px 0}.avaya-ipo-classic-grid{width:auto;margin:0 auto;display:grid;grid-template-columns:repeat(3,var(--key-size));gap:var(--key-gap);justify-content:center;flex:0 1 auto}.avaya-ipo-classic-grid button{width:var(--key-size);height:var(--key-size);border-radius:50%;border:1.6px solid var(--sd-border-strong);background:transparent;display:flex;flex-direction:column;align-items:center;justify-content:center;cursor:pointer}.avaya-ipo-classic-grid button:focus{outline:none;box-shadow:none}.avaya-ipo-classic-grid button:focus-visible{outline:none;box-shadow:var(--sd-focus-ring)}.avaya-ipo-classic-grid button:active{background:var(--sd-surface-muted)}.avaya-ipo-classic-grid .avaya-ipo-digit{font-size:clamp(15px,calc(var(--key-size) * .34),26px);font-weight:400;color:var(--sd-text);line-height:1.1}.avaya-ipo-classic-grid .avaya-ipo-letters{font-size:clamp(7px,calc(var(--key-size) * .12),10px);letter-spacing:.8px;color:var(--sd-text-subtle);margin-top:2px}.avaya-ipo-actions{display:flex;justify-content:center;width:100%;margin-top:0}.avaya-ipo-call-btn-main{width:var(--key-size);height:var(--key-size);border-radius:50%;border:none;background:var(--sd-call-accept);color:var(--sd-accent-contrast);display:inline-flex;align-items:center;justify-content:center;box-shadow:var(--sd-shadow-md);cursor:pointer}.avaya-ipo-call-btn-main:disabled{opacity:.4;cursor:not-allowed}\n"], dependencies: [{ kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i2$1.NgxIntlTelInputComponent, selector: "ngx-intl-tel-input", inputs: ["value", "preferredCountries", "enablePlaceholder", "customPlaceholder", "numberFormat", "cssClass", "onlyCountries", "enableAutoCountrySelect", "searchCountryFlag", "searchCountryField", "searchCountryPlaceholder", "maxLength", "selectFirstCountry", "selectedCountryISO", "phoneValidation", "inputId", "separateDialCode"], outputs: ["countryChange"] }, { kind: "directive", type: i2$1.NativeElementInjectorDirective, selector: "[ngModel], [formControl], [formControlName]" }] });
|
|
2903
3247
|
}
|
|
2904
3248
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: PhoneIdleComponent, decorators: [{
|
|
2905
3249
|
type: Component,
|
|
2906
|
-
args: [{ selector: 'app-phone-idle', standalone: false, template: "<form class=\"avaya-ipo-phone-shell avaya-ipo-classic\" [formGroup]=\"dialForm\" (ngSubmit)=\"onMakeCall()\">\n <div class=\"avaya-ipo-input-row avaya-ipo-classic-input\">\n <ngx-intl-tel-input\n formControlName=\"phoneNumber\"\n name=\"phoneNumber\"\n [cssClass]=\"'avaya-ipo-phone_number_sd'\"\n [preferredCountries]=\"['in']\"\n [customPlaceholder]=\"'Phone Number'\"\n [enableAutoCountrySelect]=\"false\"\n [enablePlaceholder]=\"false\"\n [searchCountryFlag]=\"true\"\n [searchCountryField]=\"[SearchCountryField.Iso2, SearchCountryField.Name]\"\n [selectFirstCountry]=\"false\"\n [selectedCountryISO]=\"CountryISO.India\"\n [maxLength]=\"15\"\n [phoneValidation]=\"true\"\n [separateDialCode]=\"true\"\n (keydown)=\"onKeyboardInput($event)\"\n >\n </ngx-intl-tel-input>\n <button type=\"button\" class=\"avaya-ipo-clear-btn\" (click)=\"onClear()\" aria-label=\"Backspace\" title=\"Backspace\">\n <svg viewBox=\"0 0 24 24\" width=\"20\" height=\"20\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\n <path d=\"M22 3H7c-.69 0-1.23.35-1.59.88L0 12l5.41 8.12c.36.53.9.88 1.59.88h15c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-3 12.59L17.59 17 14 13.41 10.41 17 9 15.59 12.59 12 9 8.41 10.41 7 14 10.59 17.59 7 19 8.41 15.41 12 19 15.59z\" />\n </svg>\n </button>\n </div>\n\n <div class=\"avaya-ipo-dial-cluster\">\n <div class=\"avaya-ipo-keypad avaya-ipo-classic-grid\">\n <button type=\"button\" (click)=\"onKeypadInput('1')\">\n <span class=\"avaya-ipo-digit\">1</span>\n <span class=\"avaya-ipo-letters\"> </span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('2')\">\n <span class=\"avaya-ipo-digit\">2</span>\n <span class=\"avaya-ipo-letters\">ABC</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('3')\">\n <span class=\"avaya-ipo-digit\">3</span>\n <span class=\"avaya-ipo-letters\">DEF</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('4')\">\n <span class=\"avaya-ipo-digit\">4</span>\n <span class=\"avaya-ipo-letters\">GHI</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('5')\">\n <span class=\"avaya-ipo-digit\">5</span>\n <span class=\"avaya-ipo-letters\">JKL</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('6')\">\n <span class=\"avaya-ipo-digit\">6</span>\n <span class=\"avaya-ipo-letters\">MNO</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('7')\">\n <span class=\"avaya-ipo-digit\">7</span>\n <span class=\"avaya-ipo-letters\">PQRS</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('8')\">\n <span class=\"avaya-ipo-digit\">8</span>\n <span class=\"avaya-ipo-letters\">TUV</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('9')\">\n <span class=\"avaya-ipo-digit\">9</span>\n <span class=\"avaya-ipo-letters\">WXYZ</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('*')\">\n <span class=\"avaya-ipo-digit\">*</span>\n <span class=\"avaya-ipo-letters\"> </span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('0')\">\n <span class=\"avaya-ipo-digit\">0</span>\n <span class=\"avaya-ipo-letters\">+</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('#')\">\n <span class=\"avaya-ipo-digit\">#</span>\n <span class=\"avaya-ipo-letters\"> </span>\n </button>\n </div>\n\n <div class=\"avaya-ipo-actions\">\n <button type=\"submit\" class=\"avaya-ipo-call-btn-main\" [disabled]=\"!this.dialForm.valid\">\n <svg viewBox=\"0 0 512 512\" width=\"22px\" height=\"22px\" fill=\"currentColor\" aria-hidden=\"true\">\n <path\n d=\"M164.9 24.6c-7.7-18.6-28-28.5-47.4-23.2l-88 24C12.1 30.2 0 46 0 64C0 311.4 200.6 512 448 512c18 0 33.8-12.1 38.6-29.5l24-88c5.3-19.4-4.6-39.7-23.2-47.4l-96-40c-16.3-6.8-35.2-2.1-46.3 11.6L304.7 368C234.3 334.7 177.3 277.7 144 207.3L193.3 167c13.7-11.2 18.4-30 11.6-46.3l-40-96z\" />\n </svg>\n </button>\n </div>\n </div>\n</form>\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0}.avaya-ipo-phone-shell.avaya-ipo-classic{--key-size: clamp(40px, min(24cqw, 18cqh - 30px), 88px);--key-gap: calc(var(--key-size) * .16);display:flex;flex-direction:column;align-items:center;justify-content:flex-start;gap:clamp(35px,4.5cqw,30px);background:transparent;width:100%;height:100%;flex:1 1 auto;min-height:0;padding:46px 15px clamp(12px,3cqw,20px);box-sizing:border-box}.avaya-ipo-dial-cluster{flex:1 1 auto;min-height:0;width:100%;display:flex;flex-direction:column;align-items:center;justify-content:flex-start;gap:clamp(10px,2.6cqw,18px)}.avaya-ipo-classic-input{width:100%;display:flex;align-items:center;padding:0;box-sizing:border-box;position:relative;border:1.5px solid var(--sd-input-border);border-radius:var(--sd-radius);background:var(--sd-input);box-shadow:var(--sd-shadow-sm);margin-bottom:0;transition:border-color .18s ease,box-shadow .18s ease}.avaya-ipo-classic-input:focus-within{border-color:var(--sd-accent);box-shadow:var(--sd-focus-ring)}.avaya-ipo-phonebook-btn{border:none;background:transparent;color:var(--sd-text-muted);width:32px;height:32px;border-radius:var(--sd-radius-sm);display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;cursor:pointer}.avaya-ipo-phonebook-btn:hover{background:var(--sd-surface-muted)}.avaya-ipo-phonebook-btn:focus{outline:none;box-shadow:none}.avaya-ipo-phonebook-btn:focus-visible{outline:none;box-shadow:var(--sd-focus-ring)}.avaya-ipo-clear-btn{position:absolute;right:clamp(5px,1.6cqw,9px);top:50%;transform:translateY(-50%);width:34px;height:34px;display:inline-flex;align-items:center;justify-content:center;border:none;background:transparent;color:var(--sd-text-subtle);border-radius:50%;padding:0;cursor:pointer;transition:background .15s ease,color .15s ease,transform .1s ease}.avaya-ipo-clear-btn:hover{background:var(--sd-surface-muted);color:var(--sd-accent)}.avaya-ipo-clear-btn:active{transform:translateY(-50%) scale(.9)}.avaya-ipo-clear-btn:focus{outline:none;box-shadow:none}.avaya-ipo-clear-btn:focus-visible{outline:none;box-shadow:var(--sd-focus-ring)}.avaya-ipo-clear-btn svg{display:block}.avaya-ipo-classic-input .avaya-ipo-phone_number_sd{width:100%}.avaya-ipo-classic-input .avaya-ipo-phone_number_sd input{width:100%;border:none!important;box-shadow:none!important;background:transparent!important;padding:clamp(8px,calc(var(--key-size) * .16),15px) 46px clamp(8px,calc(var(--key-size) * .16),15px) 0!important;font-size:clamp(12px,calc(var(--key-size) * .27),20px)!important;font-weight:600;letter-spacing:.4px;color:var(--sd-text)!important}.avaya-ipo-classic-input .avaya-ipo-phone_number_sd input::placeholder{font-weight:400;letter-spacing:normal;color:var(--sd-text-subtle)}:host ::ng-deep .avaya-ipo-classic-input .iti{width:100%}:host ::ng-deep .avaya-ipo-classic-input .iti__selected-flag{padding:0 10px 0 14px;border-right:1px solid var(--sd-border);border-radius:calc(var(--sd-radius) - 2px) 0 0 calc(var(--sd-radius) - 2px);background:transparent;outline:none;transition:background .15s ease}:host ::ng-deep .avaya-ipo-classic-input .iti--allow-dropdown .iti__flag-container:hover .iti__selected-flag{background:var(--sd-surface-muted)}:host ::ng-deep .avaya-ipo-classic-input .selected-dial-code{margin-left:6px;font-size:clamp(12px,calc(var(--key-size) * .18),16px);font-weight:600;color:var(--sd-text)}:host ::ng-deep .avaya-ipo-classic-input .iti__selected-flag:focus,:host ::ng-deep .avaya-ipo-classic-input .iti__tel-input:focus,.avaya-ipo-classic-input .avaya-ipo-phone_number_sd input:focus{outline:none;box-shadow:none}:host ::ng-deep .avaya-ipo-classic-input .iti__tel-input{background:transparent!important;border:none!important;box-shadow:none!important;height:auto;padding:clamp(8px,calc(var(--key-size) * .16),15px) 46px clamp(8px,calc(var(--key-size) * .16),15px) 0;font-size:clamp(12px,calc(var(--key-size) * .27),20px);font-weight:600;letter-spacing:.4px;color:var(--sd-text)}:host ::ng-deep .avaya-ipo-classic-input .iti.separate-dial-code .iti__tel-input{padding-left:102px!important}:host ::ng-deep .avaya-ipo-classic-input .iti__dropdown-content,:host ::ng-deep .avaya-ipo-classic-input .dropdown-menu{z-index:30;min-width:248px;padding:6px;background:var(--sd-surface);border:1px solid var(--sd-border);border-radius:var(--sd-radius);box-shadow:var(--sd-shadow-lg)}:host ::ng-deep .avaya-ipo-classic-input .iti__country-list{max-height:220px;overflow-y:auto;margin:0;padding:0;border:none;box-shadow:none;background:transparent}:host ::ng-deep .avaya-ipo-classic-input .search-container{position:sticky;top:0;z-index:1;padding:0 0 6px;background:var(--sd-surface)}:host ::ng-deep .avaya-ipo-classic-input #country-search-box{width:100%;box-sizing:border-box;margin:0!important;padding:8px 12px!important;font-size:14px!important;font-weight:400!important;letter-spacing:normal!important;color:var(--sd-text)!important;background:var(--sd-input)!important;border:1px solid var(--sd-input-border)!important;border-radius:var(--sd-radius-sm)!important;box-shadow:none!important;height:auto!important;transition:border-color .15s ease,box-shadow .15s ease}:host ::ng-deep .avaya-ipo-classic-input #country-search-box:focus{outline:none!important;border-color:var(--sd-accent)!important;box-shadow:var(--sd-focus-ring)!important}:host ::ng-deep .avaya-ipo-classic-input #country-search-box::placeholder{color:var(--sd-text-subtle)!important}:host ::ng-deep .avaya-ipo-classic-input .iti__country{padding:7px 8px;border-radius:var(--sd-radius-sm);font-size:14px;color:var(--sd-text)}:host ::ng-deep .avaya-ipo-classic-input .iti__country.iti__highlight,:host ::ng-deep .avaya-ipo-classic-input .iti__country:hover{background:var(--sd-surface-muted)}:host ::ng-deep .avaya-ipo-classic-input .iti__country .iti__dial-code{color:var(--sd-text-subtle)}:host ::ng-deep .avaya-ipo-classic-input .iti__divider{border-top:1px solid var(--sd-border);margin:4px 0}.avaya-ipo-classic-grid{width:auto;margin:0 auto;display:grid;grid-template-columns:repeat(3,var(--key-size));gap:var(--key-gap);justify-content:center;flex:0 1 auto}.avaya-ipo-classic-grid button{width:var(--key-size);height:var(--key-size);border-radius:50%;border:1.6px solid var(--sd-border-strong);background:transparent;display:flex;flex-direction:column;align-items:center;justify-content:center;cursor:pointer}.avaya-ipo-classic-grid button:focus{outline:none;box-shadow:none}.avaya-ipo-classic-grid button:focus-visible{outline:none;box-shadow:var(--sd-focus-ring)}.avaya-ipo-classic-grid button:active{background:var(--sd-surface-muted)}.avaya-ipo-classic-grid .avaya-ipo-digit{font-size:clamp(15px,calc(var(--key-size) * .34),26px);font-weight:400;color:var(--sd-text);line-height:1.1}.avaya-ipo-classic-grid .avaya-ipo-letters{font-size:clamp(7px,calc(var(--key-size) * .12),10px);letter-spacing:.8px;color:var(--sd-text-subtle);margin-top:2px}.avaya-ipo-actions{display:flex;justify-content:center;width:100%;margin-top:0}.avaya-ipo-call-btn-main{width:var(--key-size);height:var(--key-size);border-radius:50%;border:none;background:var(--sd-call-accept);color:var(--sd-accent-contrast);display:inline-flex;align-items:center;justify-content:center;box-shadow:var(--sd-shadow-md);cursor:pointer}.avaya-ipo-call-btn-main:disabled{opacity:.4;cursor:not-allowed}\n"] }]
|
|
3250
|
+
args: [{ selector: 'app-phone-idle', standalone: false, template: "<form class=\"avaya-ipo-phone-shell avaya-ipo-classic\" [formGroup]=\"dialForm\" (ngSubmit)=\"onMakeCall()\">\n <div class=\"avaya-ipo-input-row avaya-ipo-classic-input\">\n <ngx-intl-tel-input\n formControlName=\"phoneNumber\"\n name=\"phoneNumber\"\n [cssClass]=\"'avaya-ipo-phone_number_sd'\"\n [preferredCountries]=\"['in']\"\n [customPlaceholder]=\"'Phone Number'\"\n [enableAutoCountrySelect]=\"false\"\n [enablePlaceholder]=\"false\"\n [searchCountryFlag]=\"true\"\n [searchCountryField]=\"[SearchCountryField.Iso2, SearchCountryField.Name]\"\n [selectFirstCountry]=\"false\"\n [selectedCountryISO]=\"CountryISO.India\"\n [maxLength]=\"15\"\n [phoneValidation]=\"true\"\n [separateDialCode]=\"true\"\n (keydown)=\"onKeyboardInput($event)\"\n >\n </ngx-intl-tel-input>\n <button type=\"button\" class=\"avaya-ipo-clear-btn\" (click)=\"onClear()\" aria-label=\"Backspace\" title=\"Backspace\">\n <svg viewBox=\"0 0 24 24\" width=\"20\" height=\"20\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\">\n <path d=\"M22 3H7c-.69 0-1.23.35-1.59.88L0 12l5.41 8.12c.36.53.9.88 1.59.88h15c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-3 12.59L17.59 17 14 13.41 10.41 17 9 15.59 12.59 12 9 8.41 10.41 7 14 10.59 17.59 7 19 8.41 15.41 12 19 15.59z\" />\n </svg>\n </button>\n </div>\n\n <div class=\"avaya-ipo-dial-cluster\">\n <div class=\"avaya-ipo-keypad avaya-ipo-classic-grid\">\n <button type=\"button\" (click)=\"onKeypadInput('1')\">\n <span class=\"avaya-ipo-digit\">1</span>\n <span class=\"avaya-ipo-letters\"> </span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('2')\">\n <span class=\"avaya-ipo-digit\">2</span>\n <span class=\"avaya-ipo-letters\">ABC</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('3')\">\n <span class=\"avaya-ipo-digit\">3</span>\n <span class=\"avaya-ipo-letters\">DEF</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('4')\">\n <span class=\"avaya-ipo-digit\">4</span>\n <span class=\"avaya-ipo-letters\">GHI</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('5')\">\n <span class=\"avaya-ipo-digit\">5</span>\n <span class=\"avaya-ipo-letters\">JKL</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('6')\">\n <span class=\"avaya-ipo-digit\">6</span>\n <span class=\"avaya-ipo-letters\">MNO</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('7')\">\n <span class=\"avaya-ipo-digit\">7</span>\n <span class=\"avaya-ipo-letters\">PQRS</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('8')\">\n <span class=\"avaya-ipo-digit\">8</span>\n <span class=\"avaya-ipo-letters\">TUV</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('9')\">\n <span class=\"avaya-ipo-digit\">9</span>\n <span class=\"avaya-ipo-letters\">WXYZ</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('*')\">\n <span class=\"avaya-ipo-digit\">*</span>\n <span class=\"avaya-ipo-letters\"> </span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('0')\">\n <span class=\"avaya-ipo-digit\">0</span>\n <span class=\"avaya-ipo-letters\">+</span>\n </button>\n <button type=\"button\" (click)=\"onKeypadInput('#')\">\n <span class=\"avaya-ipo-digit\">#</span>\n <span class=\"avaya-ipo-letters\"> </span>\n </button>\n </div>\n\n <div class=\"avaya-ipo-actions\">\n <button type=\"submit\" class=\"avaya-ipo-call-btn-main\" [disabled]=\"!this.dialForm.valid\">\n <svg viewBox=\"0 0 512 512\" width=\"22px\" height=\"22px\" fill=\"currentColor\" aria-hidden=\"true\">\n <path\n d=\"M164.9 24.6c-7.7-18.6-28-28.5-47.4-23.2l-88 24C12.1 30.2 0 46 0 64C0 311.4 200.6 512 448 512c18 0 33.8-12.1 38.6-29.5l24-88c5.3-19.4-4.6-39.7-23.2-47.4l-96-40c-16.3-6.8-35.2-2.1-46.3 11.6L304.7 368C234.3 334.7 177.3 277.7 144 207.3L193.3 167c13.7-11.2 18.4-30 11.6-46.3l-40-96z\" />\n </svg>\n </button>\n </div>\n </div>\n</form>\n", styles: [":host{display:flex;flex-direction:column;height:100%;min-height:0}.avaya-ipo-phone-shell.avaya-ipo-classic{--key-size: clamp(40px, min(24cqw, 18cqh - 30px), 88px);--key-gap: calc(var(--key-size) * .16);display:flex;flex-direction:column;align-items:center;justify-content:flex-start;gap:clamp(35px,4.5cqw,30px);background:transparent;width:100%;height:100%;flex:1 1 auto;min-height:0;padding:46px 15px clamp(12px,3cqw,20px);box-sizing:border-box}.avaya-ipo-dial-cluster{flex:1 1 auto;min-height:0;width:100%;display:flex;flex-direction:column;align-items:center;justify-content:flex-start;gap:clamp(10px,2.6cqw,18px)}.avaya-ipo-classic-input{width:100%;display:flex;align-items:center;padding:0;box-sizing:border-box;position:relative;border:1.5px solid var(--sd-input-border);border-radius:var(--sd-radius);background:var(--sd-input);box-shadow:var(--sd-shadow-sm);margin-bottom:0;transition:border-color .18s ease,box-shadow .18s ease}.avaya-ipo-classic-input:focus-within{border-color:var(--sd-accent);box-shadow:var(--sd-focus-ring)}.avaya-ipo-clear-btn{position:absolute;right:clamp(5px,1.6cqw,9px);top:50%;transform:translateY(-50%);width:34px;height:34px;display:inline-flex;align-items:center;justify-content:center;border:none;background:transparent;color:var(--sd-text-subtle);border-radius:50%;padding:0;cursor:pointer;transition:background .15s ease,color .15s ease,transform .1s ease}.avaya-ipo-clear-btn:hover{background:var(--sd-surface-muted);color:var(--sd-accent)}.avaya-ipo-clear-btn:active{transform:translateY(-50%) scale(.9)}.avaya-ipo-clear-btn:focus{outline:none;box-shadow:none}.avaya-ipo-clear-btn:focus-visible{outline:none;box-shadow:var(--sd-focus-ring)}.avaya-ipo-clear-btn svg{display:block}.avaya-ipo-classic-input .avaya-ipo-phone_number_sd{width:100%}.avaya-ipo-classic-input .avaya-ipo-phone_number_sd input{width:100%;border:none!important;box-shadow:none!important;background:transparent!important;padding:clamp(8px,calc(var(--key-size) * .16),15px) 46px clamp(8px,calc(var(--key-size) * .16),15px) 0!important;font-size:clamp(12px,calc(var(--key-size) * .27),20px)!important;font-weight:600;letter-spacing:.4px;color:var(--sd-text)!important}.avaya-ipo-classic-input .avaya-ipo-phone_number_sd input::placeholder{font-weight:400;letter-spacing:normal;color:var(--sd-text-subtle)}:host ::ng-deep .avaya-ipo-classic-input .iti{width:100%}:host ::ng-deep .avaya-ipo-classic-input .iti__selected-flag{padding:0 10px 0 14px;border-right:1px solid var(--sd-border);border-radius:calc(var(--sd-radius) - 2px) 0 0 calc(var(--sd-radius) - 2px);background:transparent;outline:none;transition:background .15s ease}:host ::ng-deep .avaya-ipo-classic-input .iti--allow-dropdown .iti__flag-container:hover .iti__selected-flag{background:var(--sd-surface-muted)}:host ::ng-deep .avaya-ipo-classic-input .selected-dial-code{margin-left:6px;font-size:clamp(12px,calc(var(--key-size) * .18),16px);font-weight:600;color:var(--sd-text)}:host ::ng-deep .avaya-ipo-classic-input .iti__selected-flag:focus,:host ::ng-deep .avaya-ipo-classic-input .iti__tel-input:focus,.avaya-ipo-classic-input .avaya-ipo-phone_number_sd input:focus{outline:none;box-shadow:none}:host ::ng-deep .avaya-ipo-classic-input .iti__tel-input{background:transparent!important;border:none!important;box-shadow:none!important;height:auto;padding:clamp(8px,calc(var(--key-size) * .16),15px) 46px clamp(8px,calc(var(--key-size) * .16),15px) 0;font-size:clamp(12px,calc(var(--key-size) * .27),20px);font-weight:600;letter-spacing:.4px;color:var(--sd-text)}:host ::ng-deep .avaya-ipo-classic-input .iti.separate-dial-code .iti__tel-input{padding-left:102px!important}:host ::ng-deep .avaya-ipo-classic-input .iti__dropdown-content,:host ::ng-deep .avaya-ipo-classic-input .dropdown-menu{z-index:30;min-width:248px;padding:6px;background:var(--sd-surface);border:1px solid var(--sd-border);border-radius:var(--sd-radius);box-shadow:var(--sd-shadow-lg)}:host ::ng-deep .avaya-ipo-classic-input .iti__country-list{max-height:220px;overflow-y:auto;margin:0;padding:0;border:none;box-shadow:none;background:transparent}:host ::ng-deep .avaya-ipo-classic-input .search-container{position:sticky;top:0;z-index:1;padding:0 0 6px;background:var(--sd-surface)}:host ::ng-deep .avaya-ipo-classic-input #country-search-box{width:100%;box-sizing:border-box;margin:0!important;padding:8px 12px!important;font-size:14px!important;font-weight:400!important;letter-spacing:normal!important;color:var(--sd-text)!important;background:var(--sd-input)!important;border:1px solid var(--sd-input-border)!important;border-radius:var(--sd-radius-sm)!important;box-shadow:none!important;height:auto!important;transition:border-color .15s ease,box-shadow .15s ease}:host ::ng-deep .avaya-ipo-classic-input #country-search-box:focus{outline:none!important;border-color:var(--sd-accent)!important;box-shadow:var(--sd-focus-ring)!important}:host ::ng-deep .avaya-ipo-classic-input #country-search-box::placeholder{color:var(--sd-text-subtle)!important}:host ::ng-deep .avaya-ipo-classic-input .iti__country{padding:7px 8px;border-radius:var(--sd-radius-sm);font-size:14px;color:var(--sd-text)}:host ::ng-deep .avaya-ipo-classic-input .iti__country.iti__highlight,:host ::ng-deep .avaya-ipo-classic-input .iti__country:hover{background:var(--sd-surface-muted)}:host ::ng-deep .avaya-ipo-classic-input .iti__country .iti__dial-code{color:var(--sd-text-subtle)}:host ::ng-deep .avaya-ipo-classic-input .iti__divider{border-top:1px solid var(--sd-border);margin:4px 0}.avaya-ipo-classic-grid{width:auto;margin:0 auto;display:grid;grid-template-columns:repeat(3,var(--key-size));gap:var(--key-gap);justify-content:center;flex:0 1 auto}.avaya-ipo-classic-grid button{width:var(--key-size);height:var(--key-size);border-radius:50%;border:1.6px solid var(--sd-border-strong);background:transparent;display:flex;flex-direction:column;align-items:center;justify-content:center;cursor:pointer}.avaya-ipo-classic-grid button:focus{outline:none;box-shadow:none}.avaya-ipo-classic-grid button:focus-visible{outline:none;box-shadow:var(--sd-focus-ring)}.avaya-ipo-classic-grid button:active{background:var(--sd-surface-muted)}.avaya-ipo-classic-grid .avaya-ipo-digit{font-size:clamp(15px,calc(var(--key-size) * .34),26px);font-weight:400;color:var(--sd-text);line-height:1.1}.avaya-ipo-classic-grid .avaya-ipo-letters{font-size:clamp(7px,calc(var(--key-size) * .12),10px);letter-spacing:.8px;color:var(--sd-text-subtle);margin-top:2px}.avaya-ipo-actions{display:flex;justify-content:center;width:100%;margin-top:0}.avaya-ipo-call-btn-main{width:var(--key-size);height:var(--key-size);border-radius:50%;border:none;background:var(--sd-call-accept);color:var(--sd-accent-contrast);display:inline-flex;align-items:center;justify-content:center;box-shadow:var(--sd-shadow-md);cursor:pointer}.avaya-ipo-call-btn-main:disabled{opacity:.4;cursor:not-allowed}\n"] }]
|
|
2907
3251
|
}], propDecorators: { dialForm: [{
|
|
2908
3252
|
type: Input
|
|
2909
3253
|
}], contacts: [{
|
|
@@ -3108,6 +3452,9 @@ class PhoneComponent {
|
|
|
3108
3452
|
turnIp;
|
|
3109
3453
|
turnPort;
|
|
3110
3454
|
configurationMode;
|
|
3455
|
+
recordingUploadMode;
|
|
3456
|
+
/** Entity id of the call counterpart (resolved by the shell) — included in recording metadata. */
|
|
3457
|
+
entityId;
|
|
3111
3458
|
makeCallEv = new EventEmitter();
|
|
3112
3459
|
endCallEv = new EventEmitter();
|
|
3113
3460
|
audioElement;
|
|
@@ -3127,7 +3474,6 @@ class PhoneComponent {
|
|
|
3127
3474
|
pendingTranscriptionCallId = null;
|
|
3128
3475
|
pendingTranscriptionAllowBinary = false;
|
|
3129
3476
|
lastPartialIndex = {};
|
|
3130
|
-
pendingIncomingCall = false;
|
|
3131
3477
|
dialForm;
|
|
3132
3478
|
directoryForm;
|
|
3133
3479
|
showDirectoryPhonebook = false;
|
|
@@ -3245,7 +3591,6 @@ class PhoneComponent {
|
|
|
3245
3591
|
}
|
|
3246
3592
|
if (evt.state === CallState.CONNECTED) {
|
|
3247
3593
|
this.isRinging = false;
|
|
3248
|
-
this.pendingIncomingCall = false;
|
|
3249
3594
|
this._isActiveCall = true;
|
|
3250
3595
|
if (evt.callId) {
|
|
3251
3596
|
this.currentCallId = evt.callId;
|
|
@@ -3278,7 +3623,6 @@ class PhoneComponent {
|
|
|
3278
3623
|
this.pendingTranscriptionAllowBinary = false;
|
|
3279
3624
|
}
|
|
3280
3625
|
this.isRinging = false;
|
|
3281
|
-
this.pendingIncomingCall = false;
|
|
3282
3626
|
this._isActiveCall = false;
|
|
3283
3627
|
this.isMuted = false;
|
|
3284
3628
|
this.isHeld = false;
|
|
@@ -3365,7 +3709,6 @@ class PhoneComponent {
|
|
|
3365
3709
|
this.pendingTranscriptionAllowBinary = false;
|
|
3366
3710
|
}
|
|
3367
3711
|
this.isRinging = false;
|
|
3368
|
-
this.pendingIncomingCall = false;
|
|
3369
3712
|
this._isActiveCall = false;
|
|
3370
3713
|
this.incomingCallNumber = undefined;
|
|
3371
3714
|
this.currentCallId = null;
|
|
@@ -3378,16 +3721,6 @@ class PhoneComponent {
|
|
|
3378
3721
|
this.cdr.detectChanges();
|
|
3379
3722
|
});
|
|
3380
3723
|
}
|
|
3381
|
-
ngOnChanges(changes) {
|
|
3382
|
-
if (changes['incomingNameResolved'] && this.pendingIncomingCall) {
|
|
3383
|
-
if (this.incomingNameResolved) {
|
|
3384
|
-
this.isRinging = true;
|
|
3385
|
-
this.pendingIncomingCall = false;
|
|
3386
|
-
this.emitCallScreenState();
|
|
3387
|
-
this.cdr.detectChanges();
|
|
3388
|
-
}
|
|
3389
|
-
}
|
|
3390
|
-
}
|
|
3391
3724
|
ngAfterViewInit() {
|
|
3392
3725
|
this.audio = this.audioElement?.nativeElement;
|
|
3393
3726
|
}
|
|
@@ -3493,7 +3826,6 @@ class PhoneComponent {
|
|
|
3493
3826
|
declineAvayaCall() {
|
|
3494
3827
|
this.suppressHangupToneOnce = true;
|
|
3495
3828
|
this.isRinging = false;
|
|
3496
|
-
this.pendingIncomingCall = false;
|
|
3497
3829
|
this._isActiveCall = false;
|
|
3498
3830
|
this.pendingOutgoing = false;
|
|
3499
3831
|
this.avayaIpoService.clearOutgoingDial();
|
|
@@ -3510,7 +3842,6 @@ class PhoneComponent {
|
|
|
3510
3842
|
this.avayaIpoService.answerCall();
|
|
3511
3843
|
// this.headerComponent.showAvayaIpo = true;
|
|
3512
3844
|
this.isRinging = false;
|
|
3513
|
-
this.pendingIncomingCall = false;
|
|
3514
3845
|
this._isActiveCall = true;
|
|
3515
3846
|
this.callStatusText = '';
|
|
3516
3847
|
this.startCallTimer();
|
|
@@ -3606,8 +3937,10 @@ class PhoneComponent {
|
|
|
3606
3937
|
this.contactSearch = '';
|
|
3607
3938
|
this.directoryForm.reset();
|
|
3608
3939
|
this.callStatusText = '';
|
|
3609
|
-
|
|
3610
|
-
|
|
3940
|
+
// Show the ringing screen IMMEDIATELY — the caller-name lookups resolve in place (the template
|
|
3941
|
+
// shows the number first and fills the name when ready). Holding the screen until
|
|
3942
|
+
// incomingNameResolved meant two network round trips of blank UI while the phone was ringing.
|
|
3943
|
+
this.isRinging = true;
|
|
3611
3944
|
if (this.endToneTimeout) {
|
|
3612
3945
|
clearTimeout(this.endToneTimeout);
|
|
3613
3946
|
this.endToneTimeout = undefined;
|
|
@@ -4100,6 +4433,7 @@ class PhoneComponent {
|
|
|
4100
4433
|
agentId: this.agentId || '',
|
|
4101
4434
|
phoneNumber: this.activeCallNumber || '',
|
|
4102
4435
|
callId,
|
|
4436
|
+
entityId: this.entityId || undefined,
|
|
4103
4437
|
};
|
|
4104
4438
|
if (!hasAudio(local)) {
|
|
4105
4439
|
console.warn('[Phone] Missing local audio, requesting mic', callId);
|
|
@@ -4117,7 +4451,7 @@ class PhoneComponent {
|
|
|
4117
4451
|
}
|
|
4118
4452
|
const remote = this.lastRemoteStream;
|
|
4119
4453
|
if (hasAudio(remote)) {
|
|
4120
|
-
this.recordingManagerService.startRecording(local, remote, metadata);
|
|
4454
|
+
this.recordingManagerService.startRecording(local, remote, metadata, this.resolvedRecordingMode());
|
|
4121
4455
|
return;
|
|
4122
4456
|
}
|
|
4123
4457
|
const remoteSub = this.avayaIpoService.remoteStream$.subscribe((r) => {
|
|
@@ -4132,12 +4466,18 @@ class PhoneComponent {
|
|
|
4132
4466
|
return;
|
|
4133
4467
|
}
|
|
4134
4468
|
console.debug('[Phone] Remote audio ready, restarting recording', callId);
|
|
4135
|
-
this.recordingManagerService.startRecording(freshLocal, r.stream, metadata);
|
|
4469
|
+
this.recordingManagerService.startRecording(freshLocal, r.stream, metadata, this.resolvedRecordingMode());
|
|
4136
4470
|
remoteSub.unsubscribe();
|
|
4137
4471
|
});
|
|
4138
4472
|
}
|
|
4473
|
+
/** Per-embed [recordingUploadMode] wins; otherwise the APP_CONFIG default applies. */
|
|
4474
|
+
resolvedRecordingMode() {
|
|
4475
|
+
return this.recordingUploadMode === 'chunked' || this.recordingUploadMode === 'complete'
|
|
4476
|
+
? this.recordingUploadMode
|
|
4477
|
+
: APP_CONFIG.RECORDING_UPLOAD_MODE;
|
|
4478
|
+
}
|
|
4139
4479
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: PhoneComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1$2.FormBuilder }, { token: AvayaIPOService }, { token: AuthenticationService }, { token: RecordingManagerService }, { token: TranscriptionStreamService }], target: i0.ɵɵFactoryTarget.Component });
|
|
4140
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.9", type: PhoneComponent, isStandalone: false, selector: "app-phone", inputs: { contacts: "contacts", tenantId: "tenantId", agentId: "agentId", isActiveCall: "isActiveCall", activeCallNumber: "activeCallNumber", incomingCallName: "incomingCallName", incomingNameResolved: "incomingNameResolved", activeCallName: "activeCallName", serverIp: "serverIp", serverPort: "serverPort", stunIp: "stunIp", stunPort: "stunPort", turnIp: "turnIp", turnPort: "turnPort", configurationMode: "configurationMode" }, outputs: { callScreenChange: "callScreenChange", makeCallEv: "makeCallEv", endCallEv: "endCallEv" }, viewQueries: [{ propertyName: "audioElement", first: true, predicate: ["audioElement"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"avaya-ipo-phone-container\">\n <audio id=\"sd_call_ring\" #audioElement></audio>\n\n @if (isRinging) {\n <app-phone-incoming [incomingCallNumber]=\"incomingCallNumber\" [incomingCallName]=\"incomingCallName\" [incomingNameResolved]=\"incomingNameResolved\" (accept)=\"acceptAvayaCAll()\"\n (decline)=\"declineAvayaCall()\"></app-phone-incoming>\n } @else if (isActiveCall) {\n <app-phone-active [tenantId]=\"tenantId\" [activeCallNumber]=\"activeCallNumber\" [activeCallName]=\"activeCallName\"\n [activeCallDuration]=\"activeCallDuration\" [callStatusText]=\"callStatusText\" [isConnecting]=\"isConnecting\"\n [isMuted]=\"isMuted\" [isHeld]=\"isHeld\"\n (toggleMute)=\"toggleMute()\" (toggleHold)=\"toggleHold()\" (transfer)=\"onTransferCall()\"\n (openContacts)=\"onActiveOpenContacts()\" (openKeypad)=\"onActiveOpenKeypad()\"\n (startTranscription)=\"openTranscription()\" (contactsLoaded)=\"onContactsLoaded($event)\"\n (endCall)=\"onEndActiveCall()\"></app-phone-active>\n } @else {\n <app-phone-idle [dialForm]=\"dialForm\" [contacts]=\"contacts\" [showDirectoryPhonebook]=\"showDirectoryPhonebook\"\n [isRinging]=\"isRinging\" [CountryISO]=\"CountryISO\" [SearchCountryField]=\"SearchCountryField\"\n (toggleDirectory)=\"onIdleToggleDirectory()\" (clear)=\"onIdleClear()\" (makeCall)=\"onIdleMakeCall()\"\n (keypadInput)=\"onIdleKeypadInput($event)\" (keyboardInput)=\"onIdleKeyboardInput($event)\"></app-phone-idle>\n }\n</div>\n\n@if (showDirectoryPhonebook) {\n<div class=\"avaya-ipo-phonebook-overlay\">\n <div class=\"avaya-ipo-phonebook-sheet\">\n <div class=\"avaya-ipo-phonebook-header\">\n <button type=\"button\" class=\"avaya-ipo-phonebook-back\" (click)=\"onClosePhonebook()\">\u2039</button>\n <div class=\"avaya-ipo-phonebook-titles\">\n <div class=\"avaya-ipo-phonebook-kicker\">{{ isTransferMode ? 'Transfer Call' : 'Add Call' }}</div>\n <div class=\"avaya-ipo-phonebook-title\">Directory</div>\n </div>\n <button type=\"button\" class=\"avaya-ipo-phonebook-add\" disabled>\uFF0B</button>\n </div>\n @if (transferError) {\n <div class=\"avaya-ipo-phonebook-error\">{{ transferError }}</div>\n }\n @if (transferSuccess) {\n <div class=\"avaya-ipo-phonebook-success\">{{ transferSuccess }}</div>\n }\n\n <div class=\"avaya-ipo-phonebook-tabs\">\n <button type=\"button\" class=\"avaya-ipo-phonebook-tab\" [class.avaya-ipo-active]=\"directoryTab === 'contacts'\"\n (click)=\"setDirectoryTab('contacts')\">\n Contacts\n </button>\n <button type=\"button\" class=\"avaya-ipo-phonebook-tab\" [class.avaya-ipo-active]=\"directoryTab === 'dialpad'\"\n (click)=\"setDirectoryTab('dialpad')\">\n Dialpad\n </button>\n </div>\n\n <div class=\"avaya-ipo-phonebook-list\">\n @if (directoryTab === 'contacts') {\n <div class=\"avaya-ipo-phonebook-search\">\n <span class=\"avaya-ipo-search-icon\">\uD83D\uDD0D</span>\n <input type=\"text\" placeholder=\"Search\" [value]=\"contactSearch\"\n (input)=\"contactSearch = ($any($event.target).value)\" />\n </div>\n\n @if (filteredContacts?.length) {\n @for (contact of filteredContacts; track contact.id ?? contact.phone ?? $index) {\n <button type=\"button\" class=\"avaya-ipo-phonebook-item\" (click)=\"onSelectContact(contact)\">\n <span class=\"avaya-ipo-phonebook-avatar\">{{ (contact.name || contact.phone || 'U') | slice:0:2 }}</span>\n <span class=\"avaya-ipo-phonebook-name\">{{ contact.name || 'Unknown' }}</span>\n <span class=\"avaya-ipo-phonebook-number\">{{ contact.phone || '' }}</span>\n </button>\n }\n } @else {\n <div class=\"avaya-ipo-phonebook-empty\">No contacts found</div>\n }\n } @else {\n <div class=\"avaya-ipo-phonebook-dialpad\">\n <div class=\"avaya-ipo-dialpad-input\" [formGroup]=\"directoryForm\">\n <ngx-intl-tel-input formControlName=\"phoneNumber\" name=\"phoneNumber\" [cssClass]=\"'avaya-ipo-phone_number_sd'\"\n [preferredCountries]=\"['in']\" [customPlaceholder]=\"'Phone Number'\" [enableAutoCountrySelect]=\"false\"\n [enablePlaceholder]=\"false\" [searchCountryFlag]=\"true\"\n [searchCountryField]=\"[SearchCountryField.Iso2, SearchCountryField.Name]\" [selectFirstCountry]=\"false\"\n [selectedCountryISO]=\"CountryISO.India\" [maxLength]=\"15\" [phoneValidation]=\"true\" [separateDialCode]=\"true\">\n </ngx-intl-tel-input>\n <button type=\"button\" class=\"avaya-ipo-dialpad-backspace\" (click)=\"onDirectoryBackspace()\">\u232B</button>\n </div>\n\n <div class=\"avaya-ipo-dialpad-grid\">\n @for (item of directoryDigits; track item.digit) {\n <button type=\"button\" class=\"avaya-ipo-dialpad-key\" (click)=\"onDirectoryKeypad(item.digit)\">\n <span class=\"avaya-ipo-dialpad-digit\">{{ item.digit }}</span>\n <span class=\"avaya-ipo-dialpad-letters\">{{ item.letters || '\\u00a0' }}</span>\n </button>\n }\n </div>\n\n <button type=\"button\" class=\"avaya-ipo-dialpad-action\" [disabled]=\"!(directoryForm.valid || directoryNumber)\"\n (click)=\"onDirectoryUseNumber()\">\n {{ isTransferMode ? 'Transfer' : 'Add Call' }}\n </button>\n </div>\n }\n </div>\n </div>\n</div>\n}\n\n@if (isKeypadOpen) {\n<div class=\"avaya-ipo-phonebook-overlay\">\n <div class=\"avaya-ipo-phonebook-sheet avaya-ipo-keypad-sheet\">\n <div class=\"avaya-ipo-phonebook-header\">\n <button type=\"button\" class=\"avaya-ipo-phonebook-back\" (click)=\"closeKeypad()\">\u2039</button>\n <div class=\"avaya-ipo-phonebook-titles\">\n <div class=\"avaya-ipo-phonebook-kicker\">DTMF</div>\n <div class=\"avaya-ipo-phonebook-title\">Keypad</div>\n </div>\n <button type=\"button\" class=\"avaya-ipo-phonebook-add\" disabled>\uFF0B</button>\n </div>\n\n <div class=\"avaya-ipo-dialpad-input avaya-ipo-dtmf-input\">\n <div class=\"avaya-ipo-dtmf-text\">{{ dtmfBuffer || ' ' }}</div>\n </div>\n\n <div class=\"avaya-ipo-phonebook-dialpad\">\n <div class=\"avaya-ipo-dialpad-grid\">\n @for (item of directoryDigits; track item.digit) {\n <button type=\"button\" class=\"avaya-ipo-dialpad-key\" (click)=\"onDtmfDigit(item.digit)\">\n <div class=\"avaya-ipo-dialpad-digit\">{{ item.digit }}</div>\n @if (item.letters) {\n <div class=\"avaya-ipo-dialpad-letters\">{{ item.letters }}</div>\n }\n </button>\n }\n </div>\n\n <button type=\"button\" class=\"avaya-ipo-dialpad-action\" (click)=\"closeKeypad()\">Done</button>\n </div>\n </div>\n</div>\n}\n\n@if (isTranscriptionOpen) {\n<div class=\"avaya-ipo-phonebook-overlay\">\n <div class=\"avaya-ipo-phonebook-sheet avaya-ipo-transcription-sheet\">\n <div class=\"avaya-ipo-phonebook-header\">\n <button type=\"button\" class=\"avaya-ipo-phonebook-back\" (click)=\"closeTranscription()\">\u2039</button>\n <div class=\"avaya-ipo-phonebook-titles\">\n <div class=\"avaya-ipo-phonebook-title\">Live Transcription</div>\n </div>\n </div>\n <div class=\"avaya-ipo-transcription-body\">\n @if (transcriptSegments?.length) {\n <div class=\"avaya-ipo-transcription-split\">\n <div class=\"avaya-ipo-transcription-panel\">\n <div class=\"avaya-ipo-transcription-panel-title\">\n Customer\n </div>\n <div class=\"avaya-ipo-transcription-panel-body\">\n @if (customerTranscriptSegments.length) {\n @for (segment of customerTranscriptSegments; track segment.ts ?? $index) {\n <div class=\"avaya-ipo-transcription-line\">{{ segment.text }}</div>\n }\n } @else {\n <div class=\"avaya-ipo-transcription-placeholder\">No customer speech yet.</div>\n }\n </div>\n </div>\n <div class=\"avaya-ipo-transcription-panel\">\n <div class=\"avaya-ipo-transcription-panel-title\">\n Agent\n </div>\n <div class=\"avaya-ipo-transcription-panel-body\">\n @if (agentTranscriptSegments.length) {\n @for (segment of agentTranscriptSegments; track segment.ts ?? $index) {\n <div class=\"avaya-ipo-transcription-line\">{{ segment.text }}</div>\n }\n } @else {\n <div class=\"avaya-ipo-transcription-placeholder\">No agent speech yet.</div>\n }\n </div>\n </div>\n </div>\n } @else {\n <div class=\"avaya-ipo-transcription-placeholder\">Live transcription will appear here.</div>\n }\n </div>\n </div>\n</div>\n}\n", styles: [":host{display:block;height:100%}.avaya-ipo-phone-container{width:100%;box-sizing:border-box;padding:0;background:transparent;border-radius:0;min-height:100%;height:100%;position:relative;container-type:size}.avaya-ipo-phonebook-overlay{position:absolute;inset:0;background:#00000059;display:flex;align-items:flex-end;justify-content:center;z-index:100}.avaya-ipo-phonebook-sheet{width:100%;max-width:100%;height:90%;max-height:100%;background:var(--sd-surface-muted);color:var(--sd-text);border-radius:var(--sd-radius-lg) var(--sd-radius-lg) 0 0;overflow:hidden;border:1px solid var(--sd-border);display:flex;flex-direction:column;animation:sheet-slide-up .22s ease}.avaya-ipo-keypad-sheet{height:82%}.avaya-ipo-transcription-sheet{height:90%}.avaya-ipo-transcription-body{flex:1 1 auto;padding:12px 18px 20px;overflow-y:auto}.avaya-ipo-transcription-split{display:flex;flex-direction:column;gap:12px;height:100%}.avaya-ipo-transcription-panel{flex:1 1 0;border:1px solid var(--sd-border);border-radius:var(--sd-radius);background:var(--sd-card);display:flex;flex-direction:column;min-height:0}.avaya-ipo-transcription-panel-title{font-size:12px;font-weight:700;color:var(--sd-text-muted);padding:8px 12px;border-bottom:1px solid var(--sd-border);background:var(--sd-surface-muted)}.avaya-ipo-transcription-panel-body{flex:1 1 auto;padding:10px 12px;overflow-y:auto;display:flex;flex-direction:column;gap:6px}.avaya-ipo-transcription-line{font-size:14px;color:var(--sd-text);line-height:1.5;white-space:pre-wrap}.avaya-ipo-transcription-panel .avaya-ipo-transcription-placeholder{font-size:12px;color:var(--sd-text-subtle)}.avaya-ipo-transcription-placeholder{font-size:13px;color:var(--sd-text-muted)}.avaya-ipo-phonebook-header{display:flex;align-items:center;justify-content:space-between;padding:18px 18px 8px}.avaya-ipo-phonebook-titles{text-align:center;display:flex;flex-direction:column;gap:4px;flex:1 1 auto}.avaya-ipo-phonebook-kicker{font-size:12px;color:var(--sd-text-muted)}.avaya-ipo-phonebook-title{font-size:18px;font-weight:700;color:var(--sd-text)}.avaya-ipo-phonebook-back,.avaya-ipo-phonebook-add{width:36px;height:36px;border-radius:50%;border:1px solid var(--sd-border);background:var(--sd-card);color:var(--sd-text-muted);font-size:18px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer}.avaya-ipo-phonebook-back:hover,.avaya-ipo-phonebook-add:hover{background:var(--sd-surface-muted)}.avaya-ipo-phonebook-add:disabled{opacity:.4;cursor:default}.avaya-ipo-phonebook-error{margin:0 16px 8px;padding:8px 10px;border-radius:var(--sd-radius);background:var(--sd-accent-soft);color:var(--sd-call-end);font-size:12px;font-weight:600;text-align:center}.avaya-ipo-phonebook-success{margin:0 16px 8px;padding:8px 10px;border-radius:var(--sd-radius);background:#1aa2511f;color:var(--sd-call-accept);font-size:12px;font-weight:600;text-align:center}.avaya-ipo-phonebook-tabs{margin:4px 16px 8px;display:flex;gap:8px;background:var(--sd-card);border:1px solid var(--sd-border);padding:4px;border-radius:var(--sd-radius-lg)}.avaya-ipo-phonebook-tab{flex:1 1 0;border:none;background:transparent;color:var(--sd-text-muted);font-size:13px;padding:8px 0;border-radius:var(--sd-radius);cursor:pointer}.avaya-ipo-phonebook-tab.avaya-ipo-active{background:var(--sd-accent);color:var(--sd-accent-contrast)}.avaya-ipo-phonebook-search{margin:8px 16px 14px;display:flex;align-items:center;gap:8px;padding:10px 14px;border-radius:var(--sd-radius-sm);background:var(--sd-input);border:1px solid var(--sd-input-border);color:var(--sd-text)}.avaya-ipo-phonebook-search:focus-within{border-color:var(--sd-accent);box-shadow:var(--sd-focus-ring)}.avaya-ipo-phonebook-search input{flex:1 1 auto;background:transparent;border:none;outline:none;color:var(--sd-text);font-size:14px}.avaya-ipo-phonebook-search input::placeholder{color:var(--sd-text-subtle)}.avaya-ipo-phonebook-list{flex:1 1 auto;overflow-y:auto}.avaya-ipo-phonebook-item{width:100%;text-align:left;padding:12px 18px;border:none;background:transparent;display:grid;grid-template-columns:40px 1fr;grid-template-rows:auto auto;column-gap:12px;row-gap:2px;cursor:pointer;border-bottom:1px solid var(--sd-border)}.avaya-ipo-phonebook-item:hover{background:var(--sd-surface-muted)}.avaya-ipo-phonebook-item:last-child{border-bottom:none}.avaya-ipo-phonebook-avatar{width:40px;height:40px;border-radius:50%;background:var(--sd-avatar);color:var(--sd-text-muted);display:inline-flex;align-items:center;justify-content:center;font-size:14px;font-weight:700;grid-row:span 2}.avaya-ipo-phonebook-name{font-size:13px;font-weight:600;color:var(--sd-text)}.avaya-ipo-phonebook-number{font-size:12px;color:var(--sd-text-muted)}.avaya-ipo-phonebook-empty{padding:18px;font-size:12px;color:var(--sd-text-muted)}.avaya-ipo-phonebook-dialpad{padding:10px 16px 18px;display:flex;flex-direction:column;gap:14px}.avaya-ipo-dialpad-input{display:flex;align-items:center;gap:10px;padding:10px 14px;border-radius:var(--sd-radius-sm);border:1px solid var(--sd-input-border);background:var(--sd-input)}.avaya-ipo-dialpad-input:focus-within{border-color:var(--sd-accent);box-shadow:var(--sd-focus-ring)}.avaya-ipo-dialpad-input .avaya-ipo-phone_number_sd{width:100%}.avaya-ipo-dialpad-input input{flex:1 1 auto;border:none;outline:none;font-size:16px;background:transparent;color:var(--sd-text)}.avaya-ipo-dialpad-backspace{border:none;background:transparent;font-size:16px;cursor:pointer;color:var(--sd-text-muted)}:host ::ng-deep .avaya-ipo-dialpad-input .iti{width:100%}:host ::ng-deep .avaya-ipo-dialpad-input .iti__flag-container{padding-left:6px}:host ::ng-deep .avaya-ipo-dialpad-input .iti__selected-flag{border-right:1px solid var(--sd-border);padding-right:8px}:host ::ng-deep .avaya-ipo-dialpad-input .iti.separate-dial-code input{padding-left:130px!important}:host ::ng-deep .avaya-ipo-dialpad-input .iti__tel-input{background:transparent;border:none;box-shadow:none;height:32px;padding:0 0 0 6px;font-size:15px;color:var(--sd-text)}.avaya-ipo-dialpad-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(8px,3%,12px);justify-items:center}.avaya-ipo-dialpad-key{width:clamp(52px,18%,68px);aspect-ratio:1;height:auto;border-radius:50%;border:1.6px solid var(--sd-border-strong);background:transparent;display:flex;flex-direction:column;align-items:center;justify-content:center;cursor:pointer}.avaya-ipo-dialpad-key:focus{outline:none;box-shadow:none}.avaya-ipo-dialpad-key:focus-visible{outline:none;box-shadow:var(--sd-focus-ring)}.avaya-ipo-dialpad-key:active{background:var(--sd-surface-muted)}.avaya-ipo-dialpad-digit{font-size:24px;font-weight:400;color:var(--sd-text);line-height:24px}.avaya-ipo-dialpad-letters{font-size:9px;letter-spacing:.8px;color:var(--sd-text-subtle);margin-top:2px}.avaya-ipo-dialpad-action{border:none;border-radius:var(--sd-radius);padding:12px 0;background:var(--sd-accent);color:var(--sd-accent-contrast);font-size:14px;font-weight:600;cursor:pointer}.avaya-ipo-dialpad-action:hover{background:var(--sd-accent-strong)}.avaya-ipo-dialpad-action:focus-visible{outline:none;box-shadow:var(--sd-focus-ring)}.avaya-ipo-dialpad-action:disabled{opacity:.5;cursor:default}.avaya-ipo-dtmf-input{margin:0 16px 6px}.avaya-ipo-dtmf-text{flex:1 1 auto;text-align:center;font-size:18px;letter-spacing:1px;color:var(--sd-text);min-height:24px}@keyframes sheet-slide-up{0%{transform:translateY(24px);opacity:.6}to{transform:translateY(0);opacity:1}}\n"], dependencies: [{ kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i2$1.NgxIntlTelInputComponent, selector: "ngx-intl-tel-input", inputs: ["value", "preferredCountries", "enablePlaceholder", "customPlaceholder", "numberFormat", "cssClass", "onlyCountries", "enableAutoCountrySelect", "searchCountryFlag", "searchCountryField", "searchCountryPlaceholder", "maxLength", "selectFirstCountry", "selectedCountryISO", "phoneValidation", "inputId", "separateDialCode"], outputs: ["countryChange"] }, { kind: "directive", type: i2$1.NativeElementInjectorDirective, selector: "[ngModel], [formControl], [formControlName]" }, { kind: "component", type: PhoneIdleComponent, selector: "app-phone-idle", inputs: ["dialForm", "contacts", "showDirectoryPhonebook", "isRinging", "CountryISO", "SearchCountryField"], outputs: ["toggleDirectory", "clear", "makeCall", "keypadInput", "keyboardInput"] }, { kind: "component", type: PhoneIncomingComponent, selector: "app-phone-incoming", inputs: ["incomingCallName", "incomingNameResolved", "incomingCallNumber"], outputs: ["accept", "decline"] }, { kind: "component", type: PhoneActiveComponent, selector: "app-phone-active", inputs: ["tenantId", "activeCallName", "activeCallNumber", "activeCallDuration", "callStatusText", "isConnecting", "isMuted", "isHeld"], outputs: ["endCall", "toggleMute", "toggleHold", "transfer", "openContacts", "openKeypad", "startTranscription", "contactsLoaded"] }, { kind: "pipe", type: i2.SlicePipe, name: "slice" }] });
|
|
4480
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.9", type: PhoneComponent, isStandalone: false, selector: "app-phone", inputs: { contacts: "contacts", tenantId: "tenantId", agentId: "agentId", isActiveCall: "isActiveCall", activeCallNumber: "activeCallNumber", incomingCallName: "incomingCallName", incomingNameResolved: "incomingNameResolved", activeCallName: "activeCallName", serverIp: "serverIp", serverPort: "serverPort", stunIp: "stunIp", stunPort: "stunPort", turnIp: "turnIp", turnPort: "turnPort", configurationMode: "configurationMode", recordingUploadMode: "recordingUploadMode", entityId: "entityId" }, outputs: { callScreenChange: "callScreenChange", makeCallEv: "makeCallEv", endCallEv: "endCallEv" }, viewQueries: [{ propertyName: "audioElement", first: true, predicate: ["audioElement"], descendants: true }], ngImport: i0, template: "<div class=\"avaya-ipo-phone-container\">\n <audio id=\"sd_call_ring\" #audioElement></audio>\n\n @if (isRinging) {\n <app-phone-incoming [incomingCallNumber]=\"incomingCallNumber\" [incomingCallName]=\"incomingCallName\" [incomingNameResolved]=\"incomingNameResolved\" (accept)=\"acceptAvayaCAll()\"\n (decline)=\"declineAvayaCall()\"></app-phone-incoming>\n } @else if (isActiveCall) {\n <app-phone-active [tenantId]=\"tenantId\" [activeCallNumber]=\"activeCallNumber\" [activeCallName]=\"activeCallName\"\n [activeCallDuration]=\"activeCallDuration\" [callStatusText]=\"callStatusText\" [isConnecting]=\"isConnecting\"\n [isMuted]=\"isMuted\" [isHeld]=\"isHeld\"\n (toggleMute)=\"toggleMute()\" (toggleHold)=\"toggleHold()\" (transfer)=\"onTransferCall()\"\n (openContacts)=\"onActiveOpenContacts()\" (openKeypad)=\"onActiveOpenKeypad()\"\n (startTranscription)=\"openTranscription()\" (contactsLoaded)=\"onContactsLoaded($event)\"\n (endCall)=\"onEndActiveCall()\"></app-phone-active>\n } @else {\n <app-phone-idle [dialForm]=\"dialForm\" [contacts]=\"contacts\" [showDirectoryPhonebook]=\"showDirectoryPhonebook\"\n [isRinging]=\"isRinging\" [CountryISO]=\"CountryISO\" [SearchCountryField]=\"SearchCountryField\"\n (toggleDirectory)=\"onIdleToggleDirectory()\" (clear)=\"onIdleClear()\" (makeCall)=\"onIdleMakeCall()\"\n (keypadInput)=\"onIdleKeypadInput($event)\" (keyboardInput)=\"onIdleKeyboardInput($event)\"></app-phone-idle>\n }\n</div>\n\n@if (showDirectoryPhonebook) {\n<div class=\"avaya-ipo-phonebook-overlay\">\n <div class=\"avaya-ipo-phonebook-sheet\">\n <div class=\"avaya-ipo-phonebook-header\">\n <button type=\"button\" class=\"avaya-ipo-phonebook-back\" (click)=\"onClosePhonebook()\">\u2039</button>\n <div class=\"avaya-ipo-phonebook-titles\">\n <div class=\"avaya-ipo-phonebook-kicker\">{{ isTransferMode ? 'Transfer Call' : 'Add Call' }}</div>\n <div class=\"avaya-ipo-phonebook-title\">Directory</div>\n </div>\n <button type=\"button\" class=\"avaya-ipo-phonebook-add\" disabled>\uFF0B</button>\n </div>\n @if (transferError) {\n <div class=\"avaya-ipo-phonebook-error\">{{ transferError }}</div>\n }\n @if (transferSuccess) {\n <div class=\"avaya-ipo-phonebook-success\">{{ transferSuccess }}</div>\n }\n\n <div class=\"avaya-ipo-phonebook-tabs\">\n <button type=\"button\" class=\"avaya-ipo-phonebook-tab\" [class.avaya-ipo-active]=\"directoryTab === 'contacts'\"\n (click)=\"setDirectoryTab('contacts')\">\n Contacts\n </button>\n <button type=\"button\" class=\"avaya-ipo-phonebook-tab\" [class.avaya-ipo-active]=\"directoryTab === 'dialpad'\"\n (click)=\"setDirectoryTab('dialpad')\">\n Dialpad\n </button>\n </div>\n\n <div class=\"avaya-ipo-phonebook-list\">\n @if (directoryTab === 'contacts') {\n <div class=\"avaya-ipo-phonebook-search\">\n <span class=\"avaya-ipo-search-icon\">\uD83D\uDD0D</span>\n <input type=\"text\" placeholder=\"Search\" [value]=\"contactSearch\"\n (input)=\"contactSearch = ($any($event.target).value)\" />\n </div>\n\n @if (filteredContacts?.length) {\n @for (contact of filteredContacts; track contact.id ?? contact.phone ?? $index) {\n <button type=\"button\" class=\"avaya-ipo-phonebook-item\" (click)=\"onSelectContact(contact)\">\n <span class=\"avaya-ipo-phonebook-avatar\">{{ (contact.name || contact.phone || 'U') | slice:0:2 }}</span>\n <span class=\"avaya-ipo-phonebook-name\">{{ contact.name || 'Unknown' }}</span>\n <span class=\"avaya-ipo-phonebook-number\">{{ contact.phone || '' }}</span>\n </button>\n }\n } @else {\n <div class=\"avaya-ipo-phonebook-empty\">No contacts found</div>\n }\n } @else {\n <div class=\"avaya-ipo-phonebook-dialpad\">\n <div class=\"avaya-ipo-dialpad-input\" [formGroup]=\"directoryForm\">\n <ngx-intl-tel-input formControlName=\"phoneNumber\" name=\"phoneNumber\" [cssClass]=\"'avaya-ipo-phone_number_sd'\"\n [preferredCountries]=\"['in']\" [customPlaceholder]=\"'Phone Number'\" [enableAutoCountrySelect]=\"false\"\n [enablePlaceholder]=\"false\" [searchCountryFlag]=\"true\"\n [searchCountryField]=\"[SearchCountryField.Iso2, SearchCountryField.Name]\" [selectFirstCountry]=\"false\"\n [selectedCountryISO]=\"CountryISO.India\" [maxLength]=\"15\" [phoneValidation]=\"true\" [separateDialCode]=\"true\">\n </ngx-intl-tel-input>\n <button type=\"button\" class=\"avaya-ipo-dialpad-backspace\" (click)=\"onDirectoryBackspace()\">\u232B</button>\n </div>\n\n <div class=\"avaya-ipo-dialpad-grid\">\n @for (item of directoryDigits; track item.digit) {\n <button type=\"button\" class=\"avaya-ipo-dialpad-key\" (click)=\"onDirectoryKeypad(item.digit)\">\n <span class=\"avaya-ipo-dialpad-digit\">{{ item.digit }}</span>\n <span class=\"avaya-ipo-dialpad-letters\">{{ item.letters || '\\u00a0' }}</span>\n </button>\n }\n </div>\n\n <button type=\"button\" class=\"avaya-ipo-dialpad-action\" [disabled]=\"!(directoryForm.valid || directoryNumber)\"\n (click)=\"onDirectoryUseNumber()\">\n {{ isTransferMode ? 'Transfer' : 'Add Call' }}\n </button>\n </div>\n }\n </div>\n </div>\n</div>\n}\n\n@if (isKeypadOpen) {\n<div class=\"avaya-ipo-phonebook-overlay\">\n <div class=\"avaya-ipo-phonebook-sheet avaya-ipo-keypad-sheet\">\n <div class=\"avaya-ipo-phonebook-header\">\n <button type=\"button\" class=\"avaya-ipo-phonebook-back\" (click)=\"closeKeypad()\">\u2039</button>\n <div class=\"avaya-ipo-phonebook-titles\">\n <div class=\"avaya-ipo-phonebook-kicker\">DTMF</div>\n <div class=\"avaya-ipo-phonebook-title\">Keypad</div>\n </div>\n <button type=\"button\" class=\"avaya-ipo-phonebook-add\" disabled>\uFF0B</button>\n </div>\n\n <div class=\"avaya-ipo-dialpad-input avaya-ipo-dtmf-input\">\n <div class=\"avaya-ipo-dtmf-text\">{{ dtmfBuffer || ' ' }}</div>\n </div>\n\n <div class=\"avaya-ipo-phonebook-dialpad\">\n <div class=\"avaya-ipo-dialpad-grid\">\n @for (item of directoryDigits; track item.digit) {\n <button type=\"button\" class=\"avaya-ipo-dialpad-key\" (click)=\"onDtmfDigit(item.digit)\">\n <div class=\"avaya-ipo-dialpad-digit\">{{ item.digit }}</div>\n @if (item.letters) {\n <div class=\"avaya-ipo-dialpad-letters\">{{ item.letters }}</div>\n }\n </button>\n }\n </div>\n\n <button type=\"button\" class=\"avaya-ipo-dialpad-action\" (click)=\"closeKeypad()\">Done</button>\n </div>\n </div>\n</div>\n}\n\n@if (isTranscriptionOpen) {\n<div class=\"avaya-ipo-phonebook-overlay\">\n <div class=\"avaya-ipo-phonebook-sheet avaya-ipo-transcription-sheet\">\n <div class=\"avaya-ipo-phonebook-header\">\n <button type=\"button\" class=\"avaya-ipo-phonebook-back\" (click)=\"closeTranscription()\">\u2039</button>\n <div class=\"avaya-ipo-phonebook-titles\">\n <div class=\"avaya-ipo-phonebook-title\">Live Transcription</div>\n </div>\n </div>\n <div class=\"avaya-ipo-transcription-body\">\n @if (transcriptSegments?.length) {\n <div class=\"avaya-ipo-transcription-split\">\n <div class=\"avaya-ipo-transcription-panel\">\n <div class=\"avaya-ipo-transcription-panel-title\">\n Customer\n </div>\n <div class=\"avaya-ipo-transcription-panel-body\">\n @if (customerTranscriptSegments.length) {\n @for (segment of customerTranscriptSegments; track segment.ts ?? $index) {\n <div class=\"avaya-ipo-transcription-line\">{{ segment.text }}</div>\n }\n } @else {\n <div class=\"avaya-ipo-transcription-placeholder\">No customer speech yet.</div>\n }\n </div>\n </div>\n <div class=\"avaya-ipo-transcription-panel\">\n <div class=\"avaya-ipo-transcription-panel-title\">\n Agent\n </div>\n <div class=\"avaya-ipo-transcription-panel-body\">\n @if (agentTranscriptSegments.length) {\n @for (segment of agentTranscriptSegments; track segment.ts ?? $index) {\n <div class=\"avaya-ipo-transcription-line\">{{ segment.text }}</div>\n }\n } @else {\n <div class=\"avaya-ipo-transcription-placeholder\">No agent speech yet.</div>\n }\n </div>\n </div>\n </div>\n } @else {\n <div class=\"avaya-ipo-transcription-placeholder\">Live transcription will appear here.</div>\n }\n </div>\n </div>\n</div>\n}\n", styles: [":host{display:block;height:100%}.avaya-ipo-phone-container{width:100%;box-sizing:border-box;padding:0;background:transparent;border-radius:0;min-height:100%;height:100%;position:relative;container-type:size}.avaya-ipo-phonebook-overlay{position:absolute;inset:0;background:#00000059;display:flex;align-items:flex-end;justify-content:center;z-index:100}.avaya-ipo-phonebook-sheet{width:100%;max-width:100%;height:90%;max-height:100%;background:var(--sd-surface-muted);color:var(--sd-text);border-radius:var(--sd-radius-lg) var(--sd-radius-lg) 0 0;overflow:hidden;border:1px solid var(--sd-border);display:flex;flex-direction:column;animation:sheet-slide-up .22s ease}.avaya-ipo-keypad-sheet{height:82%}.avaya-ipo-transcription-sheet{height:90%}.avaya-ipo-transcription-body{flex:1 1 auto;padding:12px 18px 20px;overflow-y:auto}.avaya-ipo-transcription-split{display:flex;flex-direction:column;gap:12px;height:100%}.avaya-ipo-transcription-panel{flex:1 1 0;border:1px solid var(--sd-border);border-radius:var(--sd-radius);background:var(--sd-card);display:flex;flex-direction:column;min-height:0}.avaya-ipo-transcription-panel-title{font-size:12px;font-weight:700;color:var(--sd-text-muted);padding:8px 12px;border-bottom:1px solid var(--sd-border);background:var(--sd-surface-muted)}.avaya-ipo-transcription-panel-body{flex:1 1 auto;padding:10px 12px;overflow-y:auto;display:flex;flex-direction:column;gap:6px}.avaya-ipo-transcription-line{font-size:14px;color:var(--sd-text);line-height:1.5;white-space:pre-wrap}.avaya-ipo-transcription-panel .avaya-ipo-transcription-placeholder{font-size:12px;color:var(--sd-text-subtle)}.avaya-ipo-transcription-placeholder{font-size:13px;color:var(--sd-text-muted)}.avaya-ipo-phonebook-header{display:flex;align-items:center;justify-content:space-between;padding:18px 18px 8px}.avaya-ipo-phonebook-titles{text-align:center;display:flex;flex-direction:column;gap:4px;flex:1 1 auto}.avaya-ipo-phonebook-kicker{font-size:12px;color:var(--sd-text-muted)}.avaya-ipo-phonebook-title{font-size:18px;font-weight:700;color:var(--sd-text)}.avaya-ipo-phonebook-back,.avaya-ipo-phonebook-add{width:36px;height:36px;border-radius:50%;border:1px solid var(--sd-border);background:var(--sd-card);color:var(--sd-text-muted);font-size:18px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer}.avaya-ipo-phonebook-back:hover,.avaya-ipo-phonebook-add:hover{background:var(--sd-surface-muted)}.avaya-ipo-phonebook-add:disabled{opacity:.4;cursor:default}.avaya-ipo-phonebook-error{margin:0 16px 8px;padding:8px 10px;border-radius:var(--sd-radius);background:var(--sd-accent-soft);color:var(--sd-call-end);font-size:12px;font-weight:600;text-align:center}.avaya-ipo-phonebook-success{margin:0 16px 8px;padding:8px 10px;border-radius:var(--sd-radius);background:#1aa2511f;color:var(--sd-call-accept);font-size:12px;font-weight:600;text-align:center}.avaya-ipo-phonebook-tabs{margin:4px 16px 8px;display:flex;gap:8px;background:var(--sd-card);border:1px solid var(--sd-border);padding:4px;border-radius:var(--sd-radius-lg)}.avaya-ipo-phonebook-tab{flex:1 1 0;border:none;background:transparent;color:var(--sd-text-muted);font-size:13px;padding:8px 0;border-radius:var(--sd-radius);cursor:pointer}.avaya-ipo-phonebook-tab.avaya-ipo-active{background:var(--sd-accent);color:var(--sd-accent-contrast)}.avaya-ipo-phonebook-search{margin:8px 16px 14px;display:flex;align-items:center;gap:8px;padding:10px 14px;border-radius:var(--sd-radius-sm);background:var(--sd-input);border:1px solid var(--sd-input-border);color:var(--sd-text)}.avaya-ipo-phonebook-search:focus-within{border-color:var(--sd-accent);box-shadow:var(--sd-focus-ring)}.avaya-ipo-phonebook-search input{flex:1 1 auto;background:transparent;border:none;outline:none;color:var(--sd-text);font-size:14px}.avaya-ipo-phonebook-search input::placeholder{color:var(--sd-text-subtle)}.avaya-ipo-phonebook-list{flex:1 1 auto;overflow-y:auto}.avaya-ipo-phonebook-item{width:100%;text-align:left;padding:12px 18px;border:none;background:transparent;display:grid;grid-template-columns:40px 1fr;grid-template-rows:auto auto;column-gap:12px;row-gap:2px;cursor:pointer;border-bottom:1px solid var(--sd-border)}.avaya-ipo-phonebook-item:hover{background:var(--sd-surface-muted)}.avaya-ipo-phonebook-item:last-child{border-bottom:none}.avaya-ipo-phonebook-avatar{width:40px;height:40px;border-radius:50%;background:var(--sd-avatar);color:var(--sd-text-muted);display:inline-flex;align-items:center;justify-content:center;font-size:14px;font-weight:700;grid-row:span 2}.avaya-ipo-phonebook-name{font-size:13px;font-weight:600;color:var(--sd-text)}.avaya-ipo-phonebook-number{font-size:12px;color:var(--sd-text-muted)}.avaya-ipo-phonebook-empty{padding:18px;font-size:12px;color:var(--sd-text-muted)}.avaya-ipo-phonebook-dialpad{padding:10px 16px 18px;display:flex;flex-direction:column;gap:14px}.avaya-ipo-dialpad-input{display:flex;align-items:center;gap:10px;padding:10px 14px;border-radius:var(--sd-radius-sm);border:1px solid var(--sd-input-border);background:var(--sd-input)}.avaya-ipo-dialpad-input:focus-within{border-color:var(--sd-accent);box-shadow:var(--sd-focus-ring)}.avaya-ipo-dialpad-input .avaya-ipo-phone_number_sd{width:100%}.avaya-ipo-dialpad-input input{flex:1 1 auto;border:none;outline:none;font-size:16px;background:transparent;color:var(--sd-text)}.avaya-ipo-dialpad-backspace{border:none;background:transparent;font-size:16px;cursor:pointer;color:var(--sd-text-muted)}:host ::ng-deep .avaya-ipo-dialpad-input .iti{width:100%}:host ::ng-deep .avaya-ipo-dialpad-input .iti__flag-container{padding-left:6px}:host ::ng-deep .avaya-ipo-dialpad-input .iti__selected-flag{border-right:1px solid var(--sd-border);padding-right:8px}:host ::ng-deep .avaya-ipo-dialpad-input .iti.separate-dial-code input{padding-left:130px!important}:host ::ng-deep .avaya-ipo-dialpad-input .iti__tel-input{background:transparent;border:none;box-shadow:none;height:32px;padding:0 0 0 6px;font-size:15px;color:var(--sd-text)}.avaya-ipo-dialpad-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(8px,3%,12px);justify-items:center}.avaya-ipo-dialpad-key{width:clamp(52px,18%,68px);aspect-ratio:1;height:auto;border-radius:50%;border:1.6px solid var(--sd-border-strong);background:transparent;display:flex;flex-direction:column;align-items:center;justify-content:center;cursor:pointer}.avaya-ipo-dialpad-key:focus{outline:none;box-shadow:none}.avaya-ipo-dialpad-key:focus-visible{outline:none;box-shadow:var(--sd-focus-ring)}.avaya-ipo-dialpad-key:active{background:var(--sd-surface-muted)}.avaya-ipo-dialpad-digit{font-size:24px;font-weight:400;color:var(--sd-text);line-height:24px}.avaya-ipo-dialpad-letters{font-size:9px;letter-spacing:.8px;color:var(--sd-text-subtle);margin-top:2px}.avaya-ipo-dialpad-action{border:none;border-radius:var(--sd-radius);padding:12px 0;background:var(--sd-accent);color:var(--sd-accent-contrast);font-size:14px;font-weight:600;cursor:pointer}.avaya-ipo-dialpad-action:hover{background:var(--sd-accent-strong)}.avaya-ipo-dialpad-action:focus-visible{outline:none;box-shadow:var(--sd-focus-ring)}.avaya-ipo-dialpad-action:disabled{opacity:.5;cursor:default}.avaya-ipo-dtmf-input{margin:0 16px 6px}.avaya-ipo-dtmf-text{flex:1 1 auto;text-align:center;font-size:18px;letter-spacing:1px;color:var(--sd-text);min-height:24px}@keyframes sheet-slide-up{0%{transform:translateY(24px);opacity:.6}to{transform:translateY(0);opacity:1}}\n"], dependencies: [{ kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i2$1.NgxIntlTelInputComponent, selector: "ngx-intl-tel-input", inputs: ["value", "preferredCountries", "enablePlaceholder", "customPlaceholder", "numberFormat", "cssClass", "onlyCountries", "enableAutoCountrySelect", "searchCountryFlag", "searchCountryField", "searchCountryPlaceholder", "maxLength", "selectFirstCountry", "selectedCountryISO", "phoneValidation", "inputId", "separateDialCode"], outputs: ["countryChange"] }, { kind: "directive", type: i2$1.NativeElementInjectorDirective, selector: "[ngModel], [formControl], [formControlName]" }, { kind: "component", type: PhoneIdleComponent, selector: "app-phone-idle", inputs: ["dialForm", "contacts", "showDirectoryPhonebook", "isRinging", "CountryISO", "SearchCountryField"], outputs: ["toggleDirectory", "clear", "makeCall", "keypadInput", "keyboardInput"] }, { kind: "component", type: PhoneIncomingComponent, selector: "app-phone-incoming", inputs: ["incomingCallName", "incomingNameResolved", "incomingCallNumber"], outputs: ["accept", "decline"] }, { kind: "component", type: PhoneActiveComponent, selector: "app-phone-active", inputs: ["tenantId", "activeCallName", "activeCallNumber", "activeCallDuration", "callStatusText", "isConnecting", "isMuted", "isHeld"], outputs: ["endCall", "toggleMute", "toggleHold", "transfer", "openContacts", "openKeypad", "startTranscription", "contactsLoaded"] }, { kind: "pipe", type: i2.SlicePipe, name: "slice" }] });
|
|
4141
4481
|
}
|
|
4142
4482
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: PhoneComponent, decorators: [{
|
|
4143
4483
|
type: Component,
|
|
@@ -4174,6 +4514,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImpor
|
|
|
4174
4514
|
type: Input
|
|
4175
4515
|
}], configurationMode: [{
|
|
4176
4516
|
type: Input
|
|
4517
|
+
}], recordingUploadMode: [{
|
|
4518
|
+
type: Input
|
|
4519
|
+
}], entityId: [{
|
|
4520
|
+
type: Input
|
|
4177
4521
|
}], makeCallEv: [{
|
|
4178
4522
|
type: Output
|
|
4179
4523
|
}], endCallEv: [{
|
|
@@ -4209,6 +4553,9 @@ class AvayaIPOWidgetComponent {
|
|
|
4209
4553
|
*/
|
|
4210
4554
|
displayMode = 'popup';
|
|
4211
4555
|
configurationMode;
|
|
4556
|
+
/** Recording upload strategy: 'complete' (one upload at call end) or 'chunked' (live-streamed
|
|
4557
|
+
* over the recording WebSocket, survives a dead tab). Defaults to APP_CONFIG.RECORDING_UPLOAD_MODE. */
|
|
4558
|
+
recordingUploadMode;
|
|
4212
4559
|
avayaIPOServerIP;
|
|
4213
4560
|
avayaIPOServerPort;
|
|
4214
4561
|
avayaIPOServerStunServerIP;
|
|
@@ -4243,7 +4590,6 @@ class AvayaIPOWidgetComponent {
|
|
|
4243
4590
|
* Payload: the call id, the caller's number, and the resolved caller name (null if not yet known).
|
|
4244
4591
|
*/
|
|
4245
4592
|
incomingCall = new EventEmitter();
|
|
4246
|
-
avayaLogoUrl = assetUrl('assets/images/logo-avaya_small_color.svg');
|
|
4247
4593
|
widgetVersion = APP_CONFIG.WIDGET_VERSION;
|
|
4248
4594
|
isReconnectingInProgress = false;
|
|
4249
4595
|
isFailoverInProgress = false;
|
|
@@ -4265,6 +4611,9 @@ class AvayaIPOWidgetComponent {
|
|
|
4265
4611
|
hideTabs = false;
|
|
4266
4612
|
darkModeEnabled = false;
|
|
4267
4613
|
activeCallNumber = null;
|
|
4614
|
+
/** Entity id of the current call's counterpart (resolved via OpenSearch); sent with the
|
|
4615
|
+
* chunked-recording socket so chunks land under <tenantId>/<entityId>/avaya-ipo-chunks/. */
|
|
4616
|
+
activeCallEntityId = null;
|
|
4268
4617
|
incomingCallName = null;
|
|
4269
4618
|
incomingNameResolved = false;
|
|
4270
4619
|
activeCallName = null;
|
|
@@ -4327,6 +4676,7 @@ class AvayaIPOWidgetComponent {
|
|
|
4327
4676
|
this.activeTab = 'keypad';
|
|
4328
4677
|
const phoneNumber = (farEndNumber ?? '').toString();
|
|
4329
4678
|
this.activeCallNumber = phoneNumber;
|
|
4679
|
+
this.activeCallEntityId = null;
|
|
4330
4680
|
this.incomingCallName = null;
|
|
4331
4681
|
this.incomingNameResolved = false;
|
|
4332
4682
|
this.activeCallName = null;
|
|
@@ -4446,6 +4796,7 @@ class AvayaIPOWidgetComponent {
|
|
|
4446
4796
|
if (this.avayaIPOService.activeCallId === callId) {
|
|
4447
4797
|
this.activeCallNumber = null;
|
|
4448
4798
|
this.activeCallName = null;
|
|
4799
|
+
this.activeCallEntityId = null;
|
|
4449
4800
|
this.incomingCallName = null;
|
|
4450
4801
|
}
|
|
4451
4802
|
}
|
|
@@ -4734,6 +5085,7 @@ class AvayaIPOWidgetComponent {
|
|
|
4734
5085
|
}
|
|
4735
5086
|
this.activeTab = 'keypad';
|
|
4736
5087
|
this.activeCallNumber = destination;
|
|
5088
|
+
this.activeCallEntityId = null;
|
|
4737
5089
|
this.activeCallName = providedName || null;
|
|
4738
5090
|
const normalizedSearch = this.normalizePhoneForSearch(destination);
|
|
4739
5091
|
if (!providedName) {
|
|
@@ -4744,9 +5096,12 @@ class AvayaIPOWidgetComponent {
|
|
|
4744
5096
|
}
|
|
4745
5097
|
this.avayaIPOService.notifyOutgoingDial(destination);
|
|
4746
5098
|
try {
|
|
5099
|
+
// Always run the entity lookup — even when the caller name is already known (contacts/history)
|
|
5100
|
+
// we still need the entity ID for the recording socket payload. The provided name keeps display
|
|
5101
|
+
// priority below.
|
|
4747
5102
|
const [lookupResult, callerName] = await Promise.all([
|
|
4748
5103
|
this.getLookupPhoneNumber(destination),
|
|
4749
|
-
|
|
5104
|
+
this.getOpenSearchCallerName(normalizedSearch),
|
|
4750
5105
|
]);
|
|
4751
5106
|
const mapNumber = lookupResult?.lookupPhoneNumber?.['local-number']?.replace(/\s+/g, '') ||
|
|
4752
5107
|
lookupResult?.normalizedPhoneNumber ||
|
|
@@ -4855,7 +5210,11 @@ class AvayaIPOWidgetComponent {
|
|
|
4855
5210
|
return null;
|
|
4856
5211
|
}
|
|
4857
5212
|
try {
|
|
4858
|
-
|
|
5213
|
+
const entity = await this.entitiesSearchService.lookupEntityByPhone(tenantId, phoneNumber);
|
|
5214
|
+
// Remember the entity for the recording socket payload (chunks live under its folder).
|
|
5215
|
+
this.activeCallEntityId = entity.id;
|
|
5216
|
+
console.debug(`[avaya-ipo] entity lookup for ${phoneNumber}: id=${entity.id || 'NONE'} name=${entity.name || 'NONE'}`);
|
|
5217
|
+
return entity.name;
|
|
4859
5218
|
}
|
|
4860
5219
|
catch (err) {
|
|
4861
5220
|
console.warn('OpenSearch lookup failed:', err);
|
|
@@ -4993,11 +5352,11 @@ class AvayaIPOWidgetComponent {
|
|
|
4993
5352
|
return this.awlScriptPromise;
|
|
4994
5353
|
}
|
|
4995
5354
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AvayaIPOWidgetComponent, deps: [{ token: i1$2.FormBuilder }, { token: AvayaIPOService }, { token: i0.ChangeDetectorRef }, { token: CountryService }, { token: PhoneNumberLookupService }, { token: EntitiesSearchService }, { token: i1$1.SnugdeskAuthenticationService }], target: i0.ɵɵFactoryTarget.Component });
|
|
4996
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.9", type: AvayaIPOWidgetComponent, isStandalone: false, selector: "snugdesk-avaya-ipo-widget", inputs: { isVisible: "isVisible", displayMode: "displayMode", configurationMode: "configurationMode", avayaIPOServerIP: "avayaIPOServerIP", avayaIPOServerPort: "avayaIPOServerPort", avayaIPOServerStunServerIP: "avayaIPOServerStunServerIP", avayaIPOServerStunServerPort: "avayaIPOServerStunServerPort", avayaIPOServerTurnServerIP: "avayaIPOServerTurnServerIP", avayaIPOServerTurnServerPort: "avayaIPOServerTurnServerPort", token: "token", tenantId: "tenantId", userId: "userId", extension: "extension", password: "password", showHistory: "showHistory", showContacts: "showContacts" }, outputs: { notificationEvent: "notificationEvent", incomingCall: "incomingCall" }, host: { properties: { "class.avaya-ipo-dark-mode": "this.hostDarkMode", "class.avaya-ipo-mode-standalone": "this.hostModeStandalone", "class.avaya-ipo-mode-popup": "this.hostModePopup" } }, usesOnChanges: true, ngImport: i0, template: "<div class=\"avaya-ipo-media-elements\">\n <audio id=\"rmtAudio\" autoplay></audio>\n <audio id=\"lclAudio\" autoplay></audio>\n <video id=\"rmtVideo\" autoplay playsinline></video>\n <video id=\"lclVideo\" autoplay playsinline muted></video>\n</div>\n\n@if (authLoading) {\n <div class=\"avaya-ipo-auth-loading-screen\">\n <div class=\"avaya-ipo-auth-loading-card\">\n <div class=\"avaya-ipo-spinner\" aria-hidden=\"true\"></div>\n <div class=\"avaya-ipo-auth-loading-title\">Checking authentication</div>\n <div class=\"avaya-ipo-auth-loading-message\">Please wait...</div>\n </div>\n </div>\n} @else if (authErrorMessage) {\n <div class=\"avaya-ipo-auth-error-screen\">\n <div class=\"avaya-ipo-auth-error-card\">\n <div class=\"avaya-ipo-auth-error-icon\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" focusable=\"false\">\n <path\n fill=\"currentColor\"\n d=\"M12 2a7 7 0 0 1 7 7v3a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-6a2 2 0 0 1 2-2V9a7 7 0 0 1 7-7zm5 10V9a5 5 0 0 0-10 0v3h10zm-5 3a2 2 0 0 0-1 3.732V20h2v-1.268A2 2 0 0 0 12 15z\"\n />\n </svg>\n </div>\n <div class=\"avaya-ipo-auth-error-title\">Authentication Required</div>\n <div class=\"avaya-ipo-auth-error-message\">{{ authErrorMessage }}</div>\n </div>\n </div>\n} @else {\n@if (isReconnectingInProgress || isFailoverInProgress || isFailbackInProgress) {\n <div class=\"avaya-ipo-container_connection_status_overlay\">\n <div class=\"avaya-ipo-container_connection_status\">\n <i class=\"fa-solid fa-circle-dot\"></i>\n @if (isReconnectingInProgress) { <span>Reconnecting to the server...</span> }\n @if (isFailoverInProgress) { <span>Failover in progress...</span> }\n @if (isFailbackInProgress) { <span>Failback in progress...</span> }\n </div>\n </div>\n}\n\n@if (showLoginForm) {\n <app-avaya-ipo-login\n [loginForm]=\"loginForm\"\n [showLoginLoader]=\"showLoginLoader\"\n [hasAvayaSocketError]=\"hasAvayaSocketError\"\n [hasAvayaLoginError]=\"hasAvayaLoginError\"\n [hasAvayaLoginFailedGeneral]=\"hasAvayaLoginFailedGeneral\"\n [hasAvayaLoginConflictError]=\"hasAvayaLoginConflictError\"\n [hasAvayaDeviceError]=\"hasAvayaDeviceError\"\n (login)=\"onLogin()\"\n ></app-avaya-ipo-login>\n} @else {\n <div class=\"avaya-ipo-widget-body\">\n <div class=\"avaya-ipo-widget-content\">\n @if (activeTab === 'history') {\n @if (!hideTabs) {\n <div class=\"avaya-ipo-page-heading avaya-ipo-heading-right\">\n <span>History</span>\n <button type=\"button\" class=\"avaya-ipo-settings-btn\" (click)=\"selectTab('settings')\">\n <svg width=\"18px\" viewBox=\"-0.03 0 16.079 16.079\" aria-hidden=\"true\" focusable=\"false\">\n <g>\n <path fill=\"currentColor\"\n d=\"M8.182 1.083a6.99 6.99 0 0 0-6.248 3.493c-1.929 3.342-.776 7.629 2.57 9.562 3.346 1.934 7.634.792 9.562-2.55 1.929-3.343.776-7.634-2.57-9.567a6.98 6.98 0 0 0-3.314-.938zM8 2.08a6 6 0 0 1 6 6 6 6 0 0 1-6 6 6 6 0 0 1-6-6 6 6 0 0 1 6-6z\" />\n <path fill=\"currentColor\"\n d=\"M9.322 0L6.69.393v1.354a6.49 6.477 43.146 0 1 2.632.005V0zM3.937 1.19L1.93 2.897l.988 1.177a6.49 6.477 43.146 0 1 2.017-1.69zm8.128.013l-.993 1.184a6.49 6.477 43.146 0 1 .17.09 6.49 6.477 43.146 0 1 1.845 1.603l1.006-1.197zM.455 5.42l-.44 2.596 1.515.267a6.49 6.477 43.146 0 1 .455-2.593zm15.086.003l-1.523.269a6.49 6.477 43.146 0 1 .464 2.59l1.533-.27zM1.858 10.118l-1.351.78 1.33 2.271 1.339-.772a6.49 6.477 43.146 0 1-1.317-2.28zm12.301.003a6.49 6.477 43.146 0 1-.534 1.215 6.49 6.477 43.146 0 1-.774 1.069l1.338.773 1.302-2.288zm-9.557 3.471l-.534 1.47 2.48.884.525-1.446a6.49 6.477 43.146 0 1-2.303-.8 6.49 6.477 43.146 0 1-.168-.108zm6.814.016a6.49 6.477 43.146 0 1-2.475.897l.53 1.457 2.468-.917z\" />\n <path fill=\"currentColor\"\n d=\"M7.648 3.093a4.989 4.989 0 0 0-3.982 2.483 5.013 5.013 0 0 0 1.836 6.834 5.002 5.002 0 0 0 6.83-1.827 5.01 5.01 0 0 0-1.836-6.832 4.976 4.976 0 0 0-2.848-.658zM8 4.08a4 4 0 0 1 4 4 4 4 0 0 1-4 4 4 4 0 0 1-4-4 4 4 0 0 1 4-4z\" />\n </g>\n </svg>\n </button>\n </div>\n }\n <app-call-history\n [tenantId]=\"tenantId\"\n [agentId]=\"loginForm?.value?.ipoExtensionNo || userId\"\n (makeCallEv)=\"onHistoryMakeCall($event)\"\n ></app-call-history>\n } @else if (activeTab === 'contacts') {\n @if (!hideTabs) {\n <div class=\"avaya-ipo-page-heading\">\n <span>Contacts</span>\n <button type=\"button\" class=\"avaya-ipo-settings-btn\" (click)=\"selectTab('settings')\">\n <svg width=\"18px\" viewBox=\"-0.03 0 16.079 16.079\" aria-hidden=\"true\" focusable=\"false\">\n <g>\n <path fill=\"currentColor\"\n d=\"M8.182 1.083a6.99 6.99 0 0 0-6.248 3.493c-1.929 3.342-.776 7.629 2.57 9.562 3.346 1.934 7.634.792 9.562-2.55 1.929-3.343.776-7.634-2.57-9.567a6.98 6.98 0 0 0-3.314-.938zM8 2.08a6 6 0 0 1 6 6 6 6 0 0 1-6 6 6 6 0 0 1-6-6 6 6 0 0 1 6-6z\" />\n <path fill=\"currentColor\"\n d=\"M9.322 0L6.69.393v1.354a6.49 6.477 43.146 0 1 2.632.005V0zM3.937 1.19L1.93 2.897l.988 1.177a6.49 6.477 43.146 0 1 2.017-1.69zm8.128.013l-.993 1.184a6.49 6.477 43.146 0 1 .17.09 6.49 6.477 43.146 0 1 1.845 1.603l1.006-1.197zM.455 5.42l-.44 2.596 1.515.267a6.49 6.477 43.146 0 1 .455-2.593zm15.086.003l-1.523.269a6.49 6.477 43.146 0 1 .464 2.59l1.533-.27zM1.858 10.118l-1.351.78 1.33 2.271 1.339-.772a6.49 6.477 43.146 0 1-1.317-2.28zm12.301.003a6.49 6.477 43.146 0 1-.534 1.215 6.49 6.477 43.146 0 1-.774 1.069l1.338.773 1.302-2.288zm-9.557 3.471l-.534 1.47 2.48.884.525-1.446a6.49 6.477 43.146 0 1-2.303-.8 6.49 6.477 43.146 0 1-.168-.108zm6.814.016a6.49 6.477 43.146 0 1-2.475.897l.53 1.457 2.468-.917z\" />\n <path fill=\"currentColor\"\n d=\"M7.648 3.093a4.989 4.989 0 0 0-3.982 2.483 5.013 5.013 0 0 0 1.836 6.834 5.002 5.002 0 0 0 6.83-1.827 5.01 5.01 0 0 0-1.836-6.832 4.976 4.976 0 0 0-2.848-.658zM8 4.08a4 4 0 0 1 4 4 4 4 0 0 1-4 4 4 4 0 0 1-4-4 4 4 0 0 1 4-4z\" />\n </g>\n </svg>\n </button>\n </div>\n }\n <app-contacts [tenantId]=\"tenantId\" (makeCallEv)=\"onContactsMakeCall($event)\"></app-contacts>\n } @else if (activeTab === 'settings') {\n @if (!hideTabs) {\n <div class=\"avaya-ipo-page-heading\">\n <span class=\"avaya-ipo-heading-with-back\">\n <button type=\"button\" class=\"avaya-ipo-settings-btn avaya-ipo-back-btn\" (click)=\"closeSettings()\" aria-label=\"Go back\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.4\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\">\n <path d=\"M15 18l-6-6 6-6\" />\n </svg>\n </button>\n <span>Settings</span>\n </span>\n </div>\n }\n <app-settings\n [extensionId]=\"loginForm?.value?.ipoExtensionNo || userId\"\n [darkMode]=\"darkModeEnabled\"\n (darkModeChange)=\"onThemeToggle($event)\"\n (change)=\"onDeviceChange($event)\"\n (logout)=\"onLogout()\"\n ></app-settings>\n } @else {\n @if (!hideTabs) {\n <button type=\"button\" class=\"avaya-ipo-settings-btn avaya-ipo-settings-btn-floating\" (click)=\"selectTab('settings')\" aria-label=\"Settings\">\n <svg width=\"18px\" viewBox=\"-0.03 0 16.079 16.079\" aria-hidden=\"true\" focusable=\"false\">\n <g>\n <path fill=\"currentColor\"\n d=\"M8.182 1.083a6.99 6.99 0 0 0-6.248 3.493c-1.929 3.342-.776 7.629 2.57 9.562 3.346 1.934 7.634.792 9.562-2.55 1.929-3.343.776-7.634-2.57-9.567a6.98 6.98 0 0 0-3.314-.938zM8 2.08a6 6 0 0 1 6 6 6 6 0 0 1-6 6 6 6 0 0 1-6-6 6 6 0 0 1 6-6z\" />\n <path fill=\"currentColor\"\n d=\"M9.322 0L6.69.393v1.354a6.49 6.477 43.146 0 1 2.632.005V0zM3.937 1.19L1.93 2.897l.988 1.177a6.49 6.477 43.146 0 1 2.017-1.69zm8.128.013l-.993 1.184a6.49 6.477 43.146 0 1 .17.09 6.49 6.477 43.146 0 1 1.845 1.603l1.006-1.197zM.455 5.42l-.44 2.596 1.515.267a6.49 6.477 43.146 0 1 .455-2.593zm15.086.003l-1.523.269a6.49 6.477 43.146 0 1 .464 2.59l1.533-.27zM1.858 10.118l-1.351.78 1.33 2.271 1.339-.772a6.49 6.477 43.146 0 1-1.317-2.28zm12.301.003a6.49 6.477 43.146 0 1-.534 1.215 6.49 6.477 43.146 0 1-.774 1.069l1.338.773 1.302-2.288zm-9.557 3.471l-.534 1.47 2.48.884.525-1.446a6.49 6.477 43.146 0 1-2.303-.8 6.49 6.477 43.146 0 1-.168-.108zm6.814.016a6.49 6.477 43.146 0 1-2.475.897l.53 1.457 2.468-.917z\" />\n <path fill=\"currentColor\"\n d=\"M7.648 3.093a4.989 4.989 0 0 0-3.982 2.483 5.013 5.013 0 0 0 1.836 6.834 5.002 5.002 0 0 0 6.83-1.827 5.01 5.01 0 0 0-1.836-6.832 4.976 4.976 0 0 0-2.848-.658zM8 4.08a4 4 0 0 1 4 4 4 4 0 0 1-4 4 4 4 0 0 1-4-4 4 4 0 0 1 4-4z\" />\n </g>\n </svg>\n </button>\n }\n <app-phone\n [tenantId]=\"tenantId\"\n [agentId]=\"loginForm?.value?.ipoExtensionNo || userId\"\n [activeCallNumber]=\"activeCallNumber\"\n [incomingCallName]=\"incomingCallName\"\n [incomingNameResolved]=\"incomingNameResolved\"\n [activeCallName]=\"activeCallName\"\n [serverIp]=\"avayaIPOServerIP\"\n [serverPort]=\"avayaIPOServerPort\"\n [stunIp]=\"avayaIPOServerStunServerIP\"\n [stunPort]=\"avayaIPOServerStunServerPort\"\n [turnIp]=\"avayaIPOServerTurnServerIP\"\n [turnPort]=\"avayaIPOServerTurnServerPort\"\n [configurationMode]=\"configurationMode\"\n (makeCallEv)=\"onPhoneMakeCall($event)\"\n (endCallEv)=\"onPhoneEndCall()\"\n (callScreenChange)=\"onCallScreenChange($event)\"\n ></app-phone>\n }\n </div>\n </div>\n\n @if (showLoginLoader) {\n <div class=\"avaya-ipo-global-loader\">\n <div class=\"avaya-ipo-spinner\"></div>\n <div class=\"avaya-ipo-loader-text\">Connecting to phone service...</div>\n </div>\n }\n\n @if (!hideTabs && showTabBar) {\n <nav class=\"avaya-ipo-tabs\">\n @if (showHistory) {\n <button type=\"button\" class=\"avaya-ipo-tab-btn\" [class.avaya-ipo-active]=\"activeTab === 'history'\" (click)=\"selectTab('history')\">\n <span class=\"avaya-ipo-tab-icon\">\n <svg width=\"19px\" viewBox=\"0 0 512 512\" aria-hidden=\"true\" focusable=\"false\">\n <path fill=\"currentColor\"\n d=\"M464 256A208 208 0 1 1 48 256a208 208 0 1 1 416 0zM0 256a256 256 0 1 0 512 0A256 256 0 1 0 0 256zM232 120l0 136c0 8 4 15.5 10.7 20l96 64c11 7.4 25.9 4.4 33.3-6.7s4.4-25.9-6.7-33.3L280 243.2 280 120c0-13.3-10.7-24-24-24s-24 10.7-24 24z\" />\n </svg>\n </span>\n <span class=\"avaya-ipo-tab-text\">History</span>\n </button>\n }\n @if (showContacts) {\n <button type=\"button\" class=\"avaya-ipo-tab-btn\" [class.avaya-ipo-active]=\"activeTab === 'contacts'\" (click)=\"selectTab('contacts')\">\n <span class=\"avaya-ipo-tab-icon\">\n <svg width=\"18px\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path fill=\"currentColor\"\n d=\"M12 12a4 4 0 1 0-4-4 4 4 0 0 0 4 4zm0 2c-4.4 0-8 2.2-8 5v2h16v-2c0-2.8-3.6-5-8-5z\" />\n </svg>\n </span>\n <span class=\"avaya-ipo-tab-text\">Contacts</span>\n </button>\n }\n <button type=\"button\" class=\"avaya-ipo-tab-btn\" [class.avaya-ipo-active]=\"activeTab === 'keypad'\" (click)=\"selectTab('keypad')\">\n <span class=\"avaya-ipo-tab-icon\">\n <svg width=\"20px\" viewBox=\"0 0 512 512\" aria-hidden=\"true\" focusable=\"false\">\n <path fill=\"currentColor\" d=\"M256,400a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M256,272a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M256,144a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M256,16a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M384,272a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M384,144a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M384,16a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M128,272a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M128,144a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M128,16a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n </svg>\n </span>\n <span class=\"avaya-ipo-tab-text\">Keypad</span>\n </button>\n </nav>\n }\n\n @if (activeTab !== 'keypad') {\n <div class=\"avaya-ipo-widget-version-footer\">v{{ widgetVersion }}</div>\n }\n}\n}\n", styles: [":host{display:flex;flex-direction:column;width:100%;max-width:100%;margin:0;font-family:Open Sans,Helvetica Neue,Arial,sans-serif;min-height:100%;height:100%;background:var(--sd-surface);color:var(--sd-text);box-sizing:border-box;--sd-tabs-height: 60px;--sd-radius-sm: 8px;--sd-radius: 10px;--sd-radius-lg: 14px;--sd-radius-pill: 999px;--sd-shadow-sm: 0 1px 2px rgba(16, 24, 40, .06);--sd-shadow-md: 0 6px 16px rgba(16, 24, 40, .1);--sd-shadow-lg: 0 16px 40px rgba(16, 24, 40, .14);--sd-accent: var(--app-color, #ff6633);--sd-accent-strong: var(--app-color-dark, #ff5805);--sd-accent-soft: #fff1ea;--sd-accent-contrast: #ffffff;--sd-call-accept: #1aa251;--sd-call-end: #e5484d;--sd-surface: var(--background-color, #ffffff);--sd-surface-muted: var(--background-color-dark, #f7f7f7);--sd-surface-sunken: var(--background-color-dark-light, #e8e8e8);--sd-text: var(--header-text-primary, #333333);--sd-text-muted: var(--header-text-secondary, #5f6368);--sd-text-subtle: var(--header-text-muted, #9ca3af);--sd-border: var(--dropdown-border, #e3e3e3);--sd-border-strong: var(--border-color, #c9c9c9);--sd-card: var(--background-color, #ffffff);--sd-avatar: #eceff3;--sd-input: var(--background-color, #ffffff);--sd-input-border: var(--border-color, #c9c9c9);--sd-focus-ring: 0 0 0 3px rgba(255, 102, 51, .22)}:host.avaya-ipo-dark-mode{--sd-accent: #ff7a4d;--sd-accent-strong: #ff6633;--sd-accent-soft: rgba(255, 122, 77, .14);--sd-accent-contrast: #1a1109;--sd-call-accept: #34c759;--sd-call-end: #ff5b5b;--sd-surface: #0f1115;--sd-surface-muted: #151a22;--sd-surface-sunken: #1c222c;--sd-text: #f2f4f8;--sd-text-muted: #aab0bb;--sd-text-subtle: #7e8794;--sd-border: #2a2f3a;--sd-border-strong: #353b47;--sd-card: #161b24;--sd-avatar: #222833;--sd-input: #1c222c;--sd-input-border: #2a2f3a;--sd-focus-ring: 0 0 0 3px rgba(255, 122, 77, .25)}:host.avaya-ipo-mode-popup{min-width:320px;height:100%}:host.avaya-ipo-mode-standalone{width:100%;max-width:460px;height:100dvh;min-height:100dvh;margin-inline:auto;border-inline:1px solid var(--sd-border);box-shadow:var(--sd-shadow-lg);overflow:hidden}@media (max-width: 768px){:host.avaya-ipo-mode-standalone{max-width:100%;border-inline:0;box-shadow:none}}.avaya-ipo-container_connection_status_overlay{width:100%;position:relative;background-color:#7f7f7f1a;padding:10px;box-sizing:border-box}.avaya-ipo-container_connection_status{width:100%;background:var(--sd-card);padding:8px 10px;border-radius:var(--sd-radius-sm);font-size:13px;font-weight:600;color:var(--sd-text-muted);display:flex;gap:8px;align-items:center;box-shadow:var(--sd-shadow-md)}.avaya-ipo-container_connection_status .fa-circle-dot{color:var(--sd-accent)}.avaya-ipo-simple-placeholder{padding:20px;text-align:center;color:var(--sd-text);font-weight:600}.avaya-ipo-auth-error-screen,.avaya-ipo-auth-loading-screen{min-height:100%;flex:1 1 auto;display:flex;align-items:center;justify-content:center;padding:24px;box-sizing:border-box;background:var(--sd-surface)}.avaya-ipo-auth-loading-card{width:min(320px,100%);padding:24px;border-radius:var(--sd-radius-lg);background:var(--sd-card);border:1px solid var(--sd-border);box-shadow:var(--sd-shadow-lg);display:flex;flex-direction:column;gap:10px;text-align:center;align-items:center}.avaya-ipo-auth-loading-title{font-size:16px;font-weight:800;color:var(--sd-text)}.avaya-ipo-auth-loading-message{font-size:12px;color:var(--sd-text-muted)}.avaya-ipo-auth-error-card{width:min(360px,100%);padding:24px;border-radius:var(--sd-radius-lg);background:var(--sd-card);border:1px solid var(--sd-border);box-shadow:var(--sd-shadow-lg);display:flex;flex-direction:column;gap:10px;text-align:center}.avaya-ipo-auth-error-icon{width:56px;height:56px;margin:0 auto 4px;display:inline-flex;align-items:center;justify-content:center;border-radius:var(--sd-radius-lg);background:var(--sd-accent);color:var(--sd-accent-contrast)}.avaya-ipo-auth-error-icon svg{width:28px;height:28px}.avaya-ipo-auth-error-title{font-size:18px;font-weight:800;color:var(--sd-text)}.avaya-ipo-auth-error-message{font-size:13px;color:var(--sd-text-muted)}.avaya-ipo-auth-error-hint{font-size:12px;color:var(--sd-text-subtle)}.avaya-ipo-media-elements{display:none}.avaya-ipo-widget-body{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;position:relative;height:100%;padding-bottom:0;overflow-y:auto;-webkit-overflow-scrolling:touch}.avaya-ipo-logout-btn{border:none;background:var(--sd-call-end);color:#fff;padding:8px 12px;border-radius:var(--sd-radius-sm);cursor:pointer;font-weight:600}.avaya-ipo-logout-btn:hover{filter:brightness(.94)}.avaya-ipo-widget-content{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;gap:1rem;padding-bottom:0;height:100%}.avaya-ipo-page-heading{display:flex;align-items:center;justify-content:space-between;font-size:22px;font-weight:800;color:var(--sd-text);padding:15px;z-index:5;background:var(--sd-surface)}.avaya-ipo-settings-btn{width:34px;height:34px;border-radius:var(--sd-radius-sm);border:none;background:transparent;color:var(--sd-text-muted);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;transition:background .12s ease,color .12s ease}.avaya-ipo-settings-btn:hover{background:var(--sd-surface-muted)}.avaya-ipo-settings-btn.avaya-ipo-is-active{color:var(--sd-accent);background:var(--sd-accent-soft)}.avaya-ipo-settings-btn:disabled{cursor:default;opacity:.75}.avaya-ipo-settings-btn-floating{position:absolute;top:8px;right:8px;z-index:6}.avaya-ipo-heading-with-back{display:inline-flex;align-items:center;gap:4px}.avaya-ipo-settings-btn.avaya-ipo-back-btn{color:var(--sd-text)}.avaya-ipo-tabs{position:sticky;bottom:0;left:0;right:0;display:flex;gap:6px;padding:8px 12px;background:var(--sd-surface-muted);border-top:1px solid var(--sd-border);box-sizing:border-box;z-index:10;height:var(--sd-tabs-height)}.avaya-ipo-tab-btn{flex:1 1 0;height:44px;border:none;background:transparent;font-weight:600;color:var(--sd-text-muted);border-radius:var(--sd-radius-sm);cursor:pointer;transition:background .12s ease,color .12s ease;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;font-size:12px;box-shadow:none}.avaya-ipo-tab-btn:hover{color:var(--sd-text)}.avaya-ipo-tab-btn.avaya-ipo-active{background:var(--sd-accent-soft);color:var(--sd-accent);box-shadow:none}.avaya-ipo-tab-icon{display:flex;align-items:center;justify-content:center;font-size:16px}.avaya-ipo-tab-btn.avaya-ipo-active .avaya-ipo-tab-icon{color:var(--sd-accent)}.avaya-ipo-global-loader{position:absolute;inset:0;background:color-mix(in srgb,var(--sd-surface) 85%,transparent);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;z-index:1000;color:var(--sd-text);font-weight:700}.avaya-ipo-spinner{width:48px;height:48px;border:4px solid var(--sd-surface-sunken);border-top-color:var(--sd-accent);border-radius:50%;animation:spin .9s linear infinite}.avaya-ipo-loader-text{font-size:14px}@keyframes spin{to{transform:rotate(360deg)}}:host ::ng-deep .avaya-ipo-widget-body,:host ::ng-deep .avaya-ipo-widget-content{background:var(--sd-surface);color:var(--sd-text)}:host ::ng-deep .avaya-ipo-page-heading{color:var(--sd-text)}:host ::ng-deep .avaya-ipo-settings-btn{color:var(--sd-text-muted)}:host ::ng-deep .avaya-ipo-tabs{background:var(--sd-surface-muted);border-top-color:var(--sd-border)}:host ::ng-deep .avaya-ipo-tab-btn{color:var(--sd-text-muted)}:host ::ng-deep .avaya-ipo-tab-btn.avaya-ipo-active{color:var(--sd-accent);background:var(--sd-accent-soft)}:host ::ng-deep .avaya-ipo-tab-btn.avaya-ipo-active .avaya-ipo-tab-icon{color:var(--sd-accent)}:host ::ng-deep .avaya-ipo-history_item,:host ::ng-deep .avaya-ipo-contact-card{border-bottom-color:var(--sd-border)}:host ::ng-deep .avaya-ipo-history_avatar,:host ::ng-deep .avaya-ipo-skeleton_avatar,:host ::ng-deep .avaya-ipo-contact-avatar{background:var(--sd-avatar);color:var(--sd-text)}:host ::ng-deep .avaya-ipo-history_name,:host ::ng-deep .avaya-ipo-contact-name{color:var(--sd-text)}:host ::ng-deep .avaya-ipo-history_time,:host ::ng-deep .avaya-ipo-history_meta,:host ::ng-deep .avaya-ipo-history_group_label,:host ::ng-deep .avaya-ipo-contact-phone,:host ::ng-deep .avaya-ipo-contacts-state{color:var(--sd-text-muted)}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-history_group_label{background:#151a22eb}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-history_item:hover{background:#1a202b}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-contacts-search input{background:var(--sd-input);border-color:var(--sd-input-border);color:var(--sd-text)}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-contacts-search input::placeholder{color:var(--sd-text-muted)}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-contact-call{background:#1e2b22;color:#79d48a}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-contact-back{background:#141922;border-color:var(--sd-border);color:var(--sd-text)}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-contact-call-btn{background:#1f2530;border-color:#1f2530;color:#79d48a}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-global-loader{background:#0f1115e6;color:var(--sd-text)}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-spinner{border-color:#2a2f3a;border-top-color:var(--sd-accent)}.avaya-ipo-widget-version-footer{flex:0 0 auto;padding:2px 8px 4px;text-align:center;font-size:10px;line-height:1.2;letter-spacing:.02em;color:var(--sd-text-muted);background:var(--sd-surface);border-top:1px solid var(--sd-border);-webkit-user-select:text;user-select:text}\n"], dependencies: [{ kind: "component", type: CallHistoryComponent, selector: "app-call-history", inputs: ["mode", "tenantId", "agentId"], outputs: ["makeCallEv", "transferEv"] }, { kind: "component", type: ContactsComponent, selector: "app-contacts", inputs: ["tenantId"], outputs: ["makeCallEv"] }, { kind: "component", type: SettingsComponent, selector: "app-settings", inputs: ["selectedAudioInputId", "selectedAudioOutputId", "extensionId", "darkMode"], outputs: ["change", "darkModeChange", "logout"] }, { kind: "component", type: AvayaIpoLoginComponent, selector: "app-avaya-ipo-login", inputs: ["loginForm", "showLoginLoader", "hasAvayaSocketError", "hasAvayaLoginError", "hasAvayaLoginFailedGeneral", "hasAvayaLoginConflictError", "hasAvayaDeviceError"], outputs: ["login"] }, { kind: "component", type: PhoneComponent, selector: "app-phone", inputs: ["contacts", "tenantId", "agentId", "isActiveCall", "activeCallNumber", "incomingCallName", "incomingNameResolved", "activeCallName", "serverIp", "serverPort", "stunIp", "stunPort", "turnIp", "turnPort", "configurationMode"], outputs: ["callScreenChange", "makeCallEv", "endCallEv"] }] });
|
|
5355
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.9", type: AvayaIPOWidgetComponent, isStandalone: false, selector: "snugdesk-avaya-ipo-widget", inputs: { isVisible: "isVisible", displayMode: "displayMode", configurationMode: "configurationMode", recordingUploadMode: "recordingUploadMode", avayaIPOServerIP: "avayaIPOServerIP", avayaIPOServerPort: "avayaIPOServerPort", avayaIPOServerStunServerIP: "avayaIPOServerStunServerIP", avayaIPOServerStunServerPort: "avayaIPOServerStunServerPort", avayaIPOServerTurnServerIP: "avayaIPOServerTurnServerIP", avayaIPOServerTurnServerPort: "avayaIPOServerTurnServerPort", token: "token", tenantId: "tenantId", userId: "userId", extension: "extension", password: "password", showHistory: "showHistory", showContacts: "showContacts" }, outputs: { notificationEvent: "notificationEvent", incomingCall: "incomingCall" }, host: { properties: { "class.avaya-ipo-dark-mode": "this.hostDarkMode", "class.avaya-ipo-mode-standalone": "this.hostModeStandalone", "class.avaya-ipo-mode-popup": "this.hostModePopup" } }, usesOnChanges: true, ngImport: i0, template: "<div class=\"avaya-ipo-media-elements\">\n <audio id=\"rmtAudio\" autoplay></audio>\n <audio id=\"lclAudio\" autoplay></audio>\n <video id=\"rmtVideo\" autoplay playsinline></video>\n <video id=\"lclVideo\" autoplay playsinline muted></video>\n</div>\n\n@if (authLoading) {\n <div class=\"avaya-ipo-auth-loading-screen\">\n <div class=\"avaya-ipo-auth-loading-card\">\n <div class=\"avaya-ipo-spinner\" aria-hidden=\"true\"></div>\n <div class=\"avaya-ipo-auth-loading-title\">Checking authentication</div>\n <div class=\"avaya-ipo-auth-loading-message\">Please wait...</div>\n </div>\n </div>\n} @else if (authErrorMessage) {\n <div class=\"avaya-ipo-auth-error-screen\">\n <div class=\"avaya-ipo-auth-error-card\">\n <div class=\"avaya-ipo-auth-error-icon\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" focusable=\"false\">\n <path\n fill=\"currentColor\"\n d=\"M12 2a7 7 0 0 1 7 7v3a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-6a2 2 0 0 1 2-2V9a7 7 0 0 1 7-7zm5 10V9a5 5 0 0 0-10 0v3h10zm-5 3a2 2 0 0 0-1 3.732V20h2v-1.268A2 2 0 0 0 12 15z\"\n />\n </svg>\n </div>\n <div class=\"avaya-ipo-auth-error-title\">Authentication Required</div>\n <div class=\"avaya-ipo-auth-error-message\">{{ authErrorMessage }}</div>\n </div>\n </div>\n} @else {\n@if (isReconnectingInProgress || isFailoverInProgress || isFailbackInProgress) {\n <div class=\"avaya-ipo-container_connection_status_overlay\">\n <div class=\"avaya-ipo-container_connection_status\">\n <i class=\"fa-solid fa-circle-dot\"></i>\n @if (isReconnectingInProgress) { <span>Reconnecting to the server...</span> }\n @if (isFailoverInProgress) { <span>Failover in progress...</span> }\n @if (isFailbackInProgress) { <span>Failback in progress...</span> }\n </div>\n </div>\n}\n\n@if (showLoginForm) {\n <app-avaya-ipo-login\n [loginForm]=\"loginForm\"\n [showLoginLoader]=\"showLoginLoader\"\n [hasAvayaSocketError]=\"hasAvayaSocketError\"\n [hasAvayaLoginError]=\"hasAvayaLoginError\"\n [hasAvayaLoginFailedGeneral]=\"hasAvayaLoginFailedGeneral\"\n [hasAvayaLoginConflictError]=\"hasAvayaLoginConflictError\"\n [hasAvayaDeviceError]=\"hasAvayaDeviceError\"\n (login)=\"onLogin()\"\n ></app-avaya-ipo-login>\n} @else {\n <div class=\"avaya-ipo-widget-body\">\n <div class=\"avaya-ipo-widget-content\">\n @if (activeTab === 'history') {\n @if (!hideTabs) {\n <div class=\"avaya-ipo-page-heading avaya-ipo-heading-right\">\n <span>History</span>\n <button type=\"button\" class=\"avaya-ipo-settings-btn\" (click)=\"selectTab('settings')\">\n <svg width=\"18px\" viewBox=\"-0.03 0 16.079 16.079\" aria-hidden=\"true\" focusable=\"false\">\n <g>\n <path fill=\"currentColor\"\n d=\"M8.182 1.083a6.99 6.99 0 0 0-6.248 3.493c-1.929 3.342-.776 7.629 2.57 9.562 3.346 1.934 7.634.792 9.562-2.55 1.929-3.343.776-7.634-2.57-9.567a6.98 6.98 0 0 0-3.314-.938zM8 2.08a6 6 0 0 1 6 6 6 6 0 0 1-6 6 6 6 0 0 1-6-6 6 6 0 0 1 6-6z\" />\n <path fill=\"currentColor\"\n d=\"M9.322 0L6.69.393v1.354a6.49 6.477 43.146 0 1 2.632.005V0zM3.937 1.19L1.93 2.897l.988 1.177a6.49 6.477 43.146 0 1 2.017-1.69zm8.128.013l-.993 1.184a6.49 6.477 43.146 0 1 .17.09 6.49 6.477 43.146 0 1 1.845 1.603l1.006-1.197zM.455 5.42l-.44 2.596 1.515.267a6.49 6.477 43.146 0 1 .455-2.593zm15.086.003l-1.523.269a6.49 6.477 43.146 0 1 .464 2.59l1.533-.27zM1.858 10.118l-1.351.78 1.33 2.271 1.339-.772a6.49 6.477 43.146 0 1-1.317-2.28zm12.301.003a6.49 6.477 43.146 0 1-.534 1.215 6.49 6.477 43.146 0 1-.774 1.069l1.338.773 1.302-2.288zm-9.557 3.471l-.534 1.47 2.48.884.525-1.446a6.49 6.477 43.146 0 1-2.303-.8 6.49 6.477 43.146 0 1-.168-.108zm6.814.016a6.49 6.477 43.146 0 1-2.475.897l.53 1.457 2.468-.917z\" />\n <path fill=\"currentColor\"\n d=\"M7.648 3.093a4.989 4.989 0 0 0-3.982 2.483 5.013 5.013 0 0 0 1.836 6.834 5.002 5.002 0 0 0 6.83-1.827 5.01 5.01 0 0 0-1.836-6.832 4.976 4.976 0 0 0-2.848-.658zM8 4.08a4 4 0 0 1 4 4 4 4 0 0 1-4 4 4 4 0 0 1-4-4 4 4 0 0 1 4-4z\" />\n </g>\n </svg>\n </button>\n </div>\n }\n <app-call-history\n [tenantId]=\"tenantId\"\n [agentId]=\"loginForm?.value?.ipoExtensionNo || userId\"\n (makeCallEv)=\"onHistoryMakeCall($event)\"\n ></app-call-history>\n } @else if (activeTab === 'contacts') {\n @if (!hideTabs) {\n <div class=\"avaya-ipo-page-heading\">\n <span>Contacts</span>\n <button type=\"button\" class=\"avaya-ipo-settings-btn\" (click)=\"selectTab('settings')\">\n <svg width=\"18px\" viewBox=\"-0.03 0 16.079 16.079\" aria-hidden=\"true\" focusable=\"false\">\n <g>\n <path fill=\"currentColor\"\n d=\"M8.182 1.083a6.99 6.99 0 0 0-6.248 3.493c-1.929 3.342-.776 7.629 2.57 9.562 3.346 1.934 7.634.792 9.562-2.55 1.929-3.343.776-7.634-2.57-9.567a6.98 6.98 0 0 0-3.314-.938zM8 2.08a6 6 0 0 1 6 6 6 6 0 0 1-6 6 6 6 0 0 1-6-6 6 6 0 0 1 6-6z\" />\n <path fill=\"currentColor\"\n d=\"M9.322 0L6.69.393v1.354a6.49 6.477 43.146 0 1 2.632.005V0zM3.937 1.19L1.93 2.897l.988 1.177a6.49 6.477 43.146 0 1 2.017-1.69zm8.128.013l-.993 1.184a6.49 6.477 43.146 0 1 .17.09 6.49 6.477 43.146 0 1 1.845 1.603l1.006-1.197zM.455 5.42l-.44 2.596 1.515.267a6.49 6.477 43.146 0 1 .455-2.593zm15.086.003l-1.523.269a6.49 6.477 43.146 0 1 .464 2.59l1.533-.27zM1.858 10.118l-1.351.78 1.33 2.271 1.339-.772a6.49 6.477 43.146 0 1-1.317-2.28zm12.301.003a6.49 6.477 43.146 0 1-.534 1.215 6.49 6.477 43.146 0 1-.774 1.069l1.338.773 1.302-2.288zm-9.557 3.471l-.534 1.47 2.48.884.525-1.446a6.49 6.477 43.146 0 1-2.303-.8 6.49 6.477 43.146 0 1-.168-.108zm6.814.016a6.49 6.477 43.146 0 1-2.475.897l.53 1.457 2.468-.917z\" />\n <path fill=\"currentColor\"\n d=\"M7.648 3.093a4.989 4.989 0 0 0-3.982 2.483 5.013 5.013 0 0 0 1.836 6.834 5.002 5.002 0 0 0 6.83-1.827 5.01 5.01 0 0 0-1.836-6.832 4.976 4.976 0 0 0-2.848-.658zM8 4.08a4 4 0 0 1 4 4 4 4 0 0 1-4 4 4 4 0 0 1-4-4 4 4 0 0 1 4-4z\" />\n </g>\n </svg>\n </button>\n </div>\n }\n <app-contacts [tenantId]=\"tenantId\" (makeCallEv)=\"onContactsMakeCall($event)\"></app-contacts>\n } @else if (activeTab === 'settings') {\n @if (!hideTabs) {\n <div class=\"avaya-ipo-page-heading\">\n <span class=\"avaya-ipo-heading-with-back\">\n <button type=\"button\" class=\"avaya-ipo-settings-btn avaya-ipo-back-btn\" (click)=\"closeSettings()\" aria-label=\"Go back\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.4\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\">\n <path d=\"M15 18l-6-6 6-6\" />\n </svg>\n </button>\n <span>Settings</span>\n </span>\n </div>\n }\n <app-settings\n [extensionId]=\"loginForm?.value?.ipoExtensionNo || userId\"\n [darkMode]=\"darkModeEnabled\"\n (darkModeChange)=\"onThemeToggle($event)\"\n (change)=\"onDeviceChange($event)\"\n (logout)=\"onLogout()\"\n ></app-settings>\n } @else {\n @if (!hideTabs) {\n <button type=\"button\" class=\"avaya-ipo-settings-btn avaya-ipo-settings-btn-floating\" (click)=\"selectTab('settings')\" aria-label=\"Settings\">\n <svg width=\"18px\" viewBox=\"-0.03 0 16.079 16.079\" aria-hidden=\"true\" focusable=\"false\">\n <g>\n <path fill=\"currentColor\"\n d=\"M8.182 1.083a6.99 6.99 0 0 0-6.248 3.493c-1.929 3.342-.776 7.629 2.57 9.562 3.346 1.934 7.634.792 9.562-2.55 1.929-3.343.776-7.634-2.57-9.567a6.98 6.98 0 0 0-3.314-.938zM8 2.08a6 6 0 0 1 6 6 6 6 0 0 1-6 6 6 6 0 0 1-6-6 6 6 0 0 1 6-6z\" />\n <path fill=\"currentColor\"\n d=\"M9.322 0L6.69.393v1.354a6.49 6.477 43.146 0 1 2.632.005V0zM3.937 1.19L1.93 2.897l.988 1.177a6.49 6.477 43.146 0 1 2.017-1.69zm8.128.013l-.993 1.184a6.49 6.477 43.146 0 1 .17.09 6.49 6.477 43.146 0 1 1.845 1.603l1.006-1.197zM.455 5.42l-.44 2.596 1.515.267a6.49 6.477 43.146 0 1 .455-2.593zm15.086.003l-1.523.269a6.49 6.477 43.146 0 1 .464 2.59l1.533-.27zM1.858 10.118l-1.351.78 1.33 2.271 1.339-.772a6.49 6.477 43.146 0 1-1.317-2.28zm12.301.003a6.49 6.477 43.146 0 1-.534 1.215 6.49 6.477 43.146 0 1-.774 1.069l1.338.773 1.302-2.288zm-9.557 3.471l-.534 1.47 2.48.884.525-1.446a6.49 6.477 43.146 0 1-2.303-.8 6.49 6.477 43.146 0 1-.168-.108zm6.814.016a6.49 6.477 43.146 0 1-2.475.897l.53 1.457 2.468-.917z\" />\n <path fill=\"currentColor\"\n d=\"M7.648 3.093a4.989 4.989 0 0 0-3.982 2.483 5.013 5.013 0 0 0 1.836 6.834 5.002 5.002 0 0 0 6.83-1.827 5.01 5.01 0 0 0-1.836-6.832 4.976 4.976 0 0 0-2.848-.658zM8 4.08a4 4 0 0 1 4 4 4 4 0 0 1-4 4 4 4 0 0 1-4-4 4 4 0 0 1 4-4z\" />\n </g>\n </svg>\n </button>\n }\n <app-phone\n [tenantId]=\"tenantId\"\n [agentId]=\"loginForm?.value?.ipoExtensionNo || userId\"\n [activeCallNumber]=\"activeCallNumber\"\n [incomingCallName]=\"incomingCallName\"\n [incomingNameResolved]=\"incomingNameResolved\"\n [activeCallName]=\"activeCallName\"\n [serverIp]=\"avayaIPOServerIP\"\n [serverPort]=\"avayaIPOServerPort\"\n [stunIp]=\"avayaIPOServerStunServerIP\"\n [stunPort]=\"avayaIPOServerStunServerPort\"\n [turnIp]=\"avayaIPOServerTurnServerIP\"\n [turnPort]=\"avayaIPOServerTurnServerPort\"\n [configurationMode]=\"configurationMode\"\n [recordingUploadMode]=\"recordingUploadMode\"\n [entityId]=\"activeCallEntityId\"\n (makeCallEv)=\"onPhoneMakeCall($event)\"\n (endCallEv)=\"onPhoneEndCall()\"\n (callScreenChange)=\"onCallScreenChange($event)\"\n ></app-phone>\n }\n </div>\n </div>\n\n @if (showLoginLoader) {\n <div class=\"avaya-ipo-global-loader\">\n <div class=\"avaya-ipo-spinner\"></div>\n <div class=\"avaya-ipo-loader-text\">Connecting to phone service...</div>\n </div>\n }\n\n @if (!hideTabs && showTabBar) {\n <nav class=\"avaya-ipo-tabs\">\n @if (showHistory) {\n <button type=\"button\" class=\"avaya-ipo-tab-btn\" [class.avaya-ipo-active]=\"activeTab === 'history'\" (click)=\"selectTab('history')\">\n <span class=\"avaya-ipo-tab-icon\">\n <svg width=\"19px\" viewBox=\"0 0 512 512\" aria-hidden=\"true\" focusable=\"false\">\n <path fill=\"currentColor\"\n d=\"M464 256A208 208 0 1 1 48 256a208 208 0 1 1 416 0zM0 256a256 256 0 1 0 512 0A256 256 0 1 0 0 256zM232 120l0 136c0 8 4 15.5 10.7 20l96 64c11 7.4 25.9 4.4 33.3-6.7s4.4-25.9-6.7-33.3L280 243.2 280 120c0-13.3-10.7-24-24-24s-24 10.7-24 24z\" />\n </svg>\n </span>\n <span class=\"avaya-ipo-tab-text\">History</span>\n </button>\n }\n @if (showContacts) {\n <button type=\"button\" class=\"avaya-ipo-tab-btn\" [class.avaya-ipo-active]=\"activeTab === 'contacts'\" (click)=\"selectTab('contacts')\">\n <span class=\"avaya-ipo-tab-icon\">\n <svg width=\"18px\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path fill=\"currentColor\"\n d=\"M12 12a4 4 0 1 0-4-4 4 4 0 0 0 4 4zm0 2c-4.4 0-8 2.2-8 5v2h16v-2c0-2.8-3.6-5-8-5z\" />\n </svg>\n </span>\n <span class=\"avaya-ipo-tab-text\">Contacts</span>\n </button>\n }\n <button type=\"button\" class=\"avaya-ipo-tab-btn\" [class.avaya-ipo-active]=\"activeTab === 'keypad'\" (click)=\"selectTab('keypad')\">\n <span class=\"avaya-ipo-tab-icon\">\n <svg width=\"20px\" viewBox=\"0 0 512 512\" aria-hidden=\"true\" focusable=\"false\">\n <path fill=\"currentColor\" d=\"M256,400a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M256,272a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M256,144a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M256,16a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M384,272a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M384,144a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M384,16a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M128,272a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M128,144a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M128,16a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n </svg>\n </span>\n <span class=\"avaya-ipo-tab-text\">Keypad</span>\n </button>\n </nav>\n }\n\n @if (activeTab !== 'keypad') {\n <div class=\"avaya-ipo-widget-version-footer\">v{{ widgetVersion }}</div>\n }\n}\n}\n", styles: [":host{display:flex;flex-direction:column;width:100%;max-width:100%;margin:0;font-family:Open Sans,Helvetica Neue,Arial,sans-serif;min-height:100%;height:100%;background:var(--sd-surface);color:var(--sd-text);box-sizing:border-box;--sd-tabs-height: 60px;--sd-radius-sm: 8px;--sd-radius: 10px;--sd-radius-lg: 14px;--sd-radius-pill: 999px;--sd-shadow-sm: 0 1px 2px rgba(16, 24, 40, .06);--sd-shadow-md: 0 6px 16px rgba(16, 24, 40, .1);--sd-shadow-lg: 0 16px 40px rgba(16, 24, 40, .14);--sd-accent: var(--app-color, #ff6633);--sd-accent-strong: var(--app-color-dark, #ff5805);--sd-accent-soft: #fff1ea;--sd-accent-contrast: #ffffff;--sd-call-accept: #1aa251;--sd-call-end: #e5484d;--sd-surface: var(--background-color, #ffffff);--sd-surface-muted: var(--background-color-dark, #f7f7f7);--sd-surface-sunken: var(--background-color-dark-light, #e8e8e8);--sd-text: var(--header-text-primary, #333333);--sd-text-muted: var(--header-text-secondary, #5f6368);--sd-text-subtle: var(--header-text-muted, #9ca3af);--sd-border: var(--dropdown-border, #e3e3e3);--sd-border-strong: var(--border-color, #c9c9c9);--sd-card: var(--background-color, #ffffff);--sd-avatar: #eceff3;--sd-input: var(--background-color, #ffffff);--sd-input-border: var(--border-color, #c9c9c9);--sd-focus-ring: 0 0 0 3px rgba(255, 102, 51, .22)}:host.avaya-ipo-dark-mode{--sd-accent: #ff7a4d;--sd-accent-strong: #ff6633;--sd-accent-soft: rgba(255, 122, 77, .14);--sd-accent-contrast: #1a1109;--sd-call-accept: #34c759;--sd-call-end: #ff5b5b;--sd-surface: #0f1115;--sd-surface-muted: #151a22;--sd-surface-sunken: #1c222c;--sd-text: #f2f4f8;--sd-text-muted: #aab0bb;--sd-text-subtle: #7e8794;--sd-border: #2a2f3a;--sd-border-strong: #353b47;--sd-card: #161b24;--sd-avatar: #222833;--sd-input: #1c222c;--sd-input-border: #2a2f3a;--sd-focus-ring: 0 0 0 3px rgba(255, 122, 77, .25)}:host.avaya-ipo-mode-popup{min-width:320px;height:100%}:host.avaya-ipo-mode-standalone{width:100%;max-width:460px;height:100dvh;min-height:100dvh;margin-inline:auto;border-inline:1px solid var(--sd-border);box-shadow:var(--sd-shadow-lg);overflow:hidden}@media (max-width: 768px){:host.avaya-ipo-mode-standalone{max-width:100%;border-inline:0;box-shadow:none}}.avaya-ipo-container_connection_status_overlay{width:100%;position:relative;background-color:#7f7f7f1a;padding:10px;box-sizing:border-box}.avaya-ipo-container_connection_status{width:100%;background:var(--sd-card);padding:8px 10px;border-radius:var(--sd-radius-sm);font-size:13px;font-weight:600;color:var(--sd-text-muted);display:flex;gap:8px;align-items:center;box-shadow:var(--sd-shadow-md)}.avaya-ipo-container_connection_status .fa-circle-dot{color:var(--sd-accent)}.avaya-ipo-simple-placeholder{padding:20px;text-align:center;color:var(--sd-text);font-weight:600}.avaya-ipo-auth-error-screen,.avaya-ipo-auth-loading-screen{min-height:100%;flex:1 1 auto;display:flex;align-items:center;justify-content:center;padding:24px;box-sizing:border-box;background:var(--sd-surface)}.avaya-ipo-auth-loading-card{width:min(320px,100%);padding:24px;border-radius:var(--sd-radius-lg);background:var(--sd-card);border:1px solid var(--sd-border);box-shadow:var(--sd-shadow-lg);display:flex;flex-direction:column;gap:10px;text-align:center;align-items:center}.avaya-ipo-auth-loading-title{font-size:16px;font-weight:800;color:var(--sd-text)}.avaya-ipo-auth-loading-message{font-size:12px;color:var(--sd-text-muted)}.avaya-ipo-auth-error-card{width:min(360px,100%);padding:24px;border-radius:var(--sd-radius-lg);background:var(--sd-card);border:1px solid var(--sd-border);box-shadow:var(--sd-shadow-lg);display:flex;flex-direction:column;gap:10px;text-align:center}.avaya-ipo-auth-error-icon{width:56px;height:56px;margin:0 auto 4px;display:inline-flex;align-items:center;justify-content:center;border-radius:var(--sd-radius-lg);background:var(--sd-accent);color:var(--sd-accent-contrast)}.avaya-ipo-auth-error-icon svg{width:28px;height:28px}.avaya-ipo-auth-error-title{font-size:18px;font-weight:800;color:var(--sd-text)}.avaya-ipo-auth-error-message{font-size:13px;color:var(--sd-text-muted)}.avaya-ipo-auth-error-hint{font-size:12px;color:var(--sd-text-subtle)}.avaya-ipo-media-elements{display:none}.avaya-ipo-widget-body{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;position:relative;height:100%;padding-bottom:0;overflow-y:auto;-webkit-overflow-scrolling:touch}.avaya-ipo-logout-btn{border:none;background:var(--sd-call-end);color:#fff;padding:8px 12px;border-radius:var(--sd-radius-sm);cursor:pointer;font-weight:600}.avaya-ipo-logout-btn:hover{filter:brightness(.94)}.avaya-ipo-widget-content{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;gap:1rem;padding-bottom:0;height:100%}.avaya-ipo-page-heading{display:flex;align-items:center;justify-content:space-between;font-size:22px;font-weight:800;color:var(--sd-text);padding:15px;z-index:5;background:var(--sd-surface)}.avaya-ipo-settings-btn{width:34px;height:34px;border-radius:var(--sd-radius-sm);border:none;background:transparent;color:var(--sd-text-muted);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;transition:background .12s ease,color .12s ease}.avaya-ipo-settings-btn:hover{background:var(--sd-surface-muted)}.avaya-ipo-settings-btn.avaya-ipo-is-active{color:var(--sd-accent);background:var(--sd-accent-soft)}.avaya-ipo-settings-btn:disabled{cursor:default;opacity:.75}.avaya-ipo-settings-btn-floating{position:absolute;top:8px;right:8px;z-index:6}.avaya-ipo-heading-with-back{display:inline-flex;align-items:center;gap:4px}.avaya-ipo-settings-btn.avaya-ipo-back-btn{color:var(--sd-text)}.avaya-ipo-tabs{position:sticky;bottom:0;left:0;right:0;display:flex;gap:6px;padding:8px 12px;background:var(--sd-surface-muted);border-top:1px solid var(--sd-border);box-sizing:border-box;z-index:10;height:var(--sd-tabs-height)}.avaya-ipo-tab-btn{flex:1 1 0;height:44px;border:none;background:transparent;font-weight:600;color:var(--sd-text-muted);border-radius:var(--sd-radius-sm);cursor:pointer;transition:background .12s ease,color .12s ease;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;font-size:12px;box-shadow:none}.avaya-ipo-tab-btn:hover{color:var(--sd-text)}.avaya-ipo-tab-btn.avaya-ipo-active{background:var(--sd-accent-soft);color:var(--sd-accent);box-shadow:none}.avaya-ipo-tab-icon{display:flex;align-items:center;justify-content:center;font-size:16px}.avaya-ipo-tab-btn.avaya-ipo-active .avaya-ipo-tab-icon{color:var(--sd-accent)}.avaya-ipo-global-loader{position:absolute;inset:0;background:color-mix(in srgb,var(--sd-surface) 85%,transparent);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;z-index:1000;color:var(--sd-text);font-weight:700}.avaya-ipo-spinner{width:48px;height:48px;border:4px solid var(--sd-surface-sunken);border-top-color:var(--sd-accent);border-radius:50%;animation:spin .9s linear infinite}.avaya-ipo-loader-text{font-size:14px}@keyframes spin{to{transform:rotate(360deg)}}:host ::ng-deep .avaya-ipo-widget-body,:host ::ng-deep .avaya-ipo-widget-content{background:var(--sd-surface);color:var(--sd-text)}:host ::ng-deep .avaya-ipo-page-heading{color:var(--sd-text)}:host ::ng-deep .avaya-ipo-settings-btn{color:var(--sd-text-muted)}:host ::ng-deep .avaya-ipo-tabs{background:var(--sd-surface-muted);border-top-color:var(--sd-border)}:host ::ng-deep .avaya-ipo-tab-btn{color:var(--sd-text-muted)}:host ::ng-deep .avaya-ipo-tab-btn.avaya-ipo-active{color:var(--sd-accent);background:var(--sd-accent-soft)}:host ::ng-deep .avaya-ipo-tab-btn.avaya-ipo-active .avaya-ipo-tab-icon{color:var(--sd-accent)}:host ::ng-deep .avaya-ipo-history_item,:host ::ng-deep .avaya-ipo-contact-card{border-bottom-color:var(--sd-border)}:host ::ng-deep .avaya-ipo-history_avatar,:host ::ng-deep .avaya-ipo-skeleton_avatar,:host ::ng-deep .avaya-ipo-contact-avatar{background:var(--sd-avatar);color:var(--sd-text)}:host ::ng-deep .avaya-ipo-history_name,:host ::ng-deep .avaya-ipo-contact-name{color:var(--sd-text)}:host ::ng-deep .avaya-ipo-history_time,:host ::ng-deep .avaya-ipo-history_meta,:host ::ng-deep .avaya-ipo-history_group_label,:host ::ng-deep .avaya-ipo-contact-phone,:host ::ng-deep .avaya-ipo-contacts-state{color:var(--sd-text-muted)}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-history_group_label{background:#151a22eb}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-history_item:hover{background:#1a202b}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-contacts-search input{background:var(--sd-input);border-color:var(--sd-input-border);color:var(--sd-text)}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-contacts-search input::placeholder{color:var(--sd-text-muted)}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-contact-call{background:#1e2b22;color:#79d48a}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-contact-back{background:#141922;border-color:var(--sd-border);color:var(--sd-text)}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-contact-call-btn{background:#1f2530;border-color:#1f2530;color:#79d48a}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-global-loader{background:#0f1115e6;color:var(--sd-text)}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-spinner{border-color:#2a2f3a;border-top-color:var(--sd-accent)}.avaya-ipo-widget-version-footer{flex:0 0 auto;padding:2px 8px 4px;text-align:center;font-size:10px;line-height:1.2;letter-spacing:.02em;color:var(--sd-text-muted);background:var(--sd-surface);border-top:1px solid var(--sd-border);-webkit-user-select:text;user-select:text}\n"], dependencies: [{ kind: "component", type: CallHistoryComponent, selector: "app-call-history", inputs: ["mode", "tenantId", "agentId"], outputs: ["makeCallEv", "transferEv"] }, { kind: "component", type: ContactsComponent, selector: "app-contacts", inputs: ["tenantId"], outputs: ["makeCallEv"] }, { kind: "component", type: SettingsComponent, selector: "app-settings", inputs: ["selectedAudioInputId", "selectedAudioOutputId", "extensionId", "darkMode"], outputs: ["change", "darkModeChange", "logout"] }, { kind: "component", type: AvayaIpoLoginComponent, selector: "app-avaya-ipo-login", inputs: ["loginForm", "showLoginLoader", "hasAvayaSocketError", "hasAvayaLoginError", "hasAvayaLoginFailedGeneral", "hasAvayaLoginConflictError", "hasAvayaDeviceError"], outputs: ["login"] }, { kind: "component", type: PhoneComponent, selector: "app-phone", inputs: ["contacts", "tenantId", "agentId", "isActiveCall", "activeCallNumber", "incomingCallName", "incomingNameResolved", "activeCallName", "serverIp", "serverPort", "stunIp", "stunPort", "turnIp", "turnPort", "configurationMode", "recordingUploadMode", "entityId"], outputs: ["callScreenChange", "makeCallEv", "endCallEv"] }] });
|
|
4997
5356
|
}
|
|
4998
5357
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AvayaIPOWidgetComponent, decorators: [{
|
|
4999
5358
|
type: Component,
|
|
5000
|
-
args: [{ selector: 'snugdesk-avaya-ipo-widget', standalone: false, template: "<div class=\"avaya-ipo-media-elements\">\n <audio id=\"rmtAudio\" autoplay></audio>\n <audio id=\"lclAudio\" autoplay></audio>\n <video id=\"rmtVideo\" autoplay playsinline></video>\n <video id=\"lclVideo\" autoplay playsinline muted></video>\n</div>\n\n@if (authLoading) {\n <div class=\"avaya-ipo-auth-loading-screen\">\n <div class=\"avaya-ipo-auth-loading-card\">\n <div class=\"avaya-ipo-spinner\" aria-hidden=\"true\"></div>\n <div class=\"avaya-ipo-auth-loading-title\">Checking authentication</div>\n <div class=\"avaya-ipo-auth-loading-message\">Please wait...</div>\n </div>\n </div>\n} @else if (authErrorMessage) {\n <div class=\"avaya-ipo-auth-error-screen\">\n <div class=\"avaya-ipo-auth-error-card\">\n <div class=\"avaya-ipo-auth-error-icon\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" focusable=\"false\">\n <path\n fill=\"currentColor\"\n d=\"M12 2a7 7 0 0 1 7 7v3a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-6a2 2 0 0 1 2-2V9a7 7 0 0 1 7-7zm5 10V9a5 5 0 0 0-10 0v3h10zm-5 3a2 2 0 0 0-1 3.732V20h2v-1.268A2 2 0 0 0 12 15z\"\n />\n </svg>\n </div>\n <div class=\"avaya-ipo-auth-error-title\">Authentication Required</div>\n <div class=\"avaya-ipo-auth-error-message\">{{ authErrorMessage }}</div>\n </div>\n </div>\n} @else {\n@if (isReconnectingInProgress || isFailoverInProgress || isFailbackInProgress) {\n <div class=\"avaya-ipo-container_connection_status_overlay\">\n <div class=\"avaya-ipo-container_connection_status\">\n <i class=\"fa-solid fa-circle-dot\"></i>\n @if (isReconnectingInProgress) { <span>Reconnecting to the server...</span> }\n @if (isFailoverInProgress) { <span>Failover in progress...</span> }\n @if (isFailbackInProgress) { <span>Failback in progress...</span> }\n </div>\n </div>\n}\n\n@if (showLoginForm) {\n <app-avaya-ipo-login\n [loginForm]=\"loginForm\"\n [showLoginLoader]=\"showLoginLoader\"\n [hasAvayaSocketError]=\"hasAvayaSocketError\"\n [hasAvayaLoginError]=\"hasAvayaLoginError\"\n [hasAvayaLoginFailedGeneral]=\"hasAvayaLoginFailedGeneral\"\n [hasAvayaLoginConflictError]=\"hasAvayaLoginConflictError\"\n [hasAvayaDeviceError]=\"hasAvayaDeviceError\"\n (login)=\"onLogin()\"\n ></app-avaya-ipo-login>\n} @else {\n <div class=\"avaya-ipo-widget-body\">\n <div class=\"avaya-ipo-widget-content\">\n @if (activeTab === 'history') {\n @if (!hideTabs) {\n <div class=\"avaya-ipo-page-heading avaya-ipo-heading-right\">\n <span>History</span>\n <button type=\"button\" class=\"avaya-ipo-settings-btn\" (click)=\"selectTab('settings')\">\n <svg width=\"18px\" viewBox=\"-0.03 0 16.079 16.079\" aria-hidden=\"true\" focusable=\"false\">\n <g>\n <path fill=\"currentColor\"\n d=\"M8.182 1.083a6.99 6.99 0 0 0-6.248 3.493c-1.929 3.342-.776 7.629 2.57 9.562 3.346 1.934 7.634.792 9.562-2.55 1.929-3.343.776-7.634-2.57-9.567a6.98 6.98 0 0 0-3.314-.938zM8 2.08a6 6 0 0 1 6 6 6 6 0 0 1-6 6 6 6 0 0 1-6-6 6 6 0 0 1 6-6z\" />\n <path fill=\"currentColor\"\n d=\"M9.322 0L6.69.393v1.354a6.49 6.477 43.146 0 1 2.632.005V0zM3.937 1.19L1.93 2.897l.988 1.177a6.49 6.477 43.146 0 1 2.017-1.69zm8.128.013l-.993 1.184a6.49 6.477 43.146 0 1 .17.09 6.49 6.477 43.146 0 1 1.845 1.603l1.006-1.197zM.455 5.42l-.44 2.596 1.515.267a6.49 6.477 43.146 0 1 .455-2.593zm15.086.003l-1.523.269a6.49 6.477 43.146 0 1 .464 2.59l1.533-.27zM1.858 10.118l-1.351.78 1.33 2.271 1.339-.772a6.49 6.477 43.146 0 1-1.317-2.28zm12.301.003a6.49 6.477 43.146 0 1-.534 1.215 6.49 6.477 43.146 0 1-.774 1.069l1.338.773 1.302-2.288zm-9.557 3.471l-.534 1.47 2.48.884.525-1.446a6.49 6.477 43.146 0 1-2.303-.8 6.49 6.477 43.146 0 1-.168-.108zm6.814.016a6.49 6.477 43.146 0 1-2.475.897l.53 1.457 2.468-.917z\" />\n <path fill=\"currentColor\"\n d=\"M7.648 3.093a4.989 4.989 0 0 0-3.982 2.483 5.013 5.013 0 0 0 1.836 6.834 5.002 5.002 0 0 0 6.83-1.827 5.01 5.01 0 0 0-1.836-6.832 4.976 4.976 0 0 0-2.848-.658zM8 4.08a4 4 0 0 1 4 4 4 4 0 0 1-4 4 4 4 0 0 1-4-4 4 4 0 0 1 4-4z\" />\n </g>\n </svg>\n </button>\n </div>\n }\n <app-call-history\n [tenantId]=\"tenantId\"\n [agentId]=\"loginForm?.value?.ipoExtensionNo || userId\"\n (makeCallEv)=\"onHistoryMakeCall($event)\"\n ></app-call-history>\n } @else if (activeTab === 'contacts') {\n @if (!hideTabs) {\n <div class=\"avaya-ipo-page-heading\">\n <span>Contacts</span>\n <button type=\"button\" class=\"avaya-ipo-settings-btn\" (click)=\"selectTab('settings')\">\n <svg width=\"18px\" viewBox=\"-0.03 0 16.079 16.079\" aria-hidden=\"true\" focusable=\"false\">\n <g>\n <path fill=\"currentColor\"\n d=\"M8.182 1.083a6.99 6.99 0 0 0-6.248 3.493c-1.929 3.342-.776 7.629 2.57 9.562 3.346 1.934 7.634.792 9.562-2.55 1.929-3.343.776-7.634-2.57-9.567a6.98 6.98 0 0 0-3.314-.938zM8 2.08a6 6 0 0 1 6 6 6 6 0 0 1-6 6 6 6 0 0 1-6-6 6 6 0 0 1 6-6z\" />\n <path fill=\"currentColor\"\n d=\"M9.322 0L6.69.393v1.354a6.49 6.477 43.146 0 1 2.632.005V0zM3.937 1.19L1.93 2.897l.988 1.177a6.49 6.477 43.146 0 1 2.017-1.69zm8.128.013l-.993 1.184a6.49 6.477 43.146 0 1 .17.09 6.49 6.477 43.146 0 1 1.845 1.603l1.006-1.197zM.455 5.42l-.44 2.596 1.515.267a6.49 6.477 43.146 0 1 .455-2.593zm15.086.003l-1.523.269a6.49 6.477 43.146 0 1 .464 2.59l1.533-.27zM1.858 10.118l-1.351.78 1.33 2.271 1.339-.772a6.49 6.477 43.146 0 1-1.317-2.28zm12.301.003a6.49 6.477 43.146 0 1-.534 1.215 6.49 6.477 43.146 0 1-.774 1.069l1.338.773 1.302-2.288zm-9.557 3.471l-.534 1.47 2.48.884.525-1.446a6.49 6.477 43.146 0 1-2.303-.8 6.49 6.477 43.146 0 1-.168-.108zm6.814.016a6.49 6.477 43.146 0 1-2.475.897l.53 1.457 2.468-.917z\" />\n <path fill=\"currentColor\"\n d=\"M7.648 3.093a4.989 4.989 0 0 0-3.982 2.483 5.013 5.013 0 0 0 1.836 6.834 5.002 5.002 0 0 0 6.83-1.827 5.01 5.01 0 0 0-1.836-6.832 4.976 4.976 0 0 0-2.848-.658zM8 4.08a4 4 0 0 1 4 4 4 4 0 0 1-4 4 4 4 0 0 1-4-4 4 4 0 0 1 4-4z\" />\n </g>\n </svg>\n </button>\n </div>\n }\n <app-contacts [tenantId]=\"tenantId\" (makeCallEv)=\"onContactsMakeCall($event)\"></app-contacts>\n } @else if (activeTab === 'settings') {\n @if (!hideTabs) {\n <div class=\"avaya-ipo-page-heading\">\n <span class=\"avaya-ipo-heading-with-back\">\n <button type=\"button\" class=\"avaya-ipo-settings-btn avaya-ipo-back-btn\" (click)=\"closeSettings()\" aria-label=\"Go back\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.4\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\">\n <path d=\"M15 18l-6-6 6-6\" />\n </svg>\n </button>\n <span>Settings</span>\n </span>\n </div>\n }\n <app-settings\n [extensionId]=\"loginForm?.value?.ipoExtensionNo || userId\"\n [darkMode]=\"darkModeEnabled\"\n (darkModeChange)=\"onThemeToggle($event)\"\n (change)=\"onDeviceChange($event)\"\n (logout)=\"onLogout()\"\n ></app-settings>\n } @else {\n @if (!hideTabs) {\n <button type=\"button\" class=\"avaya-ipo-settings-btn avaya-ipo-settings-btn-floating\" (click)=\"selectTab('settings')\" aria-label=\"Settings\">\n <svg width=\"18px\" viewBox=\"-0.03 0 16.079 16.079\" aria-hidden=\"true\" focusable=\"false\">\n <g>\n <path fill=\"currentColor\"\n d=\"M8.182 1.083a6.99 6.99 0 0 0-6.248 3.493c-1.929 3.342-.776 7.629 2.57 9.562 3.346 1.934 7.634.792 9.562-2.55 1.929-3.343.776-7.634-2.57-9.567a6.98 6.98 0 0 0-3.314-.938zM8 2.08a6 6 0 0 1 6 6 6 6 0 0 1-6 6 6 6 0 0 1-6-6 6 6 0 0 1 6-6z\" />\n <path fill=\"currentColor\"\n d=\"M9.322 0L6.69.393v1.354a6.49 6.477 43.146 0 1 2.632.005V0zM3.937 1.19L1.93 2.897l.988 1.177a6.49 6.477 43.146 0 1 2.017-1.69zm8.128.013l-.993 1.184a6.49 6.477 43.146 0 1 .17.09 6.49 6.477 43.146 0 1 1.845 1.603l1.006-1.197zM.455 5.42l-.44 2.596 1.515.267a6.49 6.477 43.146 0 1 .455-2.593zm15.086.003l-1.523.269a6.49 6.477 43.146 0 1 .464 2.59l1.533-.27zM1.858 10.118l-1.351.78 1.33 2.271 1.339-.772a6.49 6.477 43.146 0 1-1.317-2.28zm12.301.003a6.49 6.477 43.146 0 1-.534 1.215 6.49 6.477 43.146 0 1-.774 1.069l1.338.773 1.302-2.288zm-9.557 3.471l-.534 1.47 2.48.884.525-1.446a6.49 6.477 43.146 0 1-2.303-.8 6.49 6.477 43.146 0 1-.168-.108zm6.814.016a6.49 6.477 43.146 0 1-2.475.897l.53 1.457 2.468-.917z\" />\n <path fill=\"currentColor\"\n d=\"M7.648 3.093a4.989 4.989 0 0 0-3.982 2.483 5.013 5.013 0 0 0 1.836 6.834 5.002 5.002 0 0 0 6.83-1.827 5.01 5.01 0 0 0-1.836-6.832 4.976 4.976 0 0 0-2.848-.658zM8 4.08a4 4 0 0 1 4 4 4 4 0 0 1-4 4 4 4 0 0 1-4-4 4 4 0 0 1 4-4z\" />\n </g>\n </svg>\n </button>\n }\n <app-phone\n [tenantId]=\"tenantId\"\n [agentId]=\"loginForm?.value?.ipoExtensionNo || userId\"\n [activeCallNumber]=\"activeCallNumber\"\n [incomingCallName]=\"incomingCallName\"\n [incomingNameResolved]=\"incomingNameResolved\"\n [activeCallName]=\"activeCallName\"\n [serverIp]=\"avayaIPOServerIP\"\n [serverPort]=\"avayaIPOServerPort\"\n [stunIp]=\"avayaIPOServerStunServerIP\"\n [stunPort]=\"avayaIPOServerStunServerPort\"\n [turnIp]=\"avayaIPOServerTurnServerIP\"\n [turnPort]=\"avayaIPOServerTurnServerPort\"\n [configurationMode]=\"configurationMode\"\n (makeCallEv)=\"onPhoneMakeCall($event)\"\n (endCallEv)=\"onPhoneEndCall()\"\n (callScreenChange)=\"onCallScreenChange($event)\"\n ></app-phone>\n }\n </div>\n </div>\n\n @if (showLoginLoader) {\n <div class=\"avaya-ipo-global-loader\">\n <div class=\"avaya-ipo-spinner\"></div>\n <div class=\"avaya-ipo-loader-text\">Connecting to phone service...</div>\n </div>\n }\n\n @if (!hideTabs && showTabBar) {\n <nav class=\"avaya-ipo-tabs\">\n @if (showHistory) {\n <button type=\"button\" class=\"avaya-ipo-tab-btn\" [class.avaya-ipo-active]=\"activeTab === 'history'\" (click)=\"selectTab('history')\">\n <span class=\"avaya-ipo-tab-icon\">\n <svg width=\"19px\" viewBox=\"0 0 512 512\" aria-hidden=\"true\" focusable=\"false\">\n <path fill=\"currentColor\"\n d=\"M464 256A208 208 0 1 1 48 256a208 208 0 1 1 416 0zM0 256a256 256 0 1 0 512 0A256 256 0 1 0 0 256zM232 120l0 136c0 8 4 15.5 10.7 20l96 64c11 7.4 25.9 4.4 33.3-6.7s4.4-25.9-6.7-33.3L280 243.2 280 120c0-13.3-10.7-24-24-24s-24 10.7-24 24z\" />\n </svg>\n </span>\n <span class=\"avaya-ipo-tab-text\">History</span>\n </button>\n }\n @if (showContacts) {\n <button type=\"button\" class=\"avaya-ipo-tab-btn\" [class.avaya-ipo-active]=\"activeTab === 'contacts'\" (click)=\"selectTab('contacts')\">\n <span class=\"avaya-ipo-tab-icon\">\n <svg width=\"18px\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path fill=\"currentColor\"\n d=\"M12 12a4 4 0 1 0-4-4 4 4 0 0 0 4 4zm0 2c-4.4 0-8 2.2-8 5v2h16v-2c0-2.8-3.6-5-8-5z\" />\n </svg>\n </span>\n <span class=\"avaya-ipo-tab-text\">Contacts</span>\n </button>\n }\n <button type=\"button\" class=\"avaya-ipo-tab-btn\" [class.avaya-ipo-active]=\"activeTab === 'keypad'\" (click)=\"selectTab('keypad')\">\n <span class=\"avaya-ipo-tab-icon\">\n <svg width=\"20px\" viewBox=\"0 0 512 512\" aria-hidden=\"true\" focusable=\"false\">\n <path fill=\"currentColor\" d=\"M256,400a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M256,272a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M256,144a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M256,16a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M384,272a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M384,144a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M384,16a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M128,272a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M128,144a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M128,16a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n </svg>\n </span>\n <span class=\"avaya-ipo-tab-text\">Keypad</span>\n </button>\n </nav>\n }\n\n @if (activeTab !== 'keypad') {\n <div class=\"avaya-ipo-widget-version-footer\">v{{ widgetVersion }}</div>\n }\n}\n}\n", styles: [":host{display:flex;flex-direction:column;width:100%;max-width:100%;margin:0;font-family:Open Sans,Helvetica Neue,Arial,sans-serif;min-height:100%;height:100%;background:var(--sd-surface);color:var(--sd-text);box-sizing:border-box;--sd-tabs-height: 60px;--sd-radius-sm: 8px;--sd-radius: 10px;--sd-radius-lg: 14px;--sd-radius-pill: 999px;--sd-shadow-sm: 0 1px 2px rgba(16, 24, 40, .06);--sd-shadow-md: 0 6px 16px rgba(16, 24, 40, .1);--sd-shadow-lg: 0 16px 40px rgba(16, 24, 40, .14);--sd-accent: var(--app-color, #ff6633);--sd-accent-strong: var(--app-color-dark, #ff5805);--sd-accent-soft: #fff1ea;--sd-accent-contrast: #ffffff;--sd-call-accept: #1aa251;--sd-call-end: #e5484d;--sd-surface: var(--background-color, #ffffff);--sd-surface-muted: var(--background-color-dark, #f7f7f7);--sd-surface-sunken: var(--background-color-dark-light, #e8e8e8);--sd-text: var(--header-text-primary, #333333);--sd-text-muted: var(--header-text-secondary, #5f6368);--sd-text-subtle: var(--header-text-muted, #9ca3af);--sd-border: var(--dropdown-border, #e3e3e3);--sd-border-strong: var(--border-color, #c9c9c9);--sd-card: var(--background-color, #ffffff);--sd-avatar: #eceff3;--sd-input: var(--background-color, #ffffff);--sd-input-border: var(--border-color, #c9c9c9);--sd-focus-ring: 0 0 0 3px rgba(255, 102, 51, .22)}:host.avaya-ipo-dark-mode{--sd-accent: #ff7a4d;--sd-accent-strong: #ff6633;--sd-accent-soft: rgba(255, 122, 77, .14);--sd-accent-contrast: #1a1109;--sd-call-accept: #34c759;--sd-call-end: #ff5b5b;--sd-surface: #0f1115;--sd-surface-muted: #151a22;--sd-surface-sunken: #1c222c;--sd-text: #f2f4f8;--sd-text-muted: #aab0bb;--sd-text-subtle: #7e8794;--sd-border: #2a2f3a;--sd-border-strong: #353b47;--sd-card: #161b24;--sd-avatar: #222833;--sd-input: #1c222c;--sd-input-border: #2a2f3a;--sd-focus-ring: 0 0 0 3px rgba(255, 122, 77, .25)}:host.avaya-ipo-mode-popup{min-width:320px;height:100%}:host.avaya-ipo-mode-standalone{width:100%;max-width:460px;height:100dvh;min-height:100dvh;margin-inline:auto;border-inline:1px solid var(--sd-border);box-shadow:var(--sd-shadow-lg);overflow:hidden}@media (max-width: 768px){:host.avaya-ipo-mode-standalone{max-width:100%;border-inline:0;box-shadow:none}}.avaya-ipo-container_connection_status_overlay{width:100%;position:relative;background-color:#7f7f7f1a;padding:10px;box-sizing:border-box}.avaya-ipo-container_connection_status{width:100%;background:var(--sd-card);padding:8px 10px;border-radius:var(--sd-radius-sm);font-size:13px;font-weight:600;color:var(--sd-text-muted);display:flex;gap:8px;align-items:center;box-shadow:var(--sd-shadow-md)}.avaya-ipo-container_connection_status .fa-circle-dot{color:var(--sd-accent)}.avaya-ipo-simple-placeholder{padding:20px;text-align:center;color:var(--sd-text);font-weight:600}.avaya-ipo-auth-error-screen,.avaya-ipo-auth-loading-screen{min-height:100%;flex:1 1 auto;display:flex;align-items:center;justify-content:center;padding:24px;box-sizing:border-box;background:var(--sd-surface)}.avaya-ipo-auth-loading-card{width:min(320px,100%);padding:24px;border-radius:var(--sd-radius-lg);background:var(--sd-card);border:1px solid var(--sd-border);box-shadow:var(--sd-shadow-lg);display:flex;flex-direction:column;gap:10px;text-align:center;align-items:center}.avaya-ipo-auth-loading-title{font-size:16px;font-weight:800;color:var(--sd-text)}.avaya-ipo-auth-loading-message{font-size:12px;color:var(--sd-text-muted)}.avaya-ipo-auth-error-card{width:min(360px,100%);padding:24px;border-radius:var(--sd-radius-lg);background:var(--sd-card);border:1px solid var(--sd-border);box-shadow:var(--sd-shadow-lg);display:flex;flex-direction:column;gap:10px;text-align:center}.avaya-ipo-auth-error-icon{width:56px;height:56px;margin:0 auto 4px;display:inline-flex;align-items:center;justify-content:center;border-radius:var(--sd-radius-lg);background:var(--sd-accent);color:var(--sd-accent-contrast)}.avaya-ipo-auth-error-icon svg{width:28px;height:28px}.avaya-ipo-auth-error-title{font-size:18px;font-weight:800;color:var(--sd-text)}.avaya-ipo-auth-error-message{font-size:13px;color:var(--sd-text-muted)}.avaya-ipo-auth-error-hint{font-size:12px;color:var(--sd-text-subtle)}.avaya-ipo-media-elements{display:none}.avaya-ipo-widget-body{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;position:relative;height:100%;padding-bottom:0;overflow-y:auto;-webkit-overflow-scrolling:touch}.avaya-ipo-logout-btn{border:none;background:var(--sd-call-end);color:#fff;padding:8px 12px;border-radius:var(--sd-radius-sm);cursor:pointer;font-weight:600}.avaya-ipo-logout-btn:hover{filter:brightness(.94)}.avaya-ipo-widget-content{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;gap:1rem;padding-bottom:0;height:100%}.avaya-ipo-page-heading{display:flex;align-items:center;justify-content:space-between;font-size:22px;font-weight:800;color:var(--sd-text);padding:15px;z-index:5;background:var(--sd-surface)}.avaya-ipo-settings-btn{width:34px;height:34px;border-radius:var(--sd-radius-sm);border:none;background:transparent;color:var(--sd-text-muted);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;transition:background .12s ease,color .12s ease}.avaya-ipo-settings-btn:hover{background:var(--sd-surface-muted)}.avaya-ipo-settings-btn.avaya-ipo-is-active{color:var(--sd-accent);background:var(--sd-accent-soft)}.avaya-ipo-settings-btn:disabled{cursor:default;opacity:.75}.avaya-ipo-settings-btn-floating{position:absolute;top:8px;right:8px;z-index:6}.avaya-ipo-heading-with-back{display:inline-flex;align-items:center;gap:4px}.avaya-ipo-settings-btn.avaya-ipo-back-btn{color:var(--sd-text)}.avaya-ipo-tabs{position:sticky;bottom:0;left:0;right:0;display:flex;gap:6px;padding:8px 12px;background:var(--sd-surface-muted);border-top:1px solid var(--sd-border);box-sizing:border-box;z-index:10;height:var(--sd-tabs-height)}.avaya-ipo-tab-btn{flex:1 1 0;height:44px;border:none;background:transparent;font-weight:600;color:var(--sd-text-muted);border-radius:var(--sd-radius-sm);cursor:pointer;transition:background .12s ease,color .12s ease;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;font-size:12px;box-shadow:none}.avaya-ipo-tab-btn:hover{color:var(--sd-text)}.avaya-ipo-tab-btn.avaya-ipo-active{background:var(--sd-accent-soft);color:var(--sd-accent);box-shadow:none}.avaya-ipo-tab-icon{display:flex;align-items:center;justify-content:center;font-size:16px}.avaya-ipo-tab-btn.avaya-ipo-active .avaya-ipo-tab-icon{color:var(--sd-accent)}.avaya-ipo-global-loader{position:absolute;inset:0;background:color-mix(in srgb,var(--sd-surface) 85%,transparent);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;z-index:1000;color:var(--sd-text);font-weight:700}.avaya-ipo-spinner{width:48px;height:48px;border:4px solid var(--sd-surface-sunken);border-top-color:var(--sd-accent);border-radius:50%;animation:spin .9s linear infinite}.avaya-ipo-loader-text{font-size:14px}@keyframes spin{to{transform:rotate(360deg)}}:host ::ng-deep .avaya-ipo-widget-body,:host ::ng-deep .avaya-ipo-widget-content{background:var(--sd-surface);color:var(--sd-text)}:host ::ng-deep .avaya-ipo-page-heading{color:var(--sd-text)}:host ::ng-deep .avaya-ipo-settings-btn{color:var(--sd-text-muted)}:host ::ng-deep .avaya-ipo-tabs{background:var(--sd-surface-muted);border-top-color:var(--sd-border)}:host ::ng-deep .avaya-ipo-tab-btn{color:var(--sd-text-muted)}:host ::ng-deep .avaya-ipo-tab-btn.avaya-ipo-active{color:var(--sd-accent);background:var(--sd-accent-soft)}:host ::ng-deep .avaya-ipo-tab-btn.avaya-ipo-active .avaya-ipo-tab-icon{color:var(--sd-accent)}:host ::ng-deep .avaya-ipo-history_item,:host ::ng-deep .avaya-ipo-contact-card{border-bottom-color:var(--sd-border)}:host ::ng-deep .avaya-ipo-history_avatar,:host ::ng-deep .avaya-ipo-skeleton_avatar,:host ::ng-deep .avaya-ipo-contact-avatar{background:var(--sd-avatar);color:var(--sd-text)}:host ::ng-deep .avaya-ipo-history_name,:host ::ng-deep .avaya-ipo-contact-name{color:var(--sd-text)}:host ::ng-deep .avaya-ipo-history_time,:host ::ng-deep .avaya-ipo-history_meta,:host ::ng-deep .avaya-ipo-history_group_label,:host ::ng-deep .avaya-ipo-contact-phone,:host ::ng-deep .avaya-ipo-contacts-state{color:var(--sd-text-muted)}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-history_group_label{background:#151a22eb}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-history_item:hover{background:#1a202b}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-contacts-search input{background:var(--sd-input);border-color:var(--sd-input-border);color:var(--sd-text)}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-contacts-search input::placeholder{color:var(--sd-text-muted)}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-contact-call{background:#1e2b22;color:#79d48a}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-contact-back{background:#141922;border-color:var(--sd-border);color:var(--sd-text)}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-contact-call-btn{background:#1f2530;border-color:#1f2530;color:#79d48a}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-global-loader{background:#0f1115e6;color:var(--sd-text)}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-spinner{border-color:#2a2f3a;border-top-color:var(--sd-accent)}.avaya-ipo-widget-version-footer{flex:0 0 auto;padding:2px 8px 4px;text-align:center;font-size:10px;line-height:1.2;letter-spacing:.02em;color:var(--sd-text-muted);background:var(--sd-surface);border-top:1px solid var(--sd-border);-webkit-user-select:text;user-select:text}\n"] }]
|
|
5359
|
+
args: [{ selector: 'snugdesk-avaya-ipo-widget', standalone: false, template: "<div class=\"avaya-ipo-media-elements\">\n <audio id=\"rmtAudio\" autoplay></audio>\n <audio id=\"lclAudio\" autoplay></audio>\n <video id=\"rmtVideo\" autoplay playsinline></video>\n <video id=\"lclVideo\" autoplay playsinline muted></video>\n</div>\n\n@if (authLoading) {\n <div class=\"avaya-ipo-auth-loading-screen\">\n <div class=\"avaya-ipo-auth-loading-card\">\n <div class=\"avaya-ipo-spinner\" aria-hidden=\"true\"></div>\n <div class=\"avaya-ipo-auth-loading-title\">Checking authentication</div>\n <div class=\"avaya-ipo-auth-loading-message\">Please wait...</div>\n </div>\n </div>\n} @else if (authErrorMessage) {\n <div class=\"avaya-ipo-auth-error-screen\">\n <div class=\"avaya-ipo-auth-error-card\">\n <div class=\"avaya-ipo-auth-error-icon\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" focusable=\"false\">\n <path\n fill=\"currentColor\"\n d=\"M12 2a7 7 0 0 1 7 7v3a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-6a2 2 0 0 1 2-2V9a7 7 0 0 1 7-7zm5 10V9a5 5 0 0 0-10 0v3h10zm-5 3a2 2 0 0 0-1 3.732V20h2v-1.268A2 2 0 0 0 12 15z\"\n />\n </svg>\n </div>\n <div class=\"avaya-ipo-auth-error-title\">Authentication Required</div>\n <div class=\"avaya-ipo-auth-error-message\">{{ authErrorMessage }}</div>\n </div>\n </div>\n} @else {\n@if (isReconnectingInProgress || isFailoverInProgress || isFailbackInProgress) {\n <div class=\"avaya-ipo-container_connection_status_overlay\">\n <div class=\"avaya-ipo-container_connection_status\">\n <i class=\"fa-solid fa-circle-dot\"></i>\n @if (isReconnectingInProgress) { <span>Reconnecting to the server...</span> }\n @if (isFailoverInProgress) { <span>Failover in progress...</span> }\n @if (isFailbackInProgress) { <span>Failback in progress...</span> }\n </div>\n </div>\n}\n\n@if (showLoginForm) {\n <app-avaya-ipo-login\n [loginForm]=\"loginForm\"\n [showLoginLoader]=\"showLoginLoader\"\n [hasAvayaSocketError]=\"hasAvayaSocketError\"\n [hasAvayaLoginError]=\"hasAvayaLoginError\"\n [hasAvayaLoginFailedGeneral]=\"hasAvayaLoginFailedGeneral\"\n [hasAvayaLoginConflictError]=\"hasAvayaLoginConflictError\"\n [hasAvayaDeviceError]=\"hasAvayaDeviceError\"\n (login)=\"onLogin()\"\n ></app-avaya-ipo-login>\n} @else {\n <div class=\"avaya-ipo-widget-body\">\n <div class=\"avaya-ipo-widget-content\">\n @if (activeTab === 'history') {\n @if (!hideTabs) {\n <div class=\"avaya-ipo-page-heading avaya-ipo-heading-right\">\n <span>History</span>\n <button type=\"button\" class=\"avaya-ipo-settings-btn\" (click)=\"selectTab('settings')\">\n <svg width=\"18px\" viewBox=\"-0.03 0 16.079 16.079\" aria-hidden=\"true\" focusable=\"false\">\n <g>\n <path fill=\"currentColor\"\n d=\"M8.182 1.083a6.99 6.99 0 0 0-6.248 3.493c-1.929 3.342-.776 7.629 2.57 9.562 3.346 1.934 7.634.792 9.562-2.55 1.929-3.343.776-7.634-2.57-9.567a6.98 6.98 0 0 0-3.314-.938zM8 2.08a6 6 0 0 1 6 6 6 6 0 0 1-6 6 6 6 0 0 1-6-6 6 6 0 0 1 6-6z\" />\n <path fill=\"currentColor\"\n d=\"M9.322 0L6.69.393v1.354a6.49 6.477 43.146 0 1 2.632.005V0zM3.937 1.19L1.93 2.897l.988 1.177a6.49 6.477 43.146 0 1 2.017-1.69zm8.128.013l-.993 1.184a6.49 6.477 43.146 0 1 .17.09 6.49 6.477 43.146 0 1 1.845 1.603l1.006-1.197zM.455 5.42l-.44 2.596 1.515.267a6.49 6.477 43.146 0 1 .455-2.593zm15.086.003l-1.523.269a6.49 6.477 43.146 0 1 .464 2.59l1.533-.27zM1.858 10.118l-1.351.78 1.33 2.271 1.339-.772a6.49 6.477 43.146 0 1-1.317-2.28zm12.301.003a6.49 6.477 43.146 0 1-.534 1.215 6.49 6.477 43.146 0 1-.774 1.069l1.338.773 1.302-2.288zm-9.557 3.471l-.534 1.47 2.48.884.525-1.446a6.49 6.477 43.146 0 1-2.303-.8 6.49 6.477 43.146 0 1-.168-.108zm6.814.016a6.49 6.477 43.146 0 1-2.475.897l.53 1.457 2.468-.917z\" />\n <path fill=\"currentColor\"\n d=\"M7.648 3.093a4.989 4.989 0 0 0-3.982 2.483 5.013 5.013 0 0 0 1.836 6.834 5.002 5.002 0 0 0 6.83-1.827 5.01 5.01 0 0 0-1.836-6.832 4.976 4.976 0 0 0-2.848-.658zM8 4.08a4 4 0 0 1 4 4 4 4 0 0 1-4 4 4 4 0 0 1-4-4 4 4 0 0 1 4-4z\" />\n </g>\n </svg>\n </button>\n </div>\n }\n <app-call-history\n [tenantId]=\"tenantId\"\n [agentId]=\"loginForm?.value?.ipoExtensionNo || userId\"\n (makeCallEv)=\"onHistoryMakeCall($event)\"\n ></app-call-history>\n } @else if (activeTab === 'contacts') {\n @if (!hideTabs) {\n <div class=\"avaya-ipo-page-heading\">\n <span>Contacts</span>\n <button type=\"button\" class=\"avaya-ipo-settings-btn\" (click)=\"selectTab('settings')\">\n <svg width=\"18px\" viewBox=\"-0.03 0 16.079 16.079\" aria-hidden=\"true\" focusable=\"false\">\n <g>\n <path fill=\"currentColor\"\n d=\"M8.182 1.083a6.99 6.99 0 0 0-6.248 3.493c-1.929 3.342-.776 7.629 2.57 9.562 3.346 1.934 7.634.792 9.562-2.55 1.929-3.343.776-7.634-2.57-9.567a6.98 6.98 0 0 0-3.314-.938zM8 2.08a6 6 0 0 1 6 6 6 6 0 0 1-6 6 6 6 0 0 1-6-6 6 6 0 0 1 6-6z\" />\n <path fill=\"currentColor\"\n d=\"M9.322 0L6.69.393v1.354a6.49 6.477 43.146 0 1 2.632.005V0zM3.937 1.19L1.93 2.897l.988 1.177a6.49 6.477 43.146 0 1 2.017-1.69zm8.128.013l-.993 1.184a6.49 6.477 43.146 0 1 .17.09 6.49 6.477 43.146 0 1 1.845 1.603l1.006-1.197zM.455 5.42l-.44 2.596 1.515.267a6.49 6.477 43.146 0 1 .455-2.593zm15.086.003l-1.523.269a6.49 6.477 43.146 0 1 .464 2.59l1.533-.27zM1.858 10.118l-1.351.78 1.33 2.271 1.339-.772a6.49 6.477 43.146 0 1-1.317-2.28zm12.301.003a6.49 6.477 43.146 0 1-.534 1.215 6.49 6.477 43.146 0 1-.774 1.069l1.338.773 1.302-2.288zm-9.557 3.471l-.534 1.47 2.48.884.525-1.446a6.49 6.477 43.146 0 1-2.303-.8 6.49 6.477 43.146 0 1-.168-.108zm6.814.016a6.49 6.477 43.146 0 1-2.475.897l.53 1.457 2.468-.917z\" />\n <path fill=\"currentColor\"\n d=\"M7.648 3.093a4.989 4.989 0 0 0-3.982 2.483 5.013 5.013 0 0 0 1.836 6.834 5.002 5.002 0 0 0 6.83-1.827 5.01 5.01 0 0 0-1.836-6.832 4.976 4.976 0 0 0-2.848-.658zM8 4.08a4 4 0 0 1 4 4 4 4 0 0 1-4 4 4 4 0 0 1-4-4 4 4 0 0 1 4-4z\" />\n </g>\n </svg>\n </button>\n </div>\n }\n <app-contacts [tenantId]=\"tenantId\" (makeCallEv)=\"onContactsMakeCall($event)\"></app-contacts>\n } @else if (activeTab === 'settings') {\n @if (!hideTabs) {\n <div class=\"avaya-ipo-page-heading\">\n <span class=\"avaya-ipo-heading-with-back\">\n <button type=\"button\" class=\"avaya-ipo-settings-btn avaya-ipo-back-btn\" (click)=\"closeSettings()\" aria-label=\"Go back\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.4\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\">\n <path d=\"M15 18l-6-6 6-6\" />\n </svg>\n </button>\n <span>Settings</span>\n </span>\n </div>\n }\n <app-settings\n [extensionId]=\"loginForm?.value?.ipoExtensionNo || userId\"\n [darkMode]=\"darkModeEnabled\"\n (darkModeChange)=\"onThemeToggle($event)\"\n (change)=\"onDeviceChange($event)\"\n (logout)=\"onLogout()\"\n ></app-settings>\n } @else {\n @if (!hideTabs) {\n <button type=\"button\" class=\"avaya-ipo-settings-btn avaya-ipo-settings-btn-floating\" (click)=\"selectTab('settings')\" aria-label=\"Settings\">\n <svg width=\"18px\" viewBox=\"-0.03 0 16.079 16.079\" aria-hidden=\"true\" focusable=\"false\">\n <g>\n <path fill=\"currentColor\"\n d=\"M8.182 1.083a6.99 6.99 0 0 0-6.248 3.493c-1.929 3.342-.776 7.629 2.57 9.562 3.346 1.934 7.634.792 9.562-2.55 1.929-3.343.776-7.634-2.57-9.567a6.98 6.98 0 0 0-3.314-.938zM8 2.08a6 6 0 0 1 6 6 6 6 0 0 1-6 6 6 6 0 0 1-6-6 6 6 0 0 1 6-6z\" />\n <path fill=\"currentColor\"\n d=\"M9.322 0L6.69.393v1.354a6.49 6.477 43.146 0 1 2.632.005V0zM3.937 1.19L1.93 2.897l.988 1.177a6.49 6.477 43.146 0 1 2.017-1.69zm8.128.013l-.993 1.184a6.49 6.477 43.146 0 1 .17.09 6.49 6.477 43.146 0 1 1.845 1.603l1.006-1.197zM.455 5.42l-.44 2.596 1.515.267a6.49 6.477 43.146 0 1 .455-2.593zm15.086.003l-1.523.269a6.49 6.477 43.146 0 1 .464 2.59l1.533-.27zM1.858 10.118l-1.351.78 1.33 2.271 1.339-.772a6.49 6.477 43.146 0 1-1.317-2.28zm12.301.003a6.49 6.477 43.146 0 1-.534 1.215 6.49 6.477 43.146 0 1-.774 1.069l1.338.773 1.302-2.288zm-9.557 3.471l-.534 1.47 2.48.884.525-1.446a6.49 6.477 43.146 0 1-2.303-.8 6.49 6.477 43.146 0 1-.168-.108zm6.814.016a6.49 6.477 43.146 0 1-2.475.897l.53 1.457 2.468-.917z\" />\n <path fill=\"currentColor\"\n d=\"M7.648 3.093a4.989 4.989 0 0 0-3.982 2.483 5.013 5.013 0 0 0 1.836 6.834 5.002 5.002 0 0 0 6.83-1.827 5.01 5.01 0 0 0-1.836-6.832 4.976 4.976 0 0 0-2.848-.658zM8 4.08a4 4 0 0 1 4 4 4 4 0 0 1-4 4 4 4 0 0 1-4-4 4 4 0 0 1 4-4z\" />\n </g>\n </svg>\n </button>\n }\n <app-phone\n [tenantId]=\"tenantId\"\n [agentId]=\"loginForm?.value?.ipoExtensionNo || userId\"\n [activeCallNumber]=\"activeCallNumber\"\n [incomingCallName]=\"incomingCallName\"\n [incomingNameResolved]=\"incomingNameResolved\"\n [activeCallName]=\"activeCallName\"\n [serverIp]=\"avayaIPOServerIP\"\n [serverPort]=\"avayaIPOServerPort\"\n [stunIp]=\"avayaIPOServerStunServerIP\"\n [stunPort]=\"avayaIPOServerStunServerPort\"\n [turnIp]=\"avayaIPOServerTurnServerIP\"\n [turnPort]=\"avayaIPOServerTurnServerPort\"\n [configurationMode]=\"configurationMode\"\n [recordingUploadMode]=\"recordingUploadMode\"\n [entityId]=\"activeCallEntityId\"\n (makeCallEv)=\"onPhoneMakeCall($event)\"\n (endCallEv)=\"onPhoneEndCall()\"\n (callScreenChange)=\"onCallScreenChange($event)\"\n ></app-phone>\n }\n </div>\n </div>\n\n @if (showLoginLoader) {\n <div class=\"avaya-ipo-global-loader\">\n <div class=\"avaya-ipo-spinner\"></div>\n <div class=\"avaya-ipo-loader-text\">Connecting to phone service...</div>\n </div>\n }\n\n @if (!hideTabs && showTabBar) {\n <nav class=\"avaya-ipo-tabs\">\n @if (showHistory) {\n <button type=\"button\" class=\"avaya-ipo-tab-btn\" [class.avaya-ipo-active]=\"activeTab === 'history'\" (click)=\"selectTab('history')\">\n <span class=\"avaya-ipo-tab-icon\">\n <svg width=\"19px\" viewBox=\"0 0 512 512\" aria-hidden=\"true\" focusable=\"false\">\n <path fill=\"currentColor\"\n d=\"M464 256A208 208 0 1 1 48 256a208 208 0 1 1 416 0zM0 256a256 256 0 1 0 512 0A256 256 0 1 0 0 256zM232 120l0 136c0 8 4 15.5 10.7 20l96 64c11 7.4 25.9 4.4 33.3-6.7s4.4-25.9-6.7-33.3L280 243.2 280 120c0-13.3-10.7-24-24-24s-24 10.7-24 24z\" />\n </svg>\n </span>\n <span class=\"avaya-ipo-tab-text\">History</span>\n </button>\n }\n @if (showContacts) {\n <button type=\"button\" class=\"avaya-ipo-tab-btn\" [class.avaya-ipo-active]=\"activeTab === 'contacts'\" (click)=\"selectTab('contacts')\">\n <span class=\"avaya-ipo-tab-icon\">\n <svg width=\"18px\" viewBox=\"0 0 24 24\" aria-hidden=\"true\" focusable=\"false\">\n <path fill=\"currentColor\"\n d=\"M12 12a4 4 0 1 0-4-4 4 4 0 0 0 4 4zm0 2c-4.4 0-8 2.2-8 5v2h16v-2c0-2.8-3.6-5-8-5z\" />\n </svg>\n </span>\n <span class=\"avaya-ipo-tab-text\">Contacts</span>\n </button>\n }\n <button type=\"button\" class=\"avaya-ipo-tab-btn\" [class.avaya-ipo-active]=\"activeTab === 'keypad'\" (click)=\"selectTab('keypad')\">\n <span class=\"avaya-ipo-tab-icon\">\n <svg width=\"20px\" viewBox=\"0 0 512 512\" aria-hidden=\"true\" focusable=\"false\">\n <path fill=\"currentColor\" d=\"M256,400a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M256,272a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M256,144a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M256,16a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M384,272a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M384,144a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M384,16a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M128,272a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M128,144a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n <path fill=\"currentColor\" d=\"M128,16a48,48,0,1,0,48,48,48,48,0,0,0-48-48Z\"></path>\n </svg>\n </span>\n <span class=\"avaya-ipo-tab-text\">Keypad</span>\n </button>\n </nav>\n }\n\n @if (activeTab !== 'keypad') {\n <div class=\"avaya-ipo-widget-version-footer\">v{{ widgetVersion }}</div>\n }\n}\n}\n", styles: [":host{display:flex;flex-direction:column;width:100%;max-width:100%;margin:0;font-family:Open Sans,Helvetica Neue,Arial,sans-serif;min-height:100%;height:100%;background:var(--sd-surface);color:var(--sd-text);box-sizing:border-box;--sd-tabs-height: 60px;--sd-radius-sm: 8px;--sd-radius: 10px;--sd-radius-lg: 14px;--sd-radius-pill: 999px;--sd-shadow-sm: 0 1px 2px rgba(16, 24, 40, .06);--sd-shadow-md: 0 6px 16px rgba(16, 24, 40, .1);--sd-shadow-lg: 0 16px 40px rgba(16, 24, 40, .14);--sd-accent: var(--app-color, #ff6633);--sd-accent-strong: var(--app-color-dark, #ff5805);--sd-accent-soft: #fff1ea;--sd-accent-contrast: #ffffff;--sd-call-accept: #1aa251;--sd-call-end: #e5484d;--sd-surface: var(--background-color, #ffffff);--sd-surface-muted: var(--background-color-dark, #f7f7f7);--sd-surface-sunken: var(--background-color-dark-light, #e8e8e8);--sd-text: var(--header-text-primary, #333333);--sd-text-muted: var(--header-text-secondary, #5f6368);--sd-text-subtle: var(--header-text-muted, #9ca3af);--sd-border: var(--dropdown-border, #e3e3e3);--sd-border-strong: var(--border-color, #c9c9c9);--sd-card: var(--background-color, #ffffff);--sd-avatar: #eceff3;--sd-input: var(--background-color, #ffffff);--sd-input-border: var(--border-color, #c9c9c9);--sd-focus-ring: 0 0 0 3px rgba(255, 102, 51, .22)}:host.avaya-ipo-dark-mode{--sd-accent: #ff7a4d;--sd-accent-strong: #ff6633;--sd-accent-soft: rgba(255, 122, 77, .14);--sd-accent-contrast: #1a1109;--sd-call-accept: #34c759;--sd-call-end: #ff5b5b;--sd-surface: #0f1115;--sd-surface-muted: #151a22;--sd-surface-sunken: #1c222c;--sd-text: #f2f4f8;--sd-text-muted: #aab0bb;--sd-text-subtle: #7e8794;--sd-border: #2a2f3a;--sd-border-strong: #353b47;--sd-card: #161b24;--sd-avatar: #222833;--sd-input: #1c222c;--sd-input-border: #2a2f3a;--sd-focus-ring: 0 0 0 3px rgba(255, 122, 77, .25)}:host.avaya-ipo-mode-popup{min-width:320px;height:100%}:host.avaya-ipo-mode-standalone{width:100%;max-width:460px;height:100dvh;min-height:100dvh;margin-inline:auto;border-inline:1px solid var(--sd-border);box-shadow:var(--sd-shadow-lg);overflow:hidden}@media (max-width: 768px){:host.avaya-ipo-mode-standalone{max-width:100%;border-inline:0;box-shadow:none}}.avaya-ipo-container_connection_status_overlay{width:100%;position:relative;background-color:#7f7f7f1a;padding:10px;box-sizing:border-box}.avaya-ipo-container_connection_status{width:100%;background:var(--sd-card);padding:8px 10px;border-radius:var(--sd-radius-sm);font-size:13px;font-weight:600;color:var(--sd-text-muted);display:flex;gap:8px;align-items:center;box-shadow:var(--sd-shadow-md)}.avaya-ipo-container_connection_status .fa-circle-dot{color:var(--sd-accent)}.avaya-ipo-simple-placeholder{padding:20px;text-align:center;color:var(--sd-text);font-weight:600}.avaya-ipo-auth-error-screen,.avaya-ipo-auth-loading-screen{min-height:100%;flex:1 1 auto;display:flex;align-items:center;justify-content:center;padding:24px;box-sizing:border-box;background:var(--sd-surface)}.avaya-ipo-auth-loading-card{width:min(320px,100%);padding:24px;border-radius:var(--sd-radius-lg);background:var(--sd-card);border:1px solid var(--sd-border);box-shadow:var(--sd-shadow-lg);display:flex;flex-direction:column;gap:10px;text-align:center;align-items:center}.avaya-ipo-auth-loading-title{font-size:16px;font-weight:800;color:var(--sd-text)}.avaya-ipo-auth-loading-message{font-size:12px;color:var(--sd-text-muted)}.avaya-ipo-auth-error-card{width:min(360px,100%);padding:24px;border-radius:var(--sd-radius-lg);background:var(--sd-card);border:1px solid var(--sd-border);box-shadow:var(--sd-shadow-lg);display:flex;flex-direction:column;gap:10px;text-align:center}.avaya-ipo-auth-error-icon{width:56px;height:56px;margin:0 auto 4px;display:inline-flex;align-items:center;justify-content:center;border-radius:var(--sd-radius-lg);background:var(--sd-accent);color:var(--sd-accent-contrast)}.avaya-ipo-auth-error-icon svg{width:28px;height:28px}.avaya-ipo-auth-error-title{font-size:18px;font-weight:800;color:var(--sd-text)}.avaya-ipo-auth-error-message{font-size:13px;color:var(--sd-text-muted)}.avaya-ipo-auth-error-hint{font-size:12px;color:var(--sd-text-subtle)}.avaya-ipo-media-elements{display:none}.avaya-ipo-widget-body{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;position:relative;height:100%;padding-bottom:0;overflow-y:auto;-webkit-overflow-scrolling:touch}.avaya-ipo-logout-btn{border:none;background:var(--sd-call-end);color:#fff;padding:8px 12px;border-radius:var(--sd-radius-sm);cursor:pointer;font-weight:600}.avaya-ipo-logout-btn:hover{filter:brightness(.94)}.avaya-ipo-widget-content{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;gap:1rem;padding-bottom:0;height:100%}.avaya-ipo-page-heading{display:flex;align-items:center;justify-content:space-between;font-size:22px;font-weight:800;color:var(--sd-text);padding:15px;z-index:5;background:var(--sd-surface)}.avaya-ipo-settings-btn{width:34px;height:34px;border-radius:var(--sd-radius-sm);border:none;background:transparent;color:var(--sd-text-muted);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;transition:background .12s ease,color .12s ease}.avaya-ipo-settings-btn:hover{background:var(--sd-surface-muted)}.avaya-ipo-settings-btn.avaya-ipo-is-active{color:var(--sd-accent);background:var(--sd-accent-soft)}.avaya-ipo-settings-btn:disabled{cursor:default;opacity:.75}.avaya-ipo-settings-btn-floating{position:absolute;top:8px;right:8px;z-index:6}.avaya-ipo-heading-with-back{display:inline-flex;align-items:center;gap:4px}.avaya-ipo-settings-btn.avaya-ipo-back-btn{color:var(--sd-text)}.avaya-ipo-tabs{position:sticky;bottom:0;left:0;right:0;display:flex;gap:6px;padding:8px 12px;background:var(--sd-surface-muted);border-top:1px solid var(--sd-border);box-sizing:border-box;z-index:10;height:var(--sd-tabs-height)}.avaya-ipo-tab-btn{flex:1 1 0;height:44px;border:none;background:transparent;font-weight:600;color:var(--sd-text-muted);border-radius:var(--sd-radius-sm);cursor:pointer;transition:background .12s ease,color .12s ease;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;font-size:12px;box-shadow:none}.avaya-ipo-tab-btn:hover{color:var(--sd-text)}.avaya-ipo-tab-btn.avaya-ipo-active{background:var(--sd-accent-soft);color:var(--sd-accent);box-shadow:none}.avaya-ipo-tab-icon{display:flex;align-items:center;justify-content:center;font-size:16px}.avaya-ipo-tab-btn.avaya-ipo-active .avaya-ipo-tab-icon{color:var(--sd-accent)}.avaya-ipo-global-loader{position:absolute;inset:0;background:color-mix(in srgb,var(--sd-surface) 85%,transparent);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;z-index:1000;color:var(--sd-text);font-weight:700}.avaya-ipo-spinner{width:48px;height:48px;border:4px solid var(--sd-surface-sunken);border-top-color:var(--sd-accent);border-radius:50%;animation:spin .9s linear infinite}.avaya-ipo-loader-text{font-size:14px}@keyframes spin{to{transform:rotate(360deg)}}:host ::ng-deep .avaya-ipo-widget-body,:host ::ng-deep .avaya-ipo-widget-content{background:var(--sd-surface);color:var(--sd-text)}:host ::ng-deep .avaya-ipo-page-heading{color:var(--sd-text)}:host ::ng-deep .avaya-ipo-settings-btn{color:var(--sd-text-muted)}:host ::ng-deep .avaya-ipo-tabs{background:var(--sd-surface-muted);border-top-color:var(--sd-border)}:host ::ng-deep .avaya-ipo-tab-btn{color:var(--sd-text-muted)}:host ::ng-deep .avaya-ipo-tab-btn.avaya-ipo-active{color:var(--sd-accent);background:var(--sd-accent-soft)}:host ::ng-deep .avaya-ipo-tab-btn.avaya-ipo-active .avaya-ipo-tab-icon{color:var(--sd-accent)}:host ::ng-deep .avaya-ipo-history_item,:host ::ng-deep .avaya-ipo-contact-card{border-bottom-color:var(--sd-border)}:host ::ng-deep .avaya-ipo-history_avatar,:host ::ng-deep .avaya-ipo-skeleton_avatar,:host ::ng-deep .avaya-ipo-contact-avatar{background:var(--sd-avatar);color:var(--sd-text)}:host ::ng-deep .avaya-ipo-history_name,:host ::ng-deep .avaya-ipo-contact-name{color:var(--sd-text)}:host ::ng-deep .avaya-ipo-history_time,:host ::ng-deep .avaya-ipo-history_meta,:host ::ng-deep .avaya-ipo-history_group_label,:host ::ng-deep .avaya-ipo-contact-phone,:host ::ng-deep .avaya-ipo-contacts-state{color:var(--sd-text-muted)}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-history_group_label{background:#151a22eb}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-history_item:hover{background:#1a202b}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-contacts-search input{background:var(--sd-input);border-color:var(--sd-input-border);color:var(--sd-text)}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-contacts-search input::placeholder{color:var(--sd-text-muted)}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-contact-call{background:#1e2b22;color:#79d48a}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-contact-back{background:#141922;border-color:var(--sd-border);color:var(--sd-text)}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-contact-call-btn{background:#1f2530;border-color:#1f2530;color:#79d48a}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-global-loader{background:#0f1115e6;color:var(--sd-text)}:host.avaya-ipo-dark-mode ::ng-deep .avaya-ipo-spinner{border-color:#2a2f3a;border-top-color:var(--sd-accent)}.avaya-ipo-widget-version-footer{flex:0 0 auto;padding:2px 8px 4px;text-align:center;font-size:10px;line-height:1.2;letter-spacing:.02em;color:var(--sd-text-muted);background:var(--sd-surface);border-top:1px solid var(--sd-border);-webkit-user-select:text;user-select:text}\n"] }]
|
|
5001
5360
|
}], ctorParameters: () => [{ type: i1$2.FormBuilder }, { type: AvayaIPOService }, { type: i0.ChangeDetectorRef }, { type: CountryService }, { type: PhoneNumberLookupService }, { type: EntitiesSearchService }, { type: i1$1.SnugdeskAuthenticationService }], propDecorators: { hostDarkMode: [{
|
|
5002
5361
|
type: HostBinding,
|
|
5003
5362
|
args: ['class.avaya-ipo-dark-mode']
|
|
@@ -5013,6 +5372,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImpor
|
|
|
5013
5372
|
type: Input
|
|
5014
5373
|
}], configurationMode: [{
|
|
5015
5374
|
type: Input
|
|
5375
|
+
}], recordingUploadMode: [{
|
|
5376
|
+
type: Input
|
|
5016
5377
|
}], avayaIPOServerIP: [{
|
|
5017
5378
|
type: Input
|
|
5018
5379
|
}], avayaIPOServerPort: [{
|