tas-uell-sdk 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/esm2020/lib/components/tas-btn/tas-btn.component.mjs +2 -13
- package/esm2020/lib/components/tas-feedback-modal/tas-feedback-modal.component.mjs +229 -0
- package/esm2020/lib/components/tas-floating-call/tas-floating-call.component.mjs +38 -3
- package/esm2020/lib/components/tas-incoming-appointment/tas-incoming-appointment.component.mjs +46 -8
- package/esm2020/lib/components/tas-videocall/tas-videocall.component.mjs +41 -6
- package/esm2020/lib/components/tas-waiting-room/tas-waiting-room.component.mjs +18 -3
- package/esm2020/lib/interfaces/tas.interfaces.mjs +6 -1
- package/esm2020/lib/services/tas.service.mjs +40 -1
- package/esm2020/lib/tas-uell-sdk.module.mjs +8 -3
- package/esm2020/public-api.mjs +2 -1
- package/fesm2015/tas-uell-sdk.mjs +421 -36
- package/fesm2015/tas-uell-sdk.mjs.map +1 -1
- package/fesm2020/tas-uell-sdk.mjs +416 -36
- package/fesm2020/tas-uell-sdk.mjs.map +1 -1
- package/lib/components/tas-btn/tas-btn.component.d.ts +1 -2
- package/lib/components/tas-feedback-modal/tas-feedback-modal.component.d.ts +101 -0
- package/lib/components/tas-floating-call/tas-floating-call.component.d.ts +5 -0
- package/lib/components/tas-incoming-appointment/tas-incoming-appointment.component.d.ts +12 -1
- package/lib/components/tas-videocall/tas-videocall.component.d.ts +8 -2
- package/lib/components/tas-waiting-room/tas-waiting-room.component.d.ts +9 -0
- package/lib/interfaces/tas.interfaces.d.ts +17 -0
- package/lib/services/tas.service.d.ts +15 -1
- package/lib/tas-uell-sdk.module.d.ts +5 -4
- package/package.json +1 -1
- package/public-api.d.ts +1 -0
- package/src/lib/styles/tas-global.scss +17 -0
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, Injectable, Inject, Component,
|
|
2
|
+
import { InjectionToken, Injectable, Inject, Component, Input, ChangeDetectionStrategy, ViewChild, EventEmitter, Output, NgModule } from '@angular/core';
|
|
3
3
|
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
|
|
4
4
|
import { map, catchError, finalize, shareReplay } from 'rxjs/operators';
|
|
5
5
|
import * as OT from '@opentok/client';
|
|
6
6
|
import { __awaiter } from 'tslib';
|
|
7
|
-
import interact from 'interactjs';
|
|
8
7
|
import * as i1 from '@ng-bootstrap/ng-bootstrap';
|
|
9
8
|
import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
|
|
10
|
-
import * as i4 from '@angular/
|
|
11
|
-
import * as i4$1 from '@angular/common';
|
|
9
|
+
import * as i4 from '@angular/common';
|
|
12
10
|
import { CommonModule } from '@angular/common';
|
|
11
|
+
import * as i4$1 from '@angular/forms';
|
|
13
12
|
import { FormsModule } from '@angular/forms';
|
|
13
|
+
import interact from 'interactjs';
|
|
14
|
+
import * as i4$2 from '@angular/platform-browser';
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Injection token for TAS configuration
|
|
@@ -96,6 +97,11 @@ var ViewMode;
|
|
|
96
97
|
ViewMode["FULLSCREEN"] = "FULLSCREEN";
|
|
97
98
|
ViewMode["PIP"] = "PIP";
|
|
98
99
|
})(ViewMode || (ViewMode = {}));
|
|
100
|
+
var FeedbackMotiveType;
|
|
101
|
+
(function (FeedbackMotiveType) {
|
|
102
|
+
FeedbackMotiveType["TECHNICAL"] = "TECHNICAL";
|
|
103
|
+
FeedbackMotiveType["BUSINESS"] = "BUSINESS";
|
|
104
|
+
})(FeedbackMotiveType || (FeedbackMotiveType = {}));
|
|
99
105
|
// Appointment types
|
|
100
106
|
var AppointmentStatus;
|
|
101
107
|
(function (AppointmentStatus) {
|
|
@@ -581,6 +587,42 @@ class TasService {
|
|
|
581
587
|
throw error;
|
|
582
588
|
}));
|
|
583
589
|
}
|
|
590
|
+
/**
|
|
591
|
+
* Save video call feedback.
|
|
592
|
+
* POST /v2/proxy/video/save/feedback
|
|
593
|
+
* @param payload Feedback data including videoCallId, motiveId, motiveType, observation, rating (1-5), and tenant
|
|
594
|
+
* @returns Observable that completes on success (HTTP 200 OK, no response body)
|
|
595
|
+
*/
|
|
596
|
+
saveFeedback(payload) {
|
|
597
|
+
// Validate rating is between 1 and 5
|
|
598
|
+
if (payload.rating < 1 || payload.rating > 5) {
|
|
599
|
+
return new Observable(observer => {
|
|
600
|
+
observer.error(new Error('Rating must be between 1 and 5'));
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
return this.httpClient
|
|
604
|
+
.post('v2/proxy/video/save/feedback', {
|
|
605
|
+
body: payload,
|
|
606
|
+
headers: {},
|
|
607
|
+
})
|
|
608
|
+
.pipe(catchError((error) => {
|
|
609
|
+
console.error('TAS Service: saveFeedback failed', error);
|
|
610
|
+
throw error;
|
|
611
|
+
}));
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* Get available feedback motives.
|
|
615
|
+
* GET /v2/proxy/video/motives
|
|
616
|
+
* @returns Observable of feedback motives array
|
|
617
|
+
*/
|
|
618
|
+
getMotives() {
|
|
619
|
+
return this.httpClient
|
|
620
|
+
.get('v2/proxy/video/motives', { headers: {} })
|
|
621
|
+
.pipe(catchError((error) => {
|
|
622
|
+
console.error('TAS Service: getMotives failed', error);
|
|
623
|
+
throw error;
|
|
624
|
+
}));
|
|
625
|
+
}
|
|
584
626
|
/**
|
|
585
627
|
* Start automatic status polling for the current session.
|
|
586
628
|
* Status is polled every STATUS_POLL_INTERVAL_MS milliseconds.
|
|
@@ -679,6 +721,9 @@ class TasService {
|
|
|
679
721
|
get videoCallId() {
|
|
680
722
|
return this.currentVideoCallId;
|
|
681
723
|
}
|
|
724
|
+
get tenant() {
|
|
725
|
+
return this.currentTenant;
|
|
726
|
+
}
|
|
682
727
|
/**
|
|
683
728
|
* Connects to a TokBox video session
|
|
684
729
|
*/
|
|
@@ -898,6 +943,229 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.12", ngImpo
|
|
|
898
943
|
}]
|
|
899
944
|
}] });
|
|
900
945
|
|
|
946
|
+
class TasFeedbackModalComponent {
|
|
947
|
+
constructor(activeModal, tasService) {
|
|
948
|
+
this.activeModal = activeModal;
|
|
949
|
+
this.tasService = tasService;
|
|
950
|
+
this.businessRole = TasBusinessRole.USER;
|
|
951
|
+
this.motives = [];
|
|
952
|
+
this.filteredMotives = [];
|
|
953
|
+
this.selectedMotive = null;
|
|
954
|
+
this.rating = 0;
|
|
955
|
+
this.hoverRating = 0;
|
|
956
|
+
this.observation = '';
|
|
957
|
+
this.isSubmitting = false;
|
|
958
|
+
this.showToast = false;
|
|
959
|
+
this.isDropdownOpen = false;
|
|
960
|
+
this.subscriptions = new Subscription();
|
|
961
|
+
this.toastTimeout = null;
|
|
962
|
+
}
|
|
963
|
+
ngOnInit() {
|
|
964
|
+
this.loadMotives();
|
|
965
|
+
}
|
|
966
|
+
ngOnDestroy() {
|
|
967
|
+
this.subscriptions.unsubscribe();
|
|
968
|
+
if (this.toastTimeout) {
|
|
969
|
+
clearTimeout(this.toastTimeout);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
/**
|
|
973
|
+
* Check if current user can see all motives (BUSINESS + TECHNICAL)
|
|
974
|
+
* Only BACKOFFICE, ADMIN_MANAGER, MANAGER roles see BUSINESS motives
|
|
975
|
+
*/
|
|
976
|
+
get canSeeBusinessMotives() {
|
|
977
|
+
return (this.businessRole === TasBusinessRole.BACKOFFICE ||
|
|
978
|
+
this.businessRole === TasBusinessRole.ADMIN_MANAGER ||
|
|
979
|
+
this.businessRole === TasBusinessRole.MANAGER);
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Observation is required when "Otro problema tecnico" is selected
|
|
983
|
+
*/
|
|
984
|
+
get isObservationRequired() {
|
|
985
|
+
var _a;
|
|
986
|
+
return ((_a = this.selectedMotive) === null || _a === void 0 ? void 0 : _a.description) === 'Otro problema tecnico';
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* Can submit if rating > 0 OR motive is selected
|
|
990
|
+
* If observation is required, it must not be empty
|
|
991
|
+
*/
|
|
992
|
+
get canSubmit() {
|
|
993
|
+
const hasRatingOrMotive = this.rating > 0 || this.selectedMotive !== null;
|
|
994
|
+
if (!hasRatingOrMotive)
|
|
995
|
+
return false;
|
|
996
|
+
if (this.isObservationRequired && !this.observation.trim())
|
|
997
|
+
return false;
|
|
998
|
+
return true;
|
|
999
|
+
}
|
|
1000
|
+
/**
|
|
1001
|
+
* Progress percentage for the divider line
|
|
1002
|
+
* 0 = nothing selected, 50 = one selected, 100 = both selected
|
|
1003
|
+
*/
|
|
1004
|
+
get feedbackProgress() {
|
|
1005
|
+
const hasRating = this.rating > 0;
|
|
1006
|
+
const hasMotive = this.selectedMotive !== null;
|
|
1007
|
+
if (hasRating && hasMotive)
|
|
1008
|
+
return 100;
|
|
1009
|
+
if (hasRating || hasMotive)
|
|
1010
|
+
return 50;
|
|
1011
|
+
return 0;
|
|
1012
|
+
}
|
|
1013
|
+
/**
|
|
1014
|
+
* Set star rating
|
|
1015
|
+
*/
|
|
1016
|
+
setRating(value) {
|
|
1017
|
+
// Toggle off if clicking the same rating
|
|
1018
|
+
if (this.rating === value) {
|
|
1019
|
+
this.rating = 0;
|
|
1020
|
+
}
|
|
1021
|
+
else {
|
|
1022
|
+
this.rating = value;
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
/**
|
|
1026
|
+
* Set hover preview rating
|
|
1027
|
+
*/
|
|
1028
|
+
setHoverRating(value) {
|
|
1029
|
+
this.hoverRating = value;
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
1032
|
+
* Clear hover preview
|
|
1033
|
+
*/
|
|
1034
|
+
clearHoverRating() {
|
|
1035
|
+
this.hoverRating = 0;
|
|
1036
|
+
}
|
|
1037
|
+
/**
|
|
1038
|
+
* Get the display rating (hover preview or actual)
|
|
1039
|
+
*/
|
|
1040
|
+
getDisplayRating() {
|
|
1041
|
+
return this.hoverRating > 0 ? this.hoverRating : this.rating;
|
|
1042
|
+
}
|
|
1043
|
+
/**
|
|
1044
|
+
* Toggle dropdown open/close
|
|
1045
|
+
*/
|
|
1046
|
+
toggleDropdown() {
|
|
1047
|
+
this.isDropdownOpen = !this.isDropdownOpen;
|
|
1048
|
+
}
|
|
1049
|
+
/**
|
|
1050
|
+
* Close dropdown
|
|
1051
|
+
*/
|
|
1052
|
+
closeDropdown() {
|
|
1053
|
+
this.isDropdownOpen = false;
|
|
1054
|
+
}
|
|
1055
|
+
/**
|
|
1056
|
+
* Select a motive from dropdown
|
|
1057
|
+
*/
|
|
1058
|
+
selectMotive(motive) {
|
|
1059
|
+
this.selectedMotive = motive;
|
|
1060
|
+
this.closeDropdown();
|
|
1061
|
+
}
|
|
1062
|
+
/**
|
|
1063
|
+
* Clear selected motive
|
|
1064
|
+
*/
|
|
1065
|
+
clearMotive() {
|
|
1066
|
+
this.selectedMotive = null;
|
|
1067
|
+
}
|
|
1068
|
+
/**
|
|
1069
|
+
* Submit feedback
|
|
1070
|
+
*/
|
|
1071
|
+
submit() {
|
|
1072
|
+
var _a, _b;
|
|
1073
|
+
if (!this.canSubmit || this.isSubmitting)
|
|
1074
|
+
return;
|
|
1075
|
+
this.isSubmitting = true;
|
|
1076
|
+
const payload = {
|
|
1077
|
+
videoCallId: this.videoCallId,
|
|
1078
|
+
motiveId: (_b = (_a = this.selectedMotive) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : 0,
|
|
1079
|
+
observation: this.observation.trim(),
|
|
1080
|
+
rating: this.rating || 1,
|
|
1081
|
+
tenant: this.tenant,
|
|
1082
|
+
};
|
|
1083
|
+
if (this.selectedMotive) {
|
|
1084
|
+
payload.motiveType = this.selectedMotive.motiveType;
|
|
1085
|
+
}
|
|
1086
|
+
this.subscriptions.add(this.tasService.saveFeedback(payload).subscribe({
|
|
1087
|
+
next: () => {
|
|
1088
|
+
this.isSubmitting = false;
|
|
1089
|
+
this.showToastNotification();
|
|
1090
|
+
},
|
|
1091
|
+
error: (err) => {
|
|
1092
|
+
console.error('Error saving feedback:', err);
|
|
1093
|
+
this.isSubmitting = false;
|
|
1094
|
+
// Still close modal on error, just don't show toast
|
|
1095
|
+
this.activeModal.close('submitted');
|
|
1096
|
+
},
|
|
1097
|
+
}));
|
|
1098
|
+
}
|
|
1099
|
+
/**
|
|
1100
|
+
* Close modal without saving
|
|
1101
|
+
*/
|
|
1102
|
+
dismiss() {
|
|
1103
|
+
this.activeModal.dismiss('dismissed');
|
|
1104
|
+
}
|
|
1105
|
+
/**
|
|
1106
|
+
* Load motives from API
|
|
1107
|
+
*/
|
|
1108
|
+
loadMotives() {
|
|
1109
|
+
this.subscriptions.add(this.tasService.getMotives().subscribe({
|
|
1110
|
+
next: (motives) => {
|
|
1111
|
+
this.motives = motives;
|
|
1112
|
+
this.filterMotives();
|
|
1113
|
+
},
|
|
1114
|
+
error: (err) => {
|
|
1115
|
+
console.error('Error loading motives:', err);
|
|
1116
|
+
// Fallback mock motives for development/testing
|
|
1117
|
+
this.motives = [
|
|
1118
|
+
{ id: 1, description: 'Problemas de audio', motiveType: FeedbackMotiveType.TECHNICAL },
|
|
1119
|
+
{ id: 2, description: 'Problemas de video', motiveType: FeedbackMotiveType.TECHNICAL },
|
|
1120
|
+
{ id: 3, description: 'Conexión inestable', motiveType: FeedbackMotiveType.TECHNICAL },
|
|
1121
|
+
{ id: 4, description: 'Otro problema tecnico', motiveType: FeedbackMotiveType.TECHNICAL },
|
|
1122
|
+
{ id: 5, description: 'Problema de negocio', motiveType: FeedbackMotiveType.BUSINESS },
|
|
1123
|
+
];
|
|
1124
|
+
this.filterMotives();
|
|
1125
|
+
},
|
|
1126
|
+
}));
|
|
1127
|
+
}
|
|
1128
|
+
/**
|
|
1129
|
+
* Filter motives based on user role
|
|
1130
|
+
* USERs see only TECHNICAL motives
|
|
1131
|
+
* Owners (BACKOFFICE, ADMIN_MANAGER, MANAGER) see all motives
|
|
1132
|
+
*/
|
|
1133
|
+
filterMotives() {
|
|
1134
|
+
if (this.canSeeBusinessMotives) {
|
|
1135
|
+
this.filteredMotives = this.motives;
|
|
1136
|
+
}
|
|
1137
|
+
else {
|
|
1138
|
+
this.filteredMotives = this.motives.filter((m) => !m.motiveType || m.motiveType === FeedbackMotiveType.TECHNICAL);
|
|
1139
|
+
}
|
|
1140
|
+
// If still empty after filtering, and we have motives, just show all as fallback
|
|
1141
|
+
if (this.filteredMotives.length === 0 && this.motives.length > 0) {
|
|
1142
|
+
this.filteredMotives = this.motives;
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Show toast notification and auto-dismiss after 3s
|
|
1147
|
+
*/
|
|
1148
|
+
showToastNotification() {
|
|
1149
|
+
this.showToast = true;
|
|
1150
|
+
this.toastTimeout = setTimeout(() => {
|
|
1151
|
+
this.showToast = false;
|
|
1152
|
+
this.activeModal.close('submitted');
|
|
1153
|
+
}, 3000);
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
TasFeedbackModalComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.12", ngImport: i0, type: TasFeedbackModalComponent, deps: [{ token: i1.NgbActiveModal }, { token: TasService }], target: i0.ɵɵFactoryTarget.Component });
|
|
1157
|
+
TasFeedbackModalComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.12", type: TasFeedbackModalComponent, selector: "tas-feedback-modal", inputs: { videoCallId: "videoCallId", tenant: "tenant", businessRole: "businessRole" }, ngImport: i0, template: "<div class=\"tas-feedback-modal\">\n <!-- Header with subtitle -->\n <div class=\"feedback-header\">\n <div class=\"header-text\">\n <span class=\"subtitle\">Cierre de la consulta</span>\n <h2>Calidad de la videollamada</h2>\n </div>\n <button\n type=\"button\"\n class=\"close-btn\"\n (click)=\"dismiss()\"\n aria-label=\"Cerrar\"\n >\n <i class=\"fa fa-times\"></i>\n </button>\n </div>\n\n <!-- Progress Divider -->\n <div class=\"progress-divider\">\n <div class=\"progress-fill\" [style.width.%]=\"feedbackProgress\"></div>\n </div>\n <div class=\"accent-line\"></div>\n\n <div class=\"feedback-content\">\n <!-- Star Rating with question -->\n <div class=\"rating-section\">\n <p class=\"rating-question\">\u00BFC\u00F3mo fue la calidad de la llamada?</p>\n <div\n class=\"stars-container\"\n (mouseleave)=\"clearHoverRating()\"\n >\n <button\n *ngFor=\"let star of [1, 2, 3, 4, 5]\"\n type=\"button\"\n class=\"star-btn\"\n [class.filled]=\"star <= getDisplayRating()\"\n (click)=\"setRating(star)\"\n (mouseenter)=\"setHoverRating(star)\"\n [attr.aria-label]=\"'Calificar ' + star + ' estrellas'\"\n >\n <i class=\"fa\" [class.fa-star]=\"star <= getDisplayRating()\" [class.fa-star-o]=\"star > getDisplayRating()\"></i>\n </button>\n </div>\n </div>\n\n <!-- Motive Dropdown -->\n <div class=\"motive-section\">\n <label class=\"motive-label\">Motivo (opcional)</label>\n <div class=\"custom-dropdown\" [class.open]=\"isDropdownOpen\">\n <button\n type=\"button\"\n class=\"dropdown-trigger\"\n (click)=\"toggleDropdown()\"\n [attr.aria-expanded]=\"isDropdownOpen\"\n aria-haspopup=\"listbox\"\n >\n <span class=\"dropdown-text\">\n {{ selectedMotive?.description || 'Seleccionar motivo' }}\n </span>\n <i class=\"fa fa-chevron-down dropdown-arrow\"></i>\n </button>\n <div\n class=\"dropdown-menu\"\n *ngIf=\"isDropdownOpen\"\n role=\"listbox\"\n >\n <button\n *ngFor=\"let motive of filteredMotives\"\n type=\"button\"\n class=\"dropdown-item\"\n [class.selected]=\"selectedMotive?.id === motive.id\"\n (click)=\"selectMotive(motive)\"\n role=\"option\"\n [attr.aria-selected]=\"selectedMotive?.id === motive.id\"\n >\n {{ motive.description }}\n </button>\n </div>\n </div>\n </div>\n\n <!-- Observation Textarea -->\n <div class=\"observation-section\">\n <label class=\"observation-label\" [class.required]=\"isObservationRequired\">\n {{ isObservationRequired ? 'Observacion (requerida)' : 'Observacion (opcional)' }}\n </label>\n <textarea\n class=\"observation-textarea\"\n [(ngModel)]=\"observation\"\n [placeholder]=\"isObservationRequired ? 'Por favor, describe el problema...' : 'Escribe tu comentario...'\"\n rows=\"3\"\n [attr.aria-required]=\"isObservationRequired\"\n ></textarea>\n </div>\n </div>\n\n <div class=\"feedback-footer\">\n <button\n type=\"button\"\n class=\"submit-btn\"\n [disabled]=\"!canSubmit || isSubmitting\"\n (click)=\"submit()\"\n >\n <span *ngIf=\"!isSubmitting\">Enviar</span>\n <span *ngIf=\"isSubmitting\">\n <i class=\"fa fa-spinner fa-spin\"></i>\n Enviando...\n </span>\n </button>\n </div>\n\n <!-- Toast Notification -->\n <div class=\"toast-notification\" *ngIf=\"showToast\" role=\"alert\" aria-live=\"polite\">\n <i class=\"fa fa-check-circle\"></i>\n <span>Gracias por tu feedback</span>\n </div>\n</div>\n\n<!-- Backdrop to close dropdown when clicking outside -->\n<div\n class=\"dropdown-backdrop\"\n *ngIf=\"isDropdownOpen\"\n (click)=\"closeDropdown()\"\n></div>\n", styles: [":host{display:block;width:100%}::ng-deep .tas-feedback-modal-wrapper .modal-content{overflow:visible!important;border:none;background:transparent}::ng-deep .tas-feedback-modal-wrapper .modal-dialog{max-width:450px}.tas-feedback-modal{width:100%;background:#fff;border-radius:16px;overflow:visible;position:relative;padding:.5rem;box-shadow:0 20px 25px -5px #0000001a,0 8px 10px -6px #0000001a}.feedback-header{display:flex;justify-content:space-between;align-items:flex-start;padding:1.25rem 1.5rem 1rem;background:#fff;border-radius:16px 16px 0 0}.feedback-header .header-text{display:flex;flex-direction:column;gap:.25rem}.feedback-header .header-text .subtitle{font-size:.8125rem;color:#6b7280;font-weight:400}.feedback-header .header-text h2{margin:0;font-size:1.25rem;font-weight:600;color:#1f2937;letter-spacing:-.01em}.feedback-header .close-btn{background:transparent;border:none;padding:8px;cursor:pointer;color:#9ca3af;font-size:1.25rem;line-height:1;transition:all .2s ease;margin-top:-4px}.feedback-header .close-btn:hover{color:#4b5563}.feedback-header .close-btn:focus{outline:none;color:#4b5563}.progress-divider{height:4px;background:#e5e7eb;margin:0;position:relative;overflow:hidden}.progress-divider .progress-fill{height:100%;background:linear-gradient(90deg,var(--Primary-Uell, #1da4b1) 0%,#4fd1c5 100%);transition:width .4s cubic-bezier(.4,0,.2,1);border-radius:0 2px 2px 0}.feedback-content{padding:1.5rem;display:flex;flex-direction:column;gap:1.5rem}.rating-section{display:flex;flex-direction:column;align-items:flex-start;gap:1rem}.rating-section .rating-question{margin:0;font-size:.9375rem;color:#4b5563;font-weight:400;align-self:center}.rating-section .stars-container{display:flex;gap:8px;width:100%;justify-content:center}.rating-section .star-btn{background:transparent;border:none;padding:4px;cursor:pointer;font-size:2rem;color:#d1d5db;transition:all .15s ease;line-height:1}.rating-section .star-btn:hover{transform:scale(1.15)}.rating-section .star-btn.filled{color:#f5a623}.rating-section .star-btn.filled i.fa-star{text-shadow:0 2px 4px rgba(245,166,35,.3)}.rating-section .star-btn i.fa-star-o{color:#d1d5db}.rating-section .star-btn:focus{outline:none}.motive-section{display:flex;flex-direction:column;gap:.5rem;position:relative;overflow:visible}.motive-section .motive-label{font-size:.875rem;font-weight:500;color:#4b5563}.motive-section .custom-dropdown{position:relative;overflow:visible}.motive-section .custom-dropdown .dropdown-trigger{width:100%;display:flex;justify-content:space-between;align-items:center;padding:1rem;background:#fff;border:1px solid #d1d5db;border-radius:8px;cursor:pointer;font-size:.9375rem;color:#4b5563;transition:all .2s ease;min-height:52px}.motive-section .custom-dropdown .dropdown-trigger:hover{border-color:#9ca3af}.motive-section .custom-dropdown .dropdown-trigger:focus{outline:none;border-color:var(--Primary-Uell, #1da4b1)}.motive-section .custom-dropdown .dropdown-trigger .dropdown-text{flex:1;text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.motive-section .custom-dropdown .dropdown-trigger .dropdown-arrow{font-size:.875rem;color:#6b7280;transition:transform .2s ease;margin-left:.5rem}.motive-section .custom-dropdown.open .dropdown-trigger{border-color:var(--Primary-Uell, #1da4b1)}.motive-section .custom-dropdown.open .dropdown-trigger .dropdown-arrow{transform:rotate(180deg)}.motive-section .custom-dropdown .dropdown-menu{position:absolute;top:calc(100% + 4px);left:0;right:0;display:block;min-height:40px;min-width:100%;background:#fff;border:1px solid #e5e7eb;border-radius:8px;box-shadow:0 4px 16px #0000001f;max-height:250px;overflow:hidden;overflow-y:auto;z-index:1050;visibility:visible;opacity:1;padding:0}.motive-section .custom-dropdown .dropdown-menu .dropdown-item{width:100%;padding:1rem 1.25rem;background:transparent;border:none;border-bottom:1px dashed #e5e7eb;text-align:left;font-size:.9375rem;color:#4b5563;cursor:pointer;transition:all .15s ease;position:relative}.motive-section .custom-dropdown .dropdown-menu .dropdown-item:last-child{border-bottom:none}.motive-section .custom-dropdown .dropdown-menu .dropdown-item:hover{background:rgba(29,164,177,.05);color:#374151}.motive-section .custom-dropdown .dropdown-menu .dropdown-item.selected{background:rgba(29,164,177,.08);color:var(--Primary-Uell, #1da4b1);border-left:3px solid var(--Primary-Uell, #1da4b1);padding-left:calc(1.25rem - 3px)}.motive-section .custom-dropdown .dropdown-menu .dropdown-item:focus{outline:none;background:rgba(29,164,177,.05)}.motive-section .custom-dropdown .dropdown-menu .dropdown-item:first-child{border-radius:8px 8px 0 0}.motive-section .custom-dropdown .dropdown-menu .dropdown-item:last-child{border-radius:0 0 8px 8px}.motive-section .custom-dropdown .dropdown-menu .dropdown-item:only-child{border-radius:8px}.motive-section .clear-motive-btn{position:absolute;right:40px;top:32px;background:#f3f4f6;border:none;padding:4px 6px;border-radius:4px;cursor:pointer;color:#6b7280;font-size:.625rem;transition:all .2s ease}.motive-section .clear-motive-btn:hover{background:#e5e7eb;color:#374151}.motive-section .clear-motive-btn:focus{outline:none}.observation-section{display:flex;flex-direction:column;gap:.5rem}.observation-section .observation-label{font-size:.875rem;font-weight:500;color:#4b5563}.observation-section .observation-label.required{color:#dc2626}.observation-section .observation-label.required:after{content:\" *\";font-weight:600}.observation-section .observation-textarea{width:100%;padding:.75rem 1rem;border:1px solid #d1d5db;border-radius:12px;font-size:.875rem;font-family:inherit;resize:vertical;min-height:80px;transition:all .2s ease;background:#fff}.observation-section .observation-textarea::placeholder{color:#9ca3af}.observation-section .observation-textarea:hover{border-color:#9ca3af}.observation-section .observation-textarea:focus{outline:none;border-color:var(--Primary-Uell, #1da4b1);box-shadow:0 0 0 3px #1da4b126}.feedback-footer{padding:1rem 1.5rem 1.5rem;display:flex;justify-content:flex-end}.feedback-footer .submit-btn{padding:.75rem 2rem;background:var(--Primary-Uell, #1da4b1);color:#fff;border:none;border-radius:24px;font-size:.9375rem;font-weight:600;cursor:pointer;transition:all .2s ease;min-width:120px}.feedback-footer .submit-btn:hover:not(:disabled){background:#178e99}.feedback-footer .submit-btn:disabled{background:rgba(29,164,177,.5);cursor:not-allowed}.feedback-footer .submit-btn:focus{outline:none;box-shadow:0 0 0 3px #1da4b126}.feedback-footer .submit-btn i.fa-spinner{margin-right:6px}.toast-notification{position:fixed;bottom:24px;left:24px;display:flex;align-items:center;gap:10px;padding:14px 20px;background:#383e52;color:#fff;border-radius:8px;box-shadow:0 4px 12px #00000040;font-size:.875rem;font-weight:500;z-index:1060;animation:slideIn .3s ease}.toast-notification i.fa-check-circle{color:#4ade80;font-size:1.125rem}@keyframes slideIn{0%{transform:translate(-100%);opacity:0}to{transform:translate(0);opacity:1}}.dropdown-backdrop{position:fixed;inset:0;z-index:99}\n"], directives: [{ type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i4$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { type: i4$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { type: i4$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
|
|
1158
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.12", ngImport: i0, type: TasFeedbackModalComponent, decorators: [{
|
|
1159
|
+
type: Component,
|
|
1160
|
+
args: [{ selector: 'tas-feedback-modal', template: "<div class=\"tas-feedback-modal\">\n <!-- Header with subtitle -->\n <div class=\"feedback-header\">\n <div class=\"header-text\">\n <span class=\"subtitle\">Cierre de la consulta</span>\n <h2>Calidad de la videollamada</h2>\n </div>\n <button\n type=\"button\"\n class=\"close-btn\"\n (click)=\"dismiss()\"\n aria-label=\"Cerrar\"\n >\n <i class=\"fa fa-times\"></i>\n </button>\n </div>\n\n <!-- Progress Divider -->\n <div class=\"progress-divider\">\n <div class=\"progress-fill\" [style.width.%]=\"feedbackProgress\"></div>\n </div>\n <div class=\"accent-line\"></div>\n\n <div class=\"feedback-content\">\n <!-- Star Rating with question -->\n <div class=\"rating-section\">\n <p class=\"rating-question\">\u00BFC\u00F3mo fue la calidad de la llamada?</p>\n <div\n class=\"stars-container\"\n (mouseleave)=\"clearHoverRating()\"\n >\n <button\n *ngFor=\"let star of [1, 2, 3, 4, 5]\"\n type=\"button\"\n class=\"star-btn\"\n [class.filled]=\"star <= getDisplayRating()\"\n (click)=\"setRating(star)\"\n (mouseenter)=\"setHoverRating(star)\"\n [attr.aria-label]=\"'Calificar ' + star + ' estrellas'\"\n >\n <i class=\"fa\" [class.fa-star]=\"star <= getDisplayRating()\" [class.fa-star-o]=\"star > getDisplayRating()\"></i>\n </button>\n </div>\n </div>\n\n <!-- Motive Dropdown -->\n <div class=\"motive-section\">\n <label class=\"motive-label\">Motivo (opcional)</label>\n <div class=\"custom-dropdown\" [class.open]=\"isDropdownOpen\">\n <button\n type=\"button\"\n class=\"dropdown-trigger\"\n (click)=\"toggleDropdown()\"\n [attr.aria-expanded]=\"isDropdownOpen\"\n aria-haspopup=\"listbox\"\n >\n <span class=\"dropdown-text\">\n {{ selectedMotive?.description || 'Seleccionar motivo' }}\n </span>\n <i class=\"fa fa-chevron-down dropdown-arrow\"></i>\n </button>\n <div\n class=\"dropdown-menu\"\n *ngIf=\"isDropdownOpen\"\n role=\"listbox\"\n >\n <button\n *ngFor=\"let motive of filteredMotives\"\n type=\"button\"\n class=\"dropdown-item\"\n [class.selected]=\"selectedMotive?.id === motive.id\"\n (click)=\"selectMotive(motive)\"\n role=\"option\"\n [attr.aria-selected]=\"selectedMotive?.id === motive.id\"\n >\n {{ motive.description }}\n </button>\n </div>\n </div>\n </div>\n\n <!-- Observation Textarea -->\n <div class=\"observation-section\">\n <label class=\"observation-label\" [class.required]=\"isObservationRequired\">\n {{ isObservationRequired ? 'Observacion (requerida)' : 'Observacion (opcional)' }}\n </label>\n <textarea\n class=\"observation-textarea\"\n [(ngModel)]=\"observation\"\n [placeholder]=\"isObservationRequired ? 'Por favor, describe el problema...' : 'Escribe tu comentario...'\"\n rows=\"3\"\n [attr.aria-required]=\"isObservationRequired\"\n ></textarea>\n </div>\n </div>\n\n <div class=\"feedback-footer\">\n <button\n type=\"button\"\n class=\"submit-btn\"\n [disabled]=\"!canSubmit || isSubmitting\"\n (click)=\"submit()\"\n >\n <span *ngIf=\"!isSubmitting\">Enviar</span>\n <span *ngIf=\"isSubmitting\">\n <i class=\"fa fa-spinner fa-spin\"></i>\n Enviando...\n </span>\n </button>\n </div>\n\n <!-- Toast Notification -->\n <div class=\"toast-notification\" *ngIf=\"showToast\" role=\"alert\" aria-live=\"polite\">\n <i class=\"fa fa-check-circle\"></i>\n <span>Gracias por tu feedback</span>\n </div>\n</div>\n\n<!-- Backdrop to close dropdown when clicking outside -->\n<div\n class=\"dropdown-backdrop\"\n *ngIf=\"isDropdownOpen\"\n (click)=\"closeDropdown()\"\n></div>\n", styles: [":host{display:block;width:100%}::ng-deep .tas-feedback-modal-wrapper .modal-content{overflow:visible!important;border:none;background:transparent}::ng-deep .tas-feedback-modal-wrapper .modal-dialog{max-width:450px}.tas-feedback-modal{width:100%;background:#fff;border-radius:16px;overflow:visible;position:relative;padding:.5rem;box-shadow:0 20px 25px -5px #0000001a,0 8px 10px -6px #0000001a}.feedback-header{display:flex;justify-content:space-between;align-items:flex-start;padding:1.25rem 1.5rem 1rem;background:#fff;border-radius:16px 16px 0 0}.feedback-header .header-text{display:flex;flex-direction:column;gap:.25rem}.feedback-header .header-text .subtitle{font-size:.8125rem;color:#6b7280;font-weight:400}.feedback-header .header-text h2{margin:0;font-size:1.25rem;font-weight:600;color:#1f2937;letter-spacing:-.01em}.feedback-header .close-btn{background:transparent;border:none;padding:8px;cursor:pointer;color:#9ca3af;font-size:1.25rem;line-height:1;transition:all .2s ease;margin-top:-4px}.feedback-header .close-btn:hover{color:#4b5563}.feedback-header .close-btn:focus{outline:none;color:#4b5563}.progress-divider{height:4px;background:#e5e7eb;margin:0;position:relative;overflow:hidden}.progress-divider .progress-fill{height:100%;background:linear-gradient(90deg,var(--Primary-Uell, #1da4b1) 0%,#4fd1c5 100%);transition:width .4s cubic-bezier(.4,0,.2,1);border-radius:0 2px 2px 0}.feedback-content{padding:1.5rem;display:flex;flex-direction:column;gap:1.5rem}.rating-section{display:flex;flex-direction:column;align-items:flex-start;gap:1rem}.rating-section .rating-question{margin:0;font-size:.9375rem;color:#4b5563;font-weight:400;align-self:center}.rating-section .stars-container{display:flex;gap:8px;width:100%;justify-content:center}.rating-section .star-btn{background:transparent;border:none;padding:4px;cursor:pointer;font-size:2rem;color:#d1d5db;transition:all .15s ease;line-height:1}.rating-section .star-btn:hover{transform:scale(1.15)}.rating-section .star-btn.filled{color:#f5a623}.rating-section .star-btn.filled i.fa-star{text-shadow:0 2px 4px rgba(245,166,35,.3)}.rating-section .star-btn i.fa-star-o{color:#d1d5db}.rating-section .star-btn:focus{outline:none}.motive-section{display:flex;flex-direction:column;gap:.5rem;position:relative;overflow:visible}.motive-section .motive-label{font-size:.875rem;font-weight:500;color:#4b5563}.motive-section .custom-dropdown{position:relative;overflow:visible}.motive-section .custom-dropdown .dropdown-trigger{width:100%;display:flex;justify-content:space-between;align-items:center;padding:1rem;background:#fff;border:1px solid #d1d5db;border-radius:8px;cursor:pointer;font-size:.9375rem;color:#4b5563;transition:all .2s ease;min-height:52px}.motive-section .custom-dropdown .dropdown-trigger:hover{border-color:#9ca3af}.motive-section .custom-dropdown .dropdown-trigger:focus{outline:none;border-color:var(--Primary-Uell, #1da4b1)}.motive-section .custom-dropdown .dropdown-trigger .dropdown-text{flex:1;text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.motive-section .custom-dropdown .dropdown-trigger .dropdown-arrow{font-size:.875rem;color:#6b7280;transition:transform .2s ease;margin-left:.5rem}.motive-section .custom-dropdown.open .dropdown-trigger{border-color:var(--Primary-Uell, #1da4b1)}.motive-section .custom-dropdown.open .dropdown-trigger .dropdown-arrow{transform:rotate(180deg)}.motive-section .custom-dropdown .dropdown-menu{position:absolute;top:calc(100% + 4px);left:0;right:0;display:block;min-height:40px;min-width:100%;background:#fff;border:1px solid #e5e7eb;border-radius:8px;box-shadow:0 4px 16px #0000001f;max-height:250px;overflow:hidden;overflow-y:auto;z-index:1050;visibility:visible;opacity:1;padding:0}.motive-section .custom-dropdown .dropdown-menu .dropdown-item{width:100%;padding:1rem 1.25rem;background:transparent;border:none;border-bottom:1px dashed #e5e7eb;text-align:left;font-size:.9375rem;color:#4b5563;cursor:pointer;transition:all .15s ease;position:relative}.motive-section .custom-dropdown .dropdown-menu .dropdown-item:last-child{border-bottom:none}.motive-section .custom-dropdown .dropdown-menu .dropdown-item:hover{background:rgba(29,164,177,.05);color:#374151}.motive-section .custom-dropdown .dropdown-menu .dropdown-item.selected{background:rgba(29,164,177,.08);color:var(--Primary-Uell, #1da4b1);border-left:3px solid var(--Primary-Uell, #1da4b1);padding-left:calc(1.25rem - 3px)}.motive-section .custom-dropdown .dropdown-menu .dropdown-item:focus{outline:none;background:rgba(29,164,177,.05)}.motive-section .custom-dropdown .dropdown-menu .dropdown-item:first-child{border-radius:8px 8px 0 0}.motive-section .custom-dropdown .dropdown-menu .dropdown-item:last-child{border-radius:0 0 8px 8px}.motive-section .custom-dropdown .dropdown-menu .dropdown-item:only-child{border-radius:8px}.motive-section .clear-motive-btn{position:absolute;right:40px;top:32px;background:#f3f4f6;border:none;padding:4px 6px;border-radius:4px;cursor:pointer;color:#6b7280;font-size:.625rem;transition:all .2s ease}.motive-section .clear-motive-btn:hover{background:#e5e7eb;color:#374151}.motive-section .clear-motive-btn:focus{outline:none}.observation-section{display:flex;flex-direction:column;gap:.5rem}.observation-section .observation-label{font-size:.875rem;font-weight:500;color:#4b5563}.observation-section .observation-label.required{color:#dc2626}.observation-section .observation-label.required:after{content:\" *\";font-weight:600}.observation-section .observation-textarea{width:100%;padding:.75rem 1rem;border:1px solid #d1d5db;border-radius:12px;font-size:.875rem;font-family:inherit;resize:vertical;min-height:80px;transition:all .2s ease;background:#fff}.observation-section .observation-textarea::placeholder{color:#9ca3af}.observation-section .observation-textarea:hover{border-color:#9ca3af}.observation-section .observation-textarea:focus{outline:none;border-color:var(--Primary-Uell, #1da4b1);box-shadow:0 0 0 3px #1da4b126}.feedback-footer{padding:1rem 1.5rem 1.5rem;display:flex;justify-content:flex-end}.feedback-footer .submit-btn{padding:.75rem 2rem;background:var(--Primary-Uell, #1da4b1);color:#fff;border:none;border-radius:24px;font-size:.9375rem;font-weight:600;cursor:pointer;transition:all .2s ease;min-width:120px}.feedback-footer .submit-btn:hover:not(:disabled){background:#178e99}.feedback-footer .submit-btn:disabled{background:rgba(29,164,177,.5);cursor:not-allowed}.feedback-footer .submit-btn:focus{outline:none;box-shadow:0 0 0 3px #1da4b126}.feedback-footer .submit-btn i.fa-spinner{margin-right:6px}.toast-notification{position:fixed;bottom:24px;left:24px;display:flex;align-items:center;gap:10px;padding:14px 20px;background:#383e52;color:#fff;border-radius:8px;box-shadow:0 4px 12px #00000040;font-size:.875rem;font-weight:500;z-index:1060;animation:slideIn .3s ease}.toast-notification i.fa-check-circle{color:#4ade80;font-size:1.125rem}@keyframes slideIn{0%{transform:translate(-100%);opacity:0}to{transform:translate(0);opacity:1}}.dropdown-backdrop{position:fixed;inset:0;z-index:99}\n"] }]
|
|
1161
|
+
}], ctorParameters: function () { return [{ type: i1.NgbActiveModal }, { type: TasService }]; }, propDecorators: { videoCallId: [{
|
|
1162
|
+
type: Input
|
|
1163
|
+
}], tenant: [{
|
|
1164
|
+
type: Input
|
|
1165
|
+
}], businessRole: [{
|
|
1166
|
+
type: Input
|
|
1167
|
+
}] } });
|
|
1168
|
+
|
|
901
1169
|
/**
|
|
902
1170
|
* SVG icons used internally by TAS SDK components.
|
|
903
1171
|
* Icons are stored as strings to avoid asset bundling complexity.
|
|
@@ -998,11 +1266,12 @@ var UserGeoViewState;
|
|
|
998
1266
|
UserGeoViewState["DENIED"] = "DENIED";
|
|
999
1267
|
})(UserGeoViewState || (UserGeoViewState = {}));
|
|
1000
1268
|
class TasVideocallComponent {
|
|
1001
|
-
constructor(activeModal, tasService, geolocationService, sanitizer) {
|
|
1269
|
+
constructor(activeModal, tasService, geolocationService, sanitizer, modalService) {
|
|
1002
1270
|
this.activeModal = activeModal;
|
|
1003
1271
|
this.tasService = tasService;
|
|
1004
1272
|
this.geolocationService = geolocationService;
|
|
1005
1273
|
this.sanitizer = sanitizer;
|
|
1274
|
+
this.modalService = modalService;
|
|
1006
1275
|
this.participantName = '';
|
|
1007
1276
|
this.tenant = '';
|
|
1008
1277
|
this.businessRole = TasBusinessRole.USER;
|
|
@@ -1026,6 +1295,7 @@ class TasVideocallComponent {
|
|
|
1026
1295
|
this.UserGeoViewState = UserGeoViewState; // Expose enum to template
|
|
1027
1296
|
this.devModeEnabled = false; // Enable dev controls for testing
|
|
1028
1297
|
this.geoPanelDismissed = false; // Track if owner manually closed the panel
|
|
1298
|
+
this.feedbackShown = false; // Track if feedback modal has been shown
|
|
1029
1299
|
this.subscriptions = new Subscription();
|
|
1030
1300
|
this.homeIcon = this.sanitizer.bypassSecurityTrustHtml(TAS_ICONS.home);
|
|
1031
1301
|
}
|
|
@@ -1053,6 +1323,38 @@ class TasVideocallComponent {
|
|
|
1053
1323
|
hangUp() {
|
|
1054
1324
|
this.tasService.disconnectSession();
|
|
1055
1325
|
}
|
|
1326
|
+
/**
|
|
1327
|
+
* Open feedback modal when call ends
|
|
1328
|
+
*/
|
|
1329
|
+
openFeedbackModal() {
|
|
1330
|
+
var _a;
|
|
1331
|
+
// Prevent opening feedback modal multiple times
|
|
1332
|
+
if (this.feedbackShown) {
|
|
1333
|
+
this.activeModal.close('hangup');
|
|
1334
|
+
return;
|
|
1335
|
+
}
|
|
1336
|
+
// Get videoCallId from input or service
|
|
1337
|
+
const videoCallId = (_a = this.videoCallId) !== null && _a !== void 0 ? _a : this.tasService.videoCallId;
|
|
1338
|
+
// If no videoCallId, skip feedback and close directly
|
|
1339
|
+
if (!videoCallId) {
|
|
1340
|
+
this.activeModal.close('hangup');
|
|
1341
|
+
return;
|
|
1342
|
+
}
|
|
1343
|
+
this.feedbackShown = true;
|
|
1344
|
+
const modalRef = this.modalService.open(TasFeedbackModalComponent, {
|
|
1345
|
+
centered: true,
|
|
1346
|
+
backdrop: true,
|
|
1347
|
+
keyboard: true,
|
|
1348
|
+
windowClass: 'tas-feedback-modal-wrapper',
|
|
1349
|
+
});
|
|
1350
|
+
modalRef.componentInstance.videoCallId = videoCallId;
|
|
1351
|
+
modalRef.componentInstance.tenant = this.tenant;
|
|
1352
|
+
modalRef.componentInstance.businessRole = this.businessRole;
|
|
1353
|
+
// Close videocall modal after feedback modal is closed/dismissed
|
|
1354
|
+
modalRef.result.finally(() => {
|
|
1355
|
+
this.activeModal.close('hangup');
|
|
1356
|
+
});
|
|
1357
|
+
}
|
|
1056
1358
|
toggleMute() {
|
|
1057
1359
|
this.tasService.toggleMute();
|
|
1058
1360
|
}
|
|
@@ -1178,7 +1480,7 @@ class TasVideocallComponent {
|
|
|
1178
1480
|
this.subscriptions.add(this.tasService.callState$.subscribe((state) => {
|
|
1179
1481
|
this.callState = state;
|
|
1180
1482
|
if (state === CallState.DISCONNECTED) {
|
|
1181
|
-
this.
|
|
1483
|
+
this.openFeedbackModal();
|
|
1182
1484
|
}
|
|
1183
1485
|
// Track if we have an active video stream
|
|
1184
1486
|
this.hasVideoStream = state === CallState.CONNECTED;
|
|
@@ -1205,8 +1507,9 @@ class TasVideocallComponent {
|
|
|
1205
1507
|
// Owner left subscription - disconnect non-owners
|
|
1206
1508
|
this.subscriptions.add(this.tasService.ownerHasLeft$.subscribe((hasLeft) => {
|
|
1207
1509
|
if (hasLeft && !this.canAdmitUsers) { // Non-owner user
|
|
1510
|
+
// hangUp() triggers DISCONNECTED state, which opens feedback modal
|
|
1511
|
+
// then closes videocall modal after feedback is done
|
|
1208
1512
|
this.hangUp();
|
|
1209
|
-
this.activeModal.close('owner_left');
|
|
1210
1513
|
}
|
|
1211
1514
|
}));
|
|
1212
1515
|
// ActivateGeo subscription - only for non-owners (users)
|
|
@@ -1397,12 +1700,12 @@ class TasVideocallComponent {
|
|
|
1397
1700
|
});
|
|
1398
1701
|
}
|
|
1399
1702
|
}
|
|
1400
|
-
TasVideocallComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.12", ngImport: i0, type: TasVideocallComponent, deps: [{ token: i1.NgbActiveModal }, { token: TasService }, { token: GeolocationService }, { token: i4.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component });
|
|
1401
|
-
TasVideocallComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.12", type: TasVideocallComponent, selector: "tas-videocall", inputs: { sessionId: "sessionId", token: "token", appointmentId: "appointmentId", videoCallId: "videoCallId", userId: "userId", participantName: "participantName", tenant: "tenant", businessRole: "businessRole", isReturningFromPip: "isReturningFromPip" }, viewQueries: [{ propertyName: "publisherContainer", first: true, predicate: ["publisherContainer"], descendants: true }, { propertyName: "subscriberContainer", first: true, predicate: ["subscriberContainer"], descendants: true }], ngImport: i0, template: "<div class=\"tas-videocall-wrapper\">\n <div class=\"tas-videocall-container\">\n <!-- Subscriber video (large, background) -->\n <div\n id=\"subscriber-container\"\n [class.subscriber-view]=\"isPublisherSmall\"\n [class.publisher-view]=\"!isPublisherSmall\"\n #subscriberContainer\n (dblclick)=\"onDoubleClick()\"\n ></div>\n\n <!-- Publisher video (small, overlay) -->\n <div\n id=\"publisher-container\"\n [class.publisher-view]=\"isPublisherSmall\"\n [class.subscriber-view]=\"!isPublisherSmall\"\n #publisherContainer\n (dblclick)=\"onDoubleClick()\"\n ></div>\n\n <!-- Centered avatar (shown when no video stream) -->\n <div class=\"avatar-container\" *ngIf=\"!hasVideoStream\">\n <tas-avatar [name]=\"participantName\" [size]=\"80\"></tas-avatar>\n </div>\n\n <!-- Controls -->\n <div class=\"controls-container\">\n <button\n class=\"btn control-btn mute-btn\"\n [class.muted]=\"isMuted\"\n (click)=\"toggleMute()\"\n [title]=\"isMuted ? 'Activar micr\u00F3fono' : 'Silenciar micr\u00F3fono'\"\n [attr.aria-label]=\"isMuted ? 'Activar micr\u00F3fono' : 'Silenciar micr\u00F3fono'\"\n >\n <i\n class=\"fa\"\n [class.fa-microphone]=\"!isMuted\"\n [class.fa-microphone-slash]=\"isMuted\"\n ></i>\n </button>\n <button\n class=\"btn control-btn swap-btn\"\n (click)=\"toggleSwap()\"\n title=\"Intercambiar vista\"\n aria-label=\"Intercambiar vista\"\n >\n <i class=\"fa fa-refresh\"></i>\n </button>\n <button\n class=\"btn control-btn pip-btn\"\n (click)=\"minimize()\"\n title=\"Minimizar (Picture in Picture)\"\n aria-label=\"Minimizar videollamada\"\n >\n <i class=\"fa fa-compress\"></i>\n </button>\n <button\n class=\"btn control-btn hangup-btn\"\n (click)=\"hangUp()\"\n title=\"Finalizar llamada\"\n aria-label=\"Finalizar llamada\"\n >\n <i class=\"fa fa-phone\"></i>\n </button>\n </div>\n\n <!-- Waiting room notification (shown for OWNER/BACKOFFICE only) -->\n <div\n class=\"waiting-notification\"\n *ngIf=\"waitingRoomUsers.length > 0 && canAdmitUsers\"\n role=\"alert\"\n aria-live=\"polite\"\n >\n <span class=\"waiting-text\">\n {{ waitingRoomUsers[0].name }} est\u00E1 en la sala de espera.\n </span>\n <button\n class=\"admit-btn\"\n (click)=\"admitUser(waitingRoomUsers[0].userId)\"\n aria-label=\"Admitir usuario\"\n >\n Admitir\n </button>\n <button\n class=\"dismiss-btn\"\n (click)=\"dismissWaitingNotification(waitingRoomUsers[0].userId)\"\n aria-label=\"Cerrar notificaci\u00F3n\"\n >\n \u00D7\n </button>\n </div>\n </div>\n\n <!-- Owner geolocation panel (for owners to request user location) -->\n <div class=\"user-geo-panel\" *ngIf=\"shouldShowUserGeoPanel || devModeEnabled\">\n <div class=\"user-geo-header\">\n <div class=\"header-title-row\">\n <i class=\"fa fa-map-marker header-icon\" *ngIf=\"userGeoViewState !== UserGeoViewState.INITIAL\"></i>\n <h3>Ubicaci\u00F3n del colaborador</h3>\n </div>\n <button class=\"close-btn\" (click)=\"setUserGeoViewState(UserGeoViewState.HIDDEN)\" aria-label=\"Cerrar\">\u00D7</button>\n </div>\n\n <div class=\"user-geo-description\" *ngIf=\"userGeoViewState !== UserGeoViewState.VERIFIED && userGeoViewState !== UserGeoViewState.DENIED\">\n <p>El colaborador tiene la ubicaci\u00F3n desactivada, solicita que la active.</p>\n <p>Esta acci\u00F3n nos permitir\u00E1 disponibilizar algunas alertas.</p>\n </div>\n\n <div class=\"user-geo-content\">\n <!-- INITIAL state: just the button -->\n <ng-container *ngIf=\"userGeoViewState === UserGeoViewState.INITIAL\">\n <!-- spacer -->\n </ng-container>\n\n <!-- VERIFYING state: spinner + loading message -->\n <ng-container *ngIf=\"userGeoViewState === UserGeoViewState.VERIFYING\">\n <div class=\"user-geo-verifying\">\n <div class=\"geo-spinner-large\"></div>\n <p class=\"verifying-title\">Verificando ubicaci\u00F3n...</p>\n <p class=\"verifying-subtitle\">Esto puede tardar unos segundos.</p>\n </div>\n </ng-container>\n\n <!-- VERIFIED state: home icon with sparkles -->\n <ng-container *ngIf=\"userGeoViewState === UserGeoViewState.VERIFIED\">\n <div class=\"user-geo-verified\">\n <div class=\"verified-icon-container\">\n <span class=\"home-icon\" [innerHTML]=\"homeIcon\"></span>\n </div>\n <p class=\"verified-title\">La ubicaci\u00F3n fue verificada</p>\n </div>\n </ng-container>\n\n <!-- DENIED state: error icon and message -->\n <ng-container *ngIf=\"userGeoViewState === UserGeoViewState.DENIED\">\n <div class=\"user-geo-denied\">\n <div class=\"denied-icon-container\">\n <i class=\"fa fa-times-circle\"></i>\n </div>\n <p class=\"denied-title\">La ubicaci\u00F3n fue rechazada</p>\n <p class=\"denied-subtitle\">El colaborador no permiti\u00F3 el acceso a su ubicaci\u00F3n.</p>\n </div>\n </ng-container>\n </div>\n\n <!-- Button (hidden in verified and denied states) -->\n <button \n class=\"user-geo-btn\" \n *ngIf=\"userGeoViewState !== UserGeoViewState.VERIFIED && userGeoViewState !== UserGeoViewState.DENIED\"\n [class.disabled]=\"userGeoViewState === UserGeoViewState.VERIFYING\"\n [disabled]=\"userGeoViewState === UserGeoViewState.VERIFYING\"\n (click)=\"requestUserLocation()\">\n Verificar ubicaci\u00F3n\n </button>\n\n <!-- Dev controls -->\n <div class=\"dev-controls\" *ngIf=\"devModeEnabled\">\n <span class=\"dev-label\">Dev:</span>\n <button class=\"dev-btn\" (click)=\"setUserGeoViewState(UserGeoViewState.INITIAL)\">Initial</button>\n <button class=\"dev-btn\" (click)=\"setUserGeoViewState(UserGeoViewState.VERIFYING)\">Verifying</button>\n <button class=\"dev-btn\" (click)=\"setUserGeoViewState(UserGeoViewState.VERIFIED)\">Verified</button>\n <button class=\"dev-btn\" (click)=\"setUserGeoViewState(UserGeoViewState.DENIED)\">Denied</button>\n <button class=\"dev-btn\" (click)=\"setUserGeoViewState(UserGeoViewState.HIDDEN)\">Hide</button>\n </div>\n </div>\n</div>\n\n", styles: ["@charset \"UTF-8\";:host{display:flex;width:100vw;height:100vh;box-sizing:border-box;padding:2rem;background:linear-gradient(281deg,rgba(29,164,177,.2) 6.96%,rgba(0,0,0,0) 70.44%),#212532}.tas-videocall-wrapper{display:flex;flex:1;gap:1rem;height:100%}.tas-videocall-container{position:relative;flex:1;height:100%;overflow:hidden;border-radius:8px;border:1px solid var(--Neutral-GreyLight, #dadfe9);background:linear-gradient(180deg,#e5f1f7 0%,#0072ac 100%)}.tas-videocall-container ::ng-deep .OT_edge-bar-item,.tas-videocall-container ::ng-deep .OT_mute,.tas-videocall-container ::ng-deep .OT_audio-level-meter,.tas-videocall-container ::ng-deep .OT_bar,.tas-videocall-container ::ng-deep .OT_name{display:none!important}.tas-videocall-container .subscriber-view{width:100%;height:100%;z-index:1}.tas-videocall-container .publisher-view{position:absolute;top:20px;right:20px;width:200px;height:150px;z-index:2;border:2px solid #fff;border-radius:8px;background-color:#0000004d;overflow:hidden}.tas-videocall-container .avatar-container{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:1;display:flex;align-items:center;justify-content:center}.tas-videocall-container .controls-container{display:flex;flex-direction:row;gap:12px;position:absolute;bottom:30px;left:50%;transform:translate(-50%);z-index:3;background-color:#33475bb3;padding:12px 20px;border-radius:30px;backdrop-filter:blur(8px)}.tas-videocall-container .controls-container .control-btn{width:44px;height:44px;border-radius:20px;display:flex;align-items:center;justify-content:center;font-size:18px;border:none;background:transparent;cursor:pointer;transition:all .2s ease}.tas-videocall-container .controls-container .control-btn i{color:#fff}.tas-videocall-container .controls-container .control-btn:hover{transform:scale(1.05);filter:brightness(1.1)}.tas-videocall-container .controls-container .control-btn:focus{outline:2px solid #fff;outline-offset:2px}.tas-videocall-container .controls-container .hangup-btn{background:#f44336}.tas-videocall-container .controls-container .hangup-btn i{transform:rotate(135deg)}.tas-videocall-container .controls-container .hangup-btn:hover{background:#d32f2f}.tas-videocall-container .controls-container .swap-btn,.tas-videocall-container .controls-container .pip-btn,.tas-videocall-container .controls-container .mute-btn{background:transparent}.tas-videocall-container .controls-container .swap-btn:hover,.tas-videocall-container .controls-container .pip-btn:hover,.tas-videocall-container .controls-container .mute-btn:hover{background:rgba(255,255,255,.15)}.tas-videocall-container .waiting-notification{position:absolute;bottom:100px;left:16px;display:flex;align-items:center;gap:12px;background-color:#33475be6;padding:10px 16px;border-radius:8px;z-index:4;backdrop-filter:blur(4px);max-width:calc(100% - 32px)}.tas-videocall-container .waiting-notification .waiting-text{color:#fff;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tas-videocall-container .waiting-notification .admit-btn{background:var(--Primary-Uell, #1da4b1);color:#fff;border:none;border-radius:4px;padding:6px 16px;font-size:14px;font-weight:500;cursor:pointer;transition:background .2s ease;white-space:nowrap}.tas-videocall-container .waiting-notification .admit-btn:hover{background:#178e99}.tas-videocall-container .waiting-notification .admit-btn:focus{outline:2px solid #fff;outline-offset:2px}.tas-videocall-container .waiting-notification .dismiss-btn{background:transparent;color:#fff;border:none;font-size:20px;line-height:1;cursor:pointer;padding:0 4px;opacity:.7;transition:opacity .2s ease}.tas-videocall-container .waiting-notification .dismiss-btn:hover{opacity:1}.tas-videocall-container .waiting-notification .dismiss-btn:focus{outline:2px solid #fff;outline-offset:2px}.location-panel{width:280px;height:100%;background:#212532;border-radius:8px;padding:1.5rem;display:flex;flex-direction:column;color:#fff}.location-panel .location-header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:1rem}.location-panel .location-header h3{font-size:16px;font-weight:600;margin:0;color:#fff}.location-panel .location-header .close-btn{background:transparent;border:none;color:#fff;font-size:20px;cursor:pointer;padding:0;line-height:1;opacity:.7}.location-panel .location-header .close-btn:hover{opacity:1}.location-panel .location-description{font-size:14px;color:#fffc;line-height:1.5;margin-bottom:.5rem}.location-panel .location-description p{margin:0 0 .5rem}.location-panel .location-user-list{display:flex;flex-direction:column;gap:.5rem;margin-bottom:1rem;max-height:200px;overflow-y:auto}.location-panel .location-user-list .user-geo-item{display:flex;align-items:center;gap:.75rem;padding:.5rem .75rem;background:rgba(255,255,255,.05);border-radius:6px}.location-panel .location-user-list .user-geo-item .user-icon{color:#fff9;font-size:14px}.location-panel .location-user-list .user-geo-item .user-name{flex:1;font-size:14px;color:#fff}.location-panel .location-user-list .user-geo-item .geo-status{display:flex;align-items:center;gap:.35rem;font-size:12px;padding:2px 8px;border-radius:12px}.location-panel .location-user-list .user-geo-item .geo-status.pending{color:#ffc107;background:rgba(255,193,7,.15)}.location-panel .location-user-list .user-geo-item .geo-status.granted{color:#4caf50;background:rgba(76,175,80,.15)}.location-panel .location-user-list .user-geo-item .geo-status.denied{color:#f44336;background:rgba(244,67,54,.15)}.location-panel .location-user-list .user-geo-item .geo-status i{font-size:10px}.location-panel .geo-denied-warning{display:flex;align-items:center;gap:.5rem;padding:.75rem;background:rgba(244,67,54,.15);border-radius:6px;margin-bottom:1rem;color:#f44336;font-size:13px}.location-panel .geo-denied-warning i{font-size:14px}.location-panel .location-content{flex:1;display:flex;flex-direction:column;justify-content:flex-end}.location-panel .verify-location-btn{width:100%;padding:12px 24px;background:var(--Primary-Uell, #1da4b1);color:#fff;border:none;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer;transition:background .2s ease;margin-bottom:1rem}.location-panel .verify-location-btn:hover{background:#178e99}.location-panel .verify-location-btn:disabled{background:rgba(29,164,177,.5);cursor:not-allowed}.location-panel .location-footer{display:flex;justify-content:flex-end;gap:.5rem;padding-top:.5rem;border-top:1px solid rgba(255,255,255,.1)}.location-panel .location-footer .footer-icon{width:32px;height:32px;border-radius:50%;display:flex;align-items:center;justify-content:center;background:rgba(255,255,255,.1);color:#fff;font-size:14px}.location-panel .location-footer .footer-icon.location-icon{background:var(--Primary-Uell, #1da4b1)}.location-panel .header-icon{color:#fff;font-size:16px}.location-panel .geo-loading-container{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem 0}.location-panel .geo-loading-container .geo-spinner{width:64px;height:64px;border:4px solid rgba(29,164,177,.2);border-top-color:var(--Primary-Uell, #1da4b1);border-radius:50%;animation:geo-spin 1s linear infinite}.location-panel .geo-loading-container .loading-title{color:#fff;font-size:16px;font-weight:600;margin:1.5rem 0 .25rem}.location-panel .geo-loading-container .loading-subtitle{color:#ffffffb3;font-size:14px;margin:0}.location-panel .geo-success-container{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem 0}.location-panel .geo-success-container .success-icon{width:80px;height:80px;border-radius:50%;background:var(--Primary-Uell, #1da4b1);display:flex;align-items:center;justify-content:center;position:relative}.location-panel .geo-success-container .success-icon i{color:#fff;font-size:32px}.location-panel .geo-success-container .success-icon:before,.location-panel .geo-success-container .success-icon:after{content:\"\\2726\";position:absolute;color:#fff;font-size:10px}.location-panel .geo-success-container .success-icon:before{top:-8px;right:-4px}.location-panel .geo-success-container .success-icon:after{bottom:-4px;left:-8px}.location-panel .geo-success-container .success-title{color:#fff;font-size:16px;font-weight:600;margin:1.5rem 0 0}.location-panel .verify-location-btn.disabled{background:rgba(29,164,177,.5);cursor:not-allowed}@keyframes geo-spin{to{transform:rotate(360deg)}}.user-geo-panel{width:360px;height:100%;background:#212532;border-radius:8px;padding:1.5rem;display:flex;flex-direction:column;color:#fff}.user-geo-panel .user-geo-header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:2.5rem;padding-top:2rem}.user-geo-panel .user-geo-header .header-title-row{display:flex;align-items:center;gap:.5rem}.user-geo-panel .user-geo-header .header-title-row .header-icon{font-size:16px;color:#fff}.user-geo-panel .user-geo-header .header-title-row h3{font-size:18px;font-weight:600;margin:0;color:#fff}.user-geo-panel .user-geo-header .close-btn{background:transparent;border:none;color:#fff;font-size:20px;cursor:pointer;padding:0;line-height:1;opacity:.7}.user-geo-panel .user-geo-header .close-btn:hover{opacity:1}.user-geo-panel .user-geo-description{font-size:14px;color:#fffc;line-height:1.6;margin-bottom:1rem}.user-geo-panel .user-geo-description p{margin:0 0 .75rem}.user-geo-panel .user-geo-content{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center}.user-geo-panel .user-geo-verifying{display:flex;flex-direction:column;align-items:center;text-align:center}.user-geo-panel .user-geo-verifying .geo-spinner-large{width:80px;height:80px;border:4px solid rgba(29,164,177,.2);border-top-color:var(--Primary-Uell, #1da4b1);border-radius:50%;animation:geo-spin 1s linear infinite;margin-bottom:1.5rem}.user-geo-panel .user-geo-verifying .verifying-title{color:#fff;font-size:16px;font-weight:600;margin:0 0 .25rem}.user-geo-panel .user-geo-verifying .verifying-subtitle{color:#fff9;font-size:14px;margin:0}.user-geo-panel .user-geo-verified{display:flex;flex-direction:column;align-items:center;text-align:center}.user-geo-panel .user-geo-verified .verified-icon-container{margin-bottom:1rem}.user-geo-panel .user-geo-verified .verified-icon-container .home-icon{display:block}.user-geo-panel .user-geo-verified .verified-icon-container .home-icon svg{width:120px;height:100px}.user-geo-panel .user-geo-verified .verified-title{color:#fff;font-size:16px;font-weight:600;margin:0}.user-geo-panel .user-geo-denied{display:flex;flex-direction:column;align-items:center;text-align:center}.user-geo-panel .user-geo-denied .denied-icon-container{width:100px;height:100px;border-radius:50%;background:rgba(244,67,54,.2);display:flex;align-items:center;justify-content:center;margin-bottom:1.5rem}.user-geo-panel .user-geo-denied .denied-icon-container i{font-size:48px;color:#f44336}.user-geo-panel .user-geo-denied .denied-title{color:#fff;font-size:16px;font-weight:600;margin:0 0 .5rem}.user-geo-panel .user-geo-denied .denied-subtitle{color:#fff9;font-size:14px;margin:0}.user-geo-panel .user-geo-btn{width:100%;padding:14px 24px;background:var(--Primary-Uell, #1da4b1);color:#fff;border:none;border-radius:24px;font-size:14px;font-weight:600;cursor:pointer;transition:all .2s ease;margin-top:auto;margin-bottom:1rem}.user-geo-panel .user-geo-btn:hover:not(.disabled){background:#178e99}.user-geo-panel .user-geo-btn.disabled{background:rgba(29,164,177,.5);cursor:not-allowed}.user-geo-panel .dev-controls{display:flex;align-items:center;gap:.5rem;padding:.75rem;background:rgba(0,0,0,.3);border-radius:6px;margin-bottom:1rem;flex-wrap:wrap}.user-geo-panel .dev-controls .dev-label{font-size:12px;color:#fff9;font-weight:600}.user-geo-panel .dev-controls .dev-btn{padding:4px 8px;font-size:11px;background:rgba(255,255,255,.1);color:#fff;border:1px solid rgba(255,255,255,.2);border-radius:4px;cursor:pointer;transition:all .2s ease}.user-geo-panel .dev-controls .dev-btn:hover{background:rgba(255,255,255,.2)}.user-geo-panel .user-geo-footer{display:flex;justify-content:center;gap:.75rem;padding-top:.5rem}.user-geo-panel .user-geo-footer .footer-icon{width:40px;height:40px;border-radius:50%;display:flex;align-items:center;justify-content:center;background:rgba(255,255,255,.1);color:#fff9;font-size:16px}.user-geo-panel .user-geo-footer .footer-icon.active{background:var(--Primary-Uell, #1da4b1);color:#fff}\n"], components: [{ type: TasAvatarComponent, selector: "tas-avatar", inputs: ["name", "size"] }], directives: [{ type: i4$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
|
|
1703
|
+
TasVideocallComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.12", ngImport: i0, type: TasVideocallComponent, deps: [{ token: i1.NgbActiveModal }, { token: TasService }, { token: GeolocationService }, { token: i4$2.DomSanitizer }, { token: i1.NgbModal }], target: i0.ɵɵFactoryTarget.Component });
|
|
1704
|
+
TasVideocallComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.12", type: TasVideocallComponent, selector: "tas-videocall", inputs: { sessionId: "sessionId", token: "token", appointmentId: "appointmentId", videoCallId: "videoCallId", userId: "userId", participantName: "participantName", tenant: "tenant", businessRole: "businessRole", isReturningFromPip: "isReturningFromPip" }, viewQueries: [{ propertyName: "publisherContainer", first: true, predicate: ["publisherContainer"], descendants: true }, { propertyName: "subscriberContainer", first: true, predicate: ["subscriberContainer"], descendants: true }], ngImport: i0, template: "<div class=\"tas-videocall-wrapper\">\n <div class=\"tas-videocall-container\">\n <!-- Subscriber video (large, background) -->\n <div\n id=\"subscriber-container\"\n [class.subscriber-view]=\"isPublisherSmall\"\n [class.publisher-view]=\"!isPublisherSmall\"\n #subscriberContainer\n (dblclick)=\"onDoubleClick()\"\n ></div>\n\n <!-- Publisher video (small, overlay) -->\n <div\n id=\"publisher-container\"\n [class.publisher-view]=\"isPublisherSmall\"\n [class.subscriber-view]=\"!isPublisherSmall\"\n #publisherContainer\n (dblclick)=\"onDoubleClick()\"\n ></div>\n\n <!-- Centered avatar (shown when no video stream) -->\n <div class=\"avatar-container\" *ngIf=\"!hasVideoStream\">\n <tas-avatar [name]=\"participantName\" [size]=\"80\"></tas-avatar>\n </div>\n\n <!-- Controls -->\n <div class=\"controls-container\">\n <button\n class=\"btn control-btn mute-btn\"\n [class.muted]=\"isMuted\"\n (click)=\"toggleMute()\"\n [title]=\"isMuted ? 'Activar micr\u00F3fono' : 'Silenciar micr\u00F3fono'\"\n [attr.aria-label]=\"isMuted ? 'Activar micr\u00F3fono' : 'Silenciar micr\u00F3fono'\"\n >\n <i\n class=\"fa\"\n [class.fa-microphone]=\"!isMuted\"\n [class.fa-microphone-slash]=\"isMuted\"\n ></i>\n </button>\n <button\n class=\"btn control-btn swap-btn\"\n (click)=\"toggleSwap()\"\n title=\"Intercambiar vista\"\n aria-label=\"Intercambiar vista\"\n >\n <i class=\"fa fa-refresh\"></i>\n </button>\n <button\n class=\"btn control-btn pip-btn\"\n (click)=\"minimize()\"\n title=\"Minimizar (Picture in Picture)\"\n aria-label=\"Minimizar videollamada\"\n >\n <i class=\"fa fa-compress\"></i>\n </button>\n <button\n class=\"btn control-btn hangup-btn\"\n (click)=\"hangUp()\"\n title=\"Finalizar llamada\"\n aria-label=\"Finalizar llamada\"\n >\n <i class=\"fa fa-phone\"></i>\n </button>\n </div>\n\n <!-- Waiting room notification (shown for OWNER/BACKOFFICE only) -->\n <div\n class=\"waiting-notification\"\n *ngIf=\"waitingRoomUsers.length > 0 && canAdmitUsers\"\n role=\"alert\"\n aria-live=\"polite\"\n >\n <span class=\"waiting-text\">\n {{ waitingRoomUsers[0].name }} est\u00E1 en la sala de espera.\n </span>\n <button\n class=\"admit-btn\"\n (click)=\"admitUser(waitingRoomUsers[0].userId)\"\n aria-label=\"Admitir usuario\"\n >\n Admitir\n </button>\n <button\n class=\"dismiss-btn\"\n (click)=\"dismissWaitingNotification(waitingRoomUsers[0].userId)\"\n aria-label=\"Cerrar notificaci\u00F3n\"\n >\n \u00D7\n </button>\n </div>\n </div>\n\n <!-- Owner geolocation panel (for owners to request user location) -->\n <div class=\"user-geo-panel\" *ngIf=\"shouldShowUserGeoPanel || devModeEnabled\">\n <div class=\"user-geo-header\">\n <div class=\"header-title-row\">\n <i class=\"fa fa-map-marker header-icon\" *ngIf=\"userGeoViewState !== UserGeoViewState.INITIAL\"></i>\n <h3>Ubicaci\u00F3n del colaborador</h3>\n </div>\n <button class=\"close-btn\" (click)=\"setUserGeoViewState(UserGeoViewState.HIDDEN)\" aria-label=\"Cerrar\">\u00D7</button>\n </div>\n\n <div class=\"user-geo-description\" *ngIf=\"userGeoViewState !== UserGeoViewState.VERIFIED && userGeoViewState !== UserGeoViewState.DENIED\">\n <p>El colaborador tiene la ubicaci\u00F3n desactivada, solicita que la active.</p>\n <p>Esta acci\u00F3n nos permitir\u00E1 disponibilizar algunas alertas.</p>\n </div>\n\n <div class=\"user-geo-content\">\n <!-- INITIAL state: just the button -->\n <ng-container *ngIf=\"userGeoViewState === UserGeoViewState.INITIAL\">\n <!-- spacer -->\n </ng-container>\n\n <!-- VERIFYING state: spinner + loading message -->\n <ng-container *ngIf=\"userGeoViewState === UserGeoViewState.VERIFYING\">\n <div class=\"user-geo-verifying\">\n <div class=\"geo-spinner-large\"></div>\n <p class=\"verifying-title\">Verificando ubicaci\u00F3n...</p>\n <p class=\"verifying-subtitle\">Esto puede tardar unos segundos.</p>\n </div>\n </ng-container>\n\n <!-- VERIFIED state: home icon with sparkles -->\n <ng-container *ngIf=\"userGeoViewState === UserGeoViewState.VERIFIED\">\n <div class=\"user-geo-verified\">\n <div class=\"verified-icon-container\">\n <span class=\"home-icon\" [innerHTML]=\"homeIcon\"></span>\n </div>\n <p class=\"verified-title\">La ubicaci\u00F3n fue verificada</p>\n </div>\n </ng-container>\n\n <!-- DENIED state: error icon and message -->\n <ng-container *ngIf=\"userGeoViewState === UserGeoViewState.DENIED\">\n <div class=\"user-geo-denied\">\n <div class=\"denied-icon-container\">\n <i class=\"fa fa-times-circle\"></i>\n </div>\n <p class=\"denied-title\">La ubicaci\u00F3n fue rechazada</p>\n <p class=\"denied-subtitle\">El colaborador no permiti\u00F3 el acceso a su ubicaci\u00F3n.</p>\n </div>\n </ng-container>\n </div>\n\n <!-- Button (hidden in verified and denied states) -->\n <button \n class=\"user-geo-btn\" \n *ngIf=\"userGeoViewState !== UserGeoViewState.VERIFIED && userGeoViewState !== UserGeoViewState.DENIED\"\n [class.disabled]=\"userGeoViewState === UserGeoViewState.VERIFYING\"\n [disabled]=\"userGeoViewState === UserGeoViewState.VERIFYING\"\n (click)=\"requestUserLocation()\">\n Verificar ubicaci\u00F3n\n </button>\n\n <!-- Dev controls -->\n <div class=\"dev-controls\" *ngIf=\"devModeEnabled\">\n <span class=\"dev-label\">Dev:</span>\n <button class=\"dev-btn\" (click)=\"setUserGeoViewState(UserGeoViewState.INITIAL)\">Initial</button>\n <button class=\"dev-btn\" (click)=\"setUserGeoViewState(UserGeoViewState.VERIFYING)\">Verifying</button>\n <button class=\"dev-btn\" (click)=\"setUserGeoViewState(UserGeoViewState.VERIFIED)\">Verified</button>\n <button class=\"dev-btn\" (click)=\"setUserGeoViewState(UserGeoViewState.DENIED)\">Denied</button>\n <button class=\"dev-btn\" (click)=\"setUserGeoViewState(UserGeoViewState.HIDDEN)\">Hide</button>\n </div>\n </div>\n</div>\n\n", styles: ["@charset \"UTF-8\";:host{display:flex;width:100vw;height:100vh;box-sizing:border-box;padding:2rem;background:linear-gradient(281deg,rgba(29,164,177,.2) 6.96%,rgba(0,0,0,0) 70.44%),#212532}.tas-videocall-wrapper{display:flex;flex:1;gap:1rem;height:100%}.tas-videocall-container{position:relative;flex:1;height:100%;overflow:hidden;border-radius:8px;border:1px solid var(--Neutral-GreyLight, #dadfe9);background:linear-gradient(180deg,#e5f1f7 0%,#0072ac 100%)}.tas-videocall-container ::ng-deep .OT_edge-bar-item,.tas-videocall-container ::ng-deep .OT_mute,.tas-videocall-container ::ng-deep .OT_audio-level-meter,.tas-videocall-container ::ng-deep .OT_bar,.tas-videocall-container ::ng-deep .OT_name{display:none!important}.tas-videocall-container .subscriber-view{width:100%;height:100%;z-index:1}.tas-videocall-container .publisher-view{position:absolute;top:20px;right:20px;width:200px;height:150px;z-index:2;border:2px solid #fff;border-radius:8px;background-color:#0000004d;overflow:hidden}.tas-videocall-container .avatar-container{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:1;display:flex;align-items:center;justify-content:center}.tas-videocall-container .controls-container{display:flex;flex-direction:row;gap:12px;position:absolute;bottom:30px;left:50%;transform:translate(-50%);z-index:3;background-color:#33475bb3;padding:12px 20px;border-radius:30px;backdrop-filter:blur(8px)}.tas-videocall-container .controls-container .control-btn{width:44px;height:44px;border-radius:20px;display:flex;align-items:center;justify-content:center;font-size:18px;border:none;background:transparent;cursor:pointer;transition:all .2s ease}.tas-videocall-container .controls-container .control-btn i{color:#fff}.tas-videocall-container .controls-container .control-btn:hover{transform:scale(1.05);filter:brightness(1.1)}.tas-videocall-container .controls-container .control-btn:focus{outline:2px solid #fff;outline-offset:2px}.tas-videocall-container .controls-container .hangup-btn{background:#f44336}.tas-videocall-container .controls-container .hangup-btn i{transform:rotate(135deg)}.tas-videocall-container .controls-container .hangup-btn:hover{background:#d32f2f}.tas-videocall-container .controls-container .swap-btn,.tas-videocall-container .controls-container .pip-btn,.tas-videocall-container .controls-container .mute-btn{background:transparent}.tas-videocall-container .controls-container .swap-btn:hover,.tas-videocall-container .controls-container .pip-btn:hover,.tas-videocall-container .controls-container .mute-btn:hover{background:rgba(255,255,255,.15)}.tas-videocall-container .waiting-notification{position:absolute;bottom:100px;left:16px;display:flex;align-items:center;gap:12px;background-color:#33475be6;padding:10px 16px;border-radius:8px;z-index:4;backdrop-filter:blur(4px);max-width:calc(100% - 32px)}.tas-videocall-container .waiting-notification .waiting-text{color:#fff;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tas-videocall-container .waiting-notification .admit-btn{background:var(--Primary-Uell, #1da4b1);color:#fff;border:none;border-radius:4px;padding:6px 16px;font-size:14px;font-weight:500;cursor:pointer;transition:background .2s ease;white-space:nowrap}.tas-videocall-container .waiting-notification .admit-btn:hover{background:#178e99}.tas-videocall-container .waiting-notification .admit-btn:focus{outline:2px solid #fff;outline-offset:2px}.tas-videocall-container .waiting-notification .dismiss-btn{background:transparent;color:#fff;border:none;font-size:20px;line-height:1;cursor:pointer;padding:0 4px;opacity:.7;transition:opacity .2s ease}.tas-videocall-container .waiting-notification .dismiss-btn:hover{opacity:1}.tas-videocall-container .waiting-notification .dismiss-btn:focus{outline:2px solid #fff;outline-offset:2px}.location-panel{width:280px;height:100%;background:#212532;border-radius:8px;padding:1.5rem;display:flex;flex-direction:column;color:#fff}.location-panel .location-header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:1rem}.location-panel .location-header h3{font-size:16px;font-weight:600;margin:0;color:#fff}.location-panel .location-header .close-btn{background:transparent;border:none;color:#fff;font-size:20px;cursor:pointer;padding:0;line-height:1;opacity:.7}.location-panel .location-header .close-btn:hover{opacity:1}.location-panel .location-description{font-size:14px;color:#fffc;line-height:1.5;margin-bottom:.5rem}.location-panel .location-description p{margin:0 0 .5rem}.location-panel .location-user-list{display:flex;flex-direction:column;gap:.5rem;margin-bottom:1rem;max-height:200px;overflow-y:auto}.location-panel .location-user-list .user-geo-item{display:flex;align-items:center;gap:.75rem;padding:.5rem .75rem;background:rgba(255,255,255,.05);border-radius:6px}.location-panel .location-user-list .user-geo-item .user-icon{color:#fff9;font-size:14px}.location-panel .location-user-list .user-geo-item .user-name{flex:1;font-size:14px;color:#fff}.location-panel .location-user-list .user-geo-item .geo-status{display:flex;align-items:center;gap:.35rem;font-size:12px;padding:2px 8px;border-radius:12px}.location-panel .location-user-list .user-geo-item .geo-status.pending{color:#ffc107;background:rgba(255,193,7,.15)}.location-panel .location-user-list .user-geo-item .geo-status.granted{color:#4caf50;background:rgba(76,175,80,.15)}.location-panel .location-user-list .user-geo-item .geo-status.denied{color:#f44336;background:rgba(244,67,54,.15)}.location-panel .location-user-list .user-geo-item .geo-status i{font-size:10px}.location-panel .geo-denied-warning{display:flex;align-items:center;gap:.5rem;padding:.75rem;background:rgba(244,67,54,.15);border-radius:6px;margin-bottom:1rem;color:#f44336;font-size:13px}.location-panel .geo-denied-warning i{font-size:14px}.location-panel .location-content{flex:1;display:flex;flex-direction:column;justify-content:flex-end}.location-panel .verify-location-btn{width:100%;padding:12px 24px;background:var(--Primary-Uell, #1da4b1);color:#fff;border:none;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer;transition:background .2s ease;margin-bottom:1rem}.location-panel .verify-location-btn:hover{background:#178e99}.location-panel .verify-location-btn:disabled{background:rgba(29,164,177,.5);cursor:not-allowed}.location-panel .location-footer{display:flex;justify-content:flex-end;gap:.5rem;padding-top:.5rem;border-top:1px solid rgba(255,255,255,.1)}.location-panel .location-footer .footer-icon{width:32px;height:32px;border-radius:50%;display:flex;align-items:center;justify-content:center;background:rgba(255,255,255,.1);color:#fff;font-size:14px}.location-panel .location-footer .footer-icon.location-icon{background:var(--Primary-Uell, #1da4b1)}.location-panel .header-icon{color:#fff;font-size:16px}.location-panel .geo-loading-container{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem 0}.location-panel .geo-loading-container .geo-spinner{width:64px;height:64px;border:4px solid rgba(29,164,177,.2);border-top-color:var(--Primary-Uell, #1da4b1);border-radius:50%;animation:geo-spin 1s linear infinite}.location-panel .geo-loading-container .loading-title{color:#fff;font-size:16px;font-weight:600;margin:1.5rem 0 .25rem}.location-panel .geo-loading-container .loading-subtitle{color:#ffffffb3;font-size:14px;margin:0}.location-panel .geo-success-container{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem 0}.location-panel .geo-success-container .success-icon{width:80px;height:80px;border-radius:50%;background:var(--Primary-Uell, #1da4b1);display:flex;align-items:center;justify-content:center;position:relative}.location-panel .geo-success-container .success-icon i{color:#fff;font-size:32px}.location-panel .geo-success-container .success-icon:before,.location-panel .geo-success-container .success-icon:after{content:\"\\2726\";position:absolute;color:#fff;font-size:10px}.location-panel .geo-success-container .success-icon:before{top:-8px;right:-4px}.location-panel .geo-success-container .success-icon:after{bottom:-4px;left:-8px}.location-panel .geo-success-container .success-title{color:#fff;font-size:16px;font-weight:600;margin:1.5rem 0 0}.location-panel .verify-location-btn.disabled{background:rgba(29,164,177,.5);cursor:not-allowed}@keyframes geo-spin{to{transform:rotate(360deg)}}.user-geo-panel{width:360px;height:100%;background:#212532;border-radius:8px;padding:1.5rem;display:flex;flex-direction:column;color:#fff}.user-geo-panel .user-geo-header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:2.5rem;padding-top:2rem}.user-geo-panel .user-geo-header .header-title-row{display:flex;align-items:center;gap:.5rem}.user-geo-panel .user-geo-header .header-title-row .header-icon{font-size:16px;color:#fff}.user-geo-panel .user-geo-header .header-title-row h3{font-size:18px;font-weight:600;margin:0;color:#fff}.user-geo-panel .user-geo-header .close-btn{background:transparent;border:none;color:#fff;font-size:20px;cursor:pointer;padding:0;line-height:1;opacity:.7}.user-geo-panel .user-geo-header .close-btn:hover{opacity:1}.user-geo-panel .user-geo-description{font-size:14px;color:#fffc;line-height:1.6;margin-bottom:1rem}.user-geo-panel .user-geo-description p{margin:0 0 .75rem}.user-geo-panel .user-geo-content{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center}.user-geo-panel .user-geo-verifying{display:flex;flex-direction:column;align-items:center;text-align:center}.user-geo-panel .user-geo-verifying .geo-spinner-large{width:80px;height:80px;border:4px solid rgba(29,164,177,.2);border-top-color:var(--Primary-Uell, #1da4b1);border-radius:50%;animation:geo-spin 1s linear infinite;margin-bottom:1.5rem}.user-geo-panel .user-geo-verifying .verifying-title{color:#fff;font-size:16px;font-weight:600;margin:0 0 .25rem}.user-geo-panel .user-geo-verifying .verifying-subtitle{color:#fff9;font-size:14px;margin:0}.user-geo-panel .user-geo-verified{display:flex;flex-direction:column;align-items:center;text-align:center}.user-geo-panel .user-geo-verified .verified-icon-container{margin-bottom:1rem}.user-geo-panel .user-geo-verified .verified-icon-container .home-icon{display:block}.user-geo-panel .user-geo-verified .verified-icon-container .home-icon svg{width:120px;height:100px}.user-geo-panel .user-geo-verified .verified-title{color:#fff;font-size:16px;font-weight:600;margin:0}.user-geo-panel .user-geo-denied{display:flex;flex-direction:column;align-items:center;text-align:center}.user-geo-panel .user-geo-denied .denied-icon-container{width:100px;height:100px;border-radius:50%;background:rgba(244,67,54,.2);display:flex;align-items:center;justify-content:center;margin-bottom:1.5rem}.user-geo-panel .user-geo-denied .denied-icon-container i{font-size:48px;color:#f44336}.user-geo-panel .user-geo-denied .denied-title{color:#fff;font-size:16px;font-weight:600;margin:0 0 .5rem}.user-geo-panel .user-geo-denied .denied-subtitle{color:#fff9;font-size:14px;margin:0}.user-geo-panel .user-geo-btn{width:100%;padding:14px 24px;background:var(--Primary-Uell, #1da4b1);color:#fff;border:none;border-radius:24px;font-size:14px;font-weight:600;cursor:pointer;transition:all .2s ease;margin-top:auto;margin-bottom:1rem}.user-geo-panel .user-geo-btn:hover:not(.disabled){background:#178e99}.user-geo-panel .user-geo-btn.disabled{background:rgba(29,164,177,.5);cursor:not-allowed}.user-geo-panel .dev-controls{display:flex;align-items:center;gap:.5rem;padding:.75rem;background:rgba(0,0,0,.3);border-radius:6px;margin-bottom:1rem;flex-wrap:wrap}.user-geo-panel .dev-controls .dev-label{font-size:12px;color:#fff9;font-weight:600}.user-geo-panel .dev-controls .dev-btn{padding:4px 8px;font-size:11px;background:rgba(255,255,255,.1);color:#fff;border:1px solid rgba(255,255,255,.2);border-radius:4px;cursor:pointer;transition:all .2s ease}.user-geo-panel .dev-controls .dev-btn:hover{background:rgba(255,255,255,.2)}.user-geo-panel .user-geo-footer{display:flex;justify-content:center;gap:.75rem;padding-top:.5rem}.user-geo-panel .user-geo-footer .footer-icon{width:40px;height:40px;border-radius:50%;display:flex;align-items:center;justify-content:center;background:rgba(255,255,255,.1);color:#fff9;font-size:16px}.user-geo-panel .user-geo-footer .footer-icon.active{background:var(--Primary-Uell, #1da4b1);color:#fff}\n"], components: [{ type: TasAvatarComponent, selector: "tas-avatar", inputs: ["name", "size"] }], directives: [{ type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
|
|
1402
1705
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.12", ngImport: i0, type: TasVideocallComponent, decorators: [{
|
|
1403
1706
|
type: Component,
|
|
1404
1707
|
args: [{ selector: 'tas-videocall', template: "<div class=\"tas-videocall-wrapper\">\n <div class=\"tas-videocall-container\">\n <!-- Subscriber video (large, background) -->\n <div\n id=\"subscriber-container\"\n [class.subscriber-view]=\"isPublisherSmall\"\n [class.publisher-view]=\"!isPublisherSmall\"\n #subscriberContainer\n (dblclick)=\"onDoubleClick()\"\n ></div>\n\n <!-- Publisher video (small, overlay) -->\n <div\n id=\"publisher-container\"\n [class.publisher-view]=\"isPublisherSmall\"\n [class.subscriber-view]=\"!isPublisherSmall\"\n #publisherContainer\n (dblclick)=\"onDoubleClick()\"\n ></div>\n\n <!-- Centered avatar (shown when no video stream) -->\n <div class=\"avatar-container\" *ngIf=\"!hasVideoStream\">\n <tas-avatar [name]=\"participantName\" [size]=\"80\"></tas-avatar>\n </div>\n\n <!-- Controls -->\n <div class=\"controls-container\">\n <button\n class=\"btn control-btn mute-btn\"\n [class.muted]=\"isMuted\"\n (click)=\"toggleMute()\"\n [title]=\"isMuted ? 'Activar micr\u00F3fono' : 'Silenciar micr\u00F3fono'\"\n [attr.aria-label]=\"isMuted ? 'Activar micr\u00F3fono' : 'Silenciar micr\u00F3fono'\"\n >\n <i\n class=\"fa\"\n [class.fa-microphone]=\"!isMuted\"\n [class.fa-microphone-slash]=\"isMuted\"\n ></i>\n </button>\n <button\n class=\"btn control-btn swap-btn\"\n (click)=\"toggleSwap()\"\n title=\"Intercambiar vista\"\n aria-label=\"Intercambiar vista\"\n >\n <i class=\"fa fa-refresh\"></i>\n </button>\n <button\n class=\"btn control-btn pip-btn\"\n (click)=\"minimize()\"\n title=\"Minimizar (Picture in Picture)\"\n aria-label=\"Minimizar videollamada\"\n >\n <i class=\"fa fa-compress\"></i>\n </button>\n <button\n class=\"btn control-btn hangup-btn\"\n (click)=\"hangUp()\"\n title=\"Finalizar llamada\"\n aria-label=\"Finalizar llamada\"\n >\n <i class=\"fa fa-phone\"></i>\n </button>\n </div>\n\n <!-- Waiting room notification (shown for OWNER/BACKOFFICE only) -->\n <div\n class=\"waiting-notification\"\n *ngIf=\"waitingRoomUsers.length > 0 && canAdmitUsers\"\n role=\"alert\"\n aria-live=\"polite\"\n >\n <span class=\"waiting-text\">\n {{ waitingRoomUsers[0].name }} est\u00E1 en la sala de espera.\n </span>\n <button\n class=\"admit-btn\"\n (click)=\"admitUser(waitingRoomUsers[0].userId)\"\n aria-label=\"Admitir usuario\"\n >\n Admitir\n </button>\n <button\n class=\"dismiss-btn\"\n (click)=\"dismissWaitingNotification(waitingRoomUsers[0].userId)\"\n aria-label=\"Cerrar notificaci\u00F3n\"\n >\n \u00D7\n </button>\n </div>\n </div>\n\n <!-- Owner geolocation panel (for owners to request user location) -->\n <div class=\"user-geo-panel\" *ngIf=\"shouldShowUserGeoPanel || devModeEnabled\">\n <div class=\"user-geo-header\">\n <div class=\"header-title-row\">\n <i class=\"fa fa-map-marker header-icon\" *ngIf=\"userGeoViewState !== UserGeoViewState.INITIAL\"></i>\n <h3>Ubicaci\u00F3n del colaborador</h3>\n </div>\n <button class=\"close-btn\" (click)=\"setUserGeoViewState(UserGeoViewState.HIDDEN)\" aria-label=\"Cerrar\">\u00D7</button>\n </div>\n\n <div class=\"user-geo-description\" *ngIf=\"userGeoViewState !== UserGeoViewState.VERIFIED && userGeoViewState !== UserGeoViewState.DENIED\">\n <p>El colaborador tiene la ubicaci\u00F3n desactivada, solicita que la active.</p>\n <p>Esta acci\u00F3n nos permitir\u00E1 disponibilizar algunas alertas.</p>\n </div>\n\n <div class=\"user-geo-content\">\n <!-- INITIAL state: just the button -->\n <ng-container *ngIf=\"userGeoViewState === UserGeoViewState.INITIAL\">\n <!-- spacer -->\n </ng-container>\n\n <!-- VERIFYING state: spinner + loading message -->\n <ng-container *ngIf=\"userGeoViewState === UserGeoViewState.VERIFYING\">\n <div class=\"user-geo-verifying\">\n <div class=\"geo-spinner-large\"></div>\n <p class=\"verifying-title\">Verificando ubicaci\u00F3n...</p>\n <p class=\"verifying-subtitle\">Esto puede tardar unos segundos.</p>\n </div>\n </ng-container>\n\n <!-- VERIFIED state: home icon with sparkles -->\n <ng-container *ngIf=\"userGeoViewState === UserGeoViewState.VERIFIED\">\n <div class=\"user-geo-verified\">\n <div class=\"verified-icon-container\">\n <span class=\"home-icon\" [innerHTML]=\"homeIcon\"></span>\n </div>\n <p class=\"verified-title\">La ubicaci\u00F3n fue verificada</p>\n </div>\n </ng-container>\n\n <!-- DENIED state: error icon and message -->\n <ng-container *ngIf=\"userGeoViewState === UserGeoViewState.DENIED\">\n <div class=\"user-geo-denied\">\n <div class=\"denied-icon-container\">\n <i class=\"fa fa-times-circle\"></i>\n </div>\n <p class=\"denied-title\">La ubicaci\u00F3n fue rechazada</p>\n <p class=\"denied-subtitle\">El colaborador no permiti\u00F3 el acceso a su ubicaci\u00F3n.</p>\n </div>\n </ng-container>\n </div>\n\n <!-- Button (hidden in verified and denied states) -->\n <button \n class=\"user-geo-btn\" \n *ngIf=\"userGeoViewState !== UserGeoViewState.VERIFIED && userGeoViewState !== UserGeoViewState.DENIED\"\n [class.disabled]=\"userGeoViewState === UserGeoViewState.VERIFYING\"\n [disabled]=\"userGeoViewState === UserGeoViewState.VERIFYING\"\n (click)=\"requestUserLocation()\">\n Verificar ubicaci\u00F3n\n </button>\n\n <!-- Dev controls -->\n <div class=\"dev-controls\" *ngIf=\"devModeEnabled\">\n <span class=\"dev-label\">Dev:</span>\n <button class=\"dev-btn\" (click)=\"setUserGeoViewState(UserGeoViewState.INITIAL)\">Initial</button>\n <button class=\"dev-btn\" (click)=\"setUserGeoViewState(UserGeoViewState.VERIFYING)\">Verifying</button>\n <button class=\"dev-btn\" (click)=\"setUserGeoViewState(UserGeoViewState.VERIFIED)\">Verified</button>\n <button class=\"dev-btn\" (click)=\"setUserGeoViewState(UserGeoViewState.DENIED)\">Denied</button>\n <button class=\"dev-btn\" (click)=\"setUserGeoViewState(UserGeoViewState.HIDDEN)\">Hide</button>\n </div>\n </div>\n</div>\n\n", styles: ["@charset \"UTF-8\";:host{display:flex;width:100vw;height:100vh;box-sizing:border-box;padding:2rem;background:linear-gradient(281deg,rgba(29,164,177,.2) 6.96%,rgba(0,0,0,0) 70.44%),#212532}.tas-videocall-wrapper{display:flex;flex:1;gap:1rem;height:100%}.tas-videocall-container{position:relative;flex:1;height:100%;overflow:hidden;border-radius:8px;border:1px solid var(--Neutral-GreyLight, #dadfe9);background:linear-gradient(180deg,#e5f1f7 0%,#0072ac 100%)}.tas-videocall-container ::ng-deep .OT_edge-bar-item,.tas-videocall-container ::ng-deep .OT_mute,.tas-videocall-container ::ng-deep .OT_audio-level-meter,.tas-videocall-container ::ng-deep .OT_bar,.tas-videocall-container ::ng-deep .OT_name{display:none!important}.tas-videocall-container .subscriber-view{width:100%;height:100%;z-index:1}.tas-videocall-container .publisher-view{position:absolute;top:20px;right:20px;width:200px;height:150px;z-index:2;border:2px solid #fff;border-radius:8px;background-color:#0000004d;overflow:hidden}.tas-videocall-container .avatar-container{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:1;display:flex;align-items:center;justify-content:center}.tas-videocall-container .controls-container{display:flex;flex-direction:row;gap:12px;position:absolute;bottom:30px;left:50%;transform:translate(-50%);z-index:3;background-color:#33475bb3;padding:12px 20px;border-radius:30px;backdrop-filter:blur(8px)}.tas-videocall-container .controls-container .control-btn{width:44px;height:44px;border-radius:20px;display:flex;align-items:center;justify-content:center;font-size:18px;border:none;background:transparent;cursor:pointer;transition:all .2s ease}.tas-videocall-container .controls-container .control-btn i{color:#fff}.tas-videocall-container .controls-container .control-btn:hover{transform:scale(1.05);filter:brightness(1.1)}.tas-videocall-container .controls-container .control-btn:focus{outline:2px solid #fff;outline-offset:2px}.tas-videocall-container .controls-container .hangup-btn{background:#f44336}.tas-videocall-container .controls-container .hangup-btn i{transform:rotate(135deg)}.tas-videocall-container .controls-container .hangup-btn:hover{background:#d32f2f}.tas-videocall-container .controls-container .swap-btn,.tas-videocall-container .controls-container .pip-btn,.tas-videocall-container .controls-container .mute-btn{background:transparent}.tas-videocall-container .controls-container .swap-btn:hover,.tas-videocall-container .controls-container .pip-btn:hover,.tas-videocall-container .controls-container .mute-btn:hover{background:rgba(255,255,255,.15)}.tas-videocall-container .waiting-notification{position:absolute;bottom:100px;left:16px;display:flex;align-items:center;gap:12px;background-color:#33475be6;padding:10px 16px;border-radius:8px;z-index:4;backdrop-filter:blur(4px);max-width:calc(100% - 32px)}.tas-videocall-container .waiting-notification .waiting-text{color:#fff;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tas-videocall-container .waiting-notification .admit-btn{background:var(--Primary-Uell, #1da4b1);color:#fff;border:none;border-radius:4px;padding:6px 16px;font-size:14px;font-weight:500;cursor:pointer;transition:background .2s ease;white-space:nowrap}.tas-videocall-container .waiting-notification .admit-btn:hover{background:#178e99}.tas-videocall-container .waiting-notification .admit-btn:focus{outline:2px solid #fff;outline-offset:2px}.tas-videocall-container .waiting-notification .dismiss-btn{background:transparent;color:#fff;border:none;font-size:20px;line-height:1;cursor:pointer;padding:0 4px;opacity:.7;transition:opacity .2s ease}.tas-videocall-container .waiting-notification .dismiss-btn:hover{opacity:1}.tas-videocall-container .waiting-notification .dismiss-btn:focus{outline:2px solid #fff;outline-offset:2px}.location-panel{width:280px;height:100%;background:#212532;border-radius:8px;padding:1.5rem;display:flex;flex-direction:column;color:#fff}.location-panel .location-header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:1rem}.location-panel .location-header h3{font-size:16px;font-weight:600;margin:0;color:#fff}.location-panel .location-header .close-btn{background:transparent;border:none;color:#fff;font-size:20px;cursor:pointer;padding:0;line-height:1;opacity:.7}.location-panel .location-header .close-btn:hover{opacity:1}.location-panel .location-description{font-size:14px;color:#fffc;line-height:1.5;margin-bottom:.5rem}.location-panel .location-description p{margin:0 0 .5rem}.location-panel .location-user-list{display:flex;flex-direction:column;gap:.5rem;margin-bottom:1rem;max-height:200px;overflow-y:auto}.location-panel .location-user-list .user-geo-item{display:flex;align-items:center;gap:.75rem;padding:.5rem .75rem;background:rgba(255,255,255,.05);border-radius:6px}.location-panel .location-user-list .user-geo-item .user-icon{color:#fff9;font-size:14px}.location-panel .location-user-list .user-geo-item .user-name{flex:1;font-size:14px;color:#fff}.location-panel .location-user-list .user-geo-item .geo-status{display:flex;align-items:center;gap:.35rem;font-size:12px;padding:2px 8px;border-radius:12px}.location-panel .location-user-list .user-geo-item .geo-status.pending{color:#ffc107;background:rgba(255,193,7,.15)}.location-panel .location-user-list .user-geo-item .geo-status.granted{color:#4caf50;background:rgba(76,175,80,.15)}.location-panel .location-user-list .user-geo-item .geo-status.denied{color:#f44336;background:rgba(244,67,54,.15)}.location-panel .location-user-list .user-geo-item .geo-status i{font-size:10px}.location-panel .geo-denied-warning{display:flex;align-items:center;gap:.5rem;padding:.75rem;background:rgba(244,67,54,.15);border-radius:6px;margin-bottom:1rem;color:#f44336;font-size:13px}.location-panel .geo-denied-warning i{font-size:14px}.location-panel .location-content{flex:1;display:flex;flex-direction:column;justify-content:flex-end}.location-panel .verify-location-btn{width:100%;padding:12px 24px;background:var(--Primary-Uell, #1da4b1);color:#fff;border:none;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer;transition:background .2s ease;margin-bottom:1rem}.location-panel .verify-location-btn:hover{background:#178e99}.location-panel .verify-location-btn:disabled{background:rgba(29,164,177,.5);cursor:not-allowed}.location-panel .location-footer{display:flex;justify-content:flex-end;gap:.5rem;padding-top:.5rem;border-top:1px solid rgba(255,255,255,.1)}.location-panel .location-footer .footer-icon{width:32px;height:32px;border-radius:50%;display:flex;align-items:center;justify-content:center;background:rgba(255,255,255,.1);color:#fff;font-size:14px}.location-panel .location-footer .footer-icon.location-icon{background:var(--Primary-Uell, #1da4b1)}.location-panel .header-icon{color:#fff;font-size:16px}.location-panel .geo-loading-container{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem 0}.location-panel .geo-loading-container .geo-spinner{width:64px;height:64px;border:4px solid rgba(29,164,177,.2);border-top-color:var(--Primary-Uell, #1da4b1);border-radius:50%;animation:geo-spin 1s linear infinite}.location-panel .geo-loading-container .loading-title{color:#fff;font-size:16px;font-weight:600;margin:1.5rem 0 .25rem}.location-panel .geo-loading-container .loading-subtitle{color:#ffffffb3;font-size:14px;margin:0}.location-panel .geo-success-container{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem 0}.location-panel .geo-success-container .success-icon{width:80px;height:80px;border-radius:50%;background:var(--Primary-Uell, #1da4b1);display:flex;align-items:center;justify-content:center;position:relative}.location-panel .geo-success-container .success-icon i{color:#fff;font-size:32px}.location-panel .geo-success-container .success-icon:before,.location-panel .geo-success-container .success-icon:after{content:\"\\2726\";position:absolute;color:#fff;font-size:10px}.location-panel .geo-success-container .success-icon:before{top:-8px;right:-4px}.location-panel .geo-success-container .success-icon:after{bottom:-4px;left:-8px}.location-panel .geo-success-container .success-title{color:#fff;font-size:16px;font-weight:600;margin:1.5rem 0 0}.location-panel .verify-location-btn.disabled{background:rgba(29,164,177,.5);cursor:not-allowed}@keyframes geo-spin{to{transform:rotate(360deg)}}.user-geo-panel{width:360px;height:100%;background:#212532;border-radius:8px;padding:1.5rem;display:flex;flex-direction:column;color:#fff}.user-geo-panel .user-geo-header{display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:2.5rem;padding-top:2rem}.user-geo-panel .user-geo-header .header-title-row{display:flex;align-items:center;gap:.5rem}.user-geo-panel .user-geo-header .header-title-row .header-icon{font-size:16px;color:#fff}.user-geo-panel .user-geo-header .header-title-row h3{font-size:18px;font-weight:600;margin:0;color:#fff}.user-geo-panel .user-geo-header .close-btn{background:transparent;border:none;color:#fff;font-size:20px;cursor:pointer;padding:0;line-height:1;opacity:.7}.user-geo-panel .user-geo-header .close-btn:hover{opacity:1}.user-geo-panel .user-geo-description{font-size:14px;color:#fffc;line-height:1.6;margin-bottom:1rem}.user-geo-panel .user-geo-description p{margin:0 0 .75rem}.user-geo-panel .user-geo-content{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center}.user-geo-panel .user-geo-verifying{display:flex;flex-direction:column;align-items:center;text-align:center}.user-geo-panel .user-geo-verifying .geo-spinner-large{width:80px;height:80px;border:4px solid rgba(29,164,177,.2);border-top-color:var(--Primary-Uell, #1da4b1);border-radius:50%;animation:geo-spin 1s linear infinite;margin-bottom:1.5rem}.user-geo-panel .user-geo-verifying .verifying-title{color:#fff;font-size:16px;font-weight:600;margin:0 0 .25rem}.user-geo-panel .user-geo-verifying .verifying-subtitle{color:#fff9;font-size:14px;margin:0}.user-geo-panel .user-geo-verified{display:flex;flex-direction:column;align-items:center;text-align:center}.user-geo-panel .user-geo-verified .verified-icon-container{margin-bottom:1rem}.user-geo-panel .user-geo-verified .verified-icon-container .home-icon{display:block}.user-geo-panel .user-geo-verified .verified-icon-container .home-icon svg{width:120px;height:100px}.user-geo-panel .user-geo-verified .verified-title{color:#fff;font-size:16px;font-weight:600;margin:0}.user-geo-panel .user-geo-denied{display:flex;flex-direction:column;align-items:center;text-align:center}.user-geo-panel .user-geo-denied .denied-icon-container{width:100px;height:100px;border-radius:50%;background:rgba(244,67,54,.2);display:flex;align-items:center;justify-content:center;margin-bottom:1.5rem}.user-geo-panel .user-geo-denied .denied-icon-container i{font-size:48px;color:#f44336}.user-geo-panel .user-geo-denied .denied-title{color:#fff;font-size:16px;font-weight:600;margin:0 0 .5rem}.user-geo-panel .user-geo-denied .denied-subtitle{color:#fff9;font-size:14px;margin:0}.user-geo-panel .user-geo-btn{width:100%;padding:14px 24px;background:var(--Primary-Uell, #1da4b1);color:#fff;border:none;border-radius:24px;font-size:14px;font-weight:600;cursor:pointer;transition:all .2s ease;margin-top:auto;margin-bottom:1rem}.user-geo-panel .user-geo-btn:hover:not(.disabled){background:#178e99}.user-geo-panel .user-geo-btn.disabled{background:rgba(29,164,177,.5);cursor:not-allowed}.user-geo-panel .dev-controls{display:flex;align-items:center;gap:.5rem;padding:.75rem;background:rgba(0,0,0,.3);border-radius:6px;margin-bottom:1rem;flex-wrap:wrap}.user-geo-panel .dev-controls .dev-label{font-size:12px;color:#fff9;font-weight:600}.user-geo-panel .dev-controls .dev-btn{padding:4px 8px;font-size:11px;background:rgba(255,255,255,.1);color:#fff;border:1px solid rgba(255,255,255,.2);border-radius:4px;cursor:pointer;transition:all .2s ease}.user-geo-panel .dev-controls .dev-btn:hover{background:rgba(255,255,255,.2)}.user-geo-panel .user-geo-footer{display:flex;justify-content:center;gap:.75rem;padding-top:.5rem}.user-geo-panel .user-geo-footer .footer-icon{width:40px;height:40px;border-radius:50%;display:flex;align-items:center;justify-content:center;background:rgba(255,255,255,.1);color:#fff9;font-size:16px}.user-geo-panel .user-geo-footer .footer-icon.active{background:var(--Primary-Uell, #1da4b1);color:#fff}\n"] }]
|
|
1405
|
-
}], ctorParameters: function () { return [{ type: i1.NgbActiveModal }, { type: TasService }, { type: GeolocationService }, { type: i4.DomSanitizer }]; }, propDecorators: { sessionId: [{
|
|
1708
|
+
}], ctorParameters: function () { return [{ type: i1.NgbActiveModal }, { type: TasService }, { type: GeolocationService }, { type: i4$2.DomSanitizer }, { type: i1.NgbModal }]; }, propDecorators: { sessionId: [{
|
|
1406
1709
|
type: Input
|
|
1407
1710
|
}], token: [{
|
|
1408
1711
|
type: Input
|
|
@@ -1451,6 +1754,7 @@ class TasWaitingRoomComponent {
|
|
|
1451
1754
|
this.state = WaitingRoomState.CHECKING_STATUS;
|
|
1452
1755
|
this.WaitingRoomState = WaitingRoomState; // Expose enum to template
|
|
1453
1756
|
this.errorMessage = '';
|
|
1757
|
+
this.showErrorDetails = false;
|
|
1454
1758
|
this.isJoinable = false;
|
|
1455
1759
|
// Session data from status response
|
|
1456
1760
|
this.resolvedSessionId = '';
|
|
@@ -1795,10 +2099,24 @@ class TasWaitingRoomComponent {
|
|
|
1795
2099
|
retry() {
|
|
1796
2100
|
this.state = WaitingRoomState.CHECKING_STATUS;
|
|
1797
2101
|
this.errorMessage = '';
|
|
2102
|
+
this.showErrorDetails = false;
|
|
1798
2103
|
this.token = '';
|
|
1799
2104
|
this.retryCount = 0; // Reset retry count for auto-retry
|
|
1800
2105
|
this.checkStatus();
|
|
1801
2106
|
}
|
|
2107
|
+
/**
|
|
2108
|
+
* Toggle the error details dropdown visibility.
|
|
2109
|
+
*/
|
|
2110
|
+
toggleErrorDetails() {
|
|
2111
|
+
this.showErrorDetails = !this.showErrorDetails;
|
|
2112
|
+
}
|
|
2113
|
+
/**
|
|
2114
|
+
* Close the waiting room modal.
|
|
2115
|
+
*/
|
|
2116
|
+
close() {
|
|
2117
|
+
this.tasService.stopStatusPolling();
|
|
2118
|
+
this.activeModal.dismiss('close');
|
|
2119
|
+
}
|
|
1802
2120
|
openVideoCallModal() {
|
|
1803
2121
|
var _a;
|
|
1804
2122
|
this.videoCallModalRef = this.modalService.open(TasVideocallComponent, {
|
|
@@ -1823,10 +2141,10 @@ class TasWaitingRoomComponent {
|
|
|
1823
2141
|
}
|
|
1824
2142
|
}
|
|
1825
2143
|
TasWaitingRoomComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.12", ngImport: i0, type: TasWaitingRoomComponent, deps: [{ token: i1.NgbActiveModal }, { token: TasService }, { token: GeolocationService }, { token: i1.NgbModal }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
1826
|
-
TasWaitingRoomComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.12", type: TasWaitingRoomComponent, selector: "tas-waiting-room", inputs: { roomType: "roomType", entityId: "entityId", tenant: "tenant", businessRole: "businessRole", currentUser: "currentUser" }, ngImport: i0, template: "<div class=\"tas-waiting-room\">\n <div class=\"waiting-room-content\">\n <div class=\"state-container\">\n <!-- Loading states (show spinner) -->\n <ng-container *ngIf=\"state !== WaitingRoomState.ERROR\">\n <div class=\"state-icon loading\">\n <div class=\"spinner\"></div>\n </div>\n \n <!-- Requesting permissions -->\n <ng-container *ngIf=\"isRequestingPermissions\">\n <p class=\"state-message\">Solicitando permisos...</p>\n <p class=\"state-submessage\">Por favor, acept\u00E1 los permisos de c\u00E1mara y micr\u00F3fono.</p>\n </ng-container>\n \n <!-- Waiting for admission (permissions granted, not joinable yet) -->\n <ng-container *ngIf=\"isWaitingForAdmission\">\n <p class=\"state-message\">Medicina laboral va a admitirte</p>\n <p class=\"state-submessage\">Por favor, permanec\u00E9 en esta pantalla.</p>\n </ng-container>\n \n <!-- Getting token / Joining -->\n <ng-container *ngIf=\"state === WaitingRoomState.GETTING_TOKEN || state === WaitingRoomState.JOINING\">\n <p class=\"state-message\">Conectando...</p>\n </ng-container>\n \n <!-- Checking status (when not requesting permissions) -->\n <ng-container *ngIf=\"state === WaitingRoomState.CHECKING_STATUS && !isRequestingPermissions\">\n <p class=\"state-message\">Verificando estado...</p>\n </ng-container>\n </ng-container>\n\n <!-- Error state (show error icon and message) -->\n <ng-container *ngIf=\"state === WaitingRoomState.ERROR\">\n <div class=\"state-icon error\">\n <i class=\"fa fa-exclamation-triangle\"></i>\n </div>\n <p class=\"state-message error\">Ocurri\u00F3 un error</p>\n <
|
|
2144
|
+
TasWaitingRoomComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.12", type: TasWaitingRoomComponent, selector: "tas-waiting-room", inputs: { roomType: "roomType", entityId: "entityId", tenant: "tenant", businessRole: "businessRole", currentUser: "currentUser" }, ngImport: i0, template: "<div class=\"tas-waiting-room\">\n <div class=\"waiting-room-content\">\n <div class=\"state-container\">\n <!-- Loading states (show spinner) -->\n <ng-container *ngIf=\"state !== WaitingRoomState.ERROR\">\n <div class=\"state-icon loading\">\n <div class=\"spinner\"></div>\n </div>\n \n <!-- Requesting permissions -->\n <ng-container *ngIf=\"isRequestingPermissions\">\n <p class=\"state-message\">Solicitando permisos...</p>\n <p class=\"state-submessage\">Por favor, acept\u00E1 los permisos de c\u00E1mara y micr\u00F3fono.</p>\n </ng-container>\n \n <!-- Waiting for admission (permissions granted, not joinable yet) -->\n <ng-container *ngIf=\"isWaitingForAdmission\">\n <p class=\"state-message\">Medicina laboral va a admitirte</p>\n <p class=\"state-submessage\">Por favor, permanec\u00E9 en esta pantalla.</p>\n </ng-container>\n \n <!-- Getting token / Joining -->\n <ng-container *ngIf=\"state === WaitingRoomState.GETTING_TOKEN || state === WaitingRoomState.JOINING\">\n <p class=\"state-message\">Conectando...</p>\n </ng-container>\n \n <!-- Checking status (when not requesting permissions) -->\n <ng-container *ngIf=\"state === WaitingRoomState.CHECKING_STATUS && !isRequestingPermissions\">\n <p class=\"state-message\">Verificando estado...</p>\n </ng-container>\n </ng-container>\n\n <!-- Error state (show error icon and message) -->\n <ng-container *ngIf=\"state === WaitingRoomState.ERROR\">\n <div class=\"state-icon error\">\n <i class=\"fa fa-exclamation-triangle\"></i>\n </div>\n <p class=\"state-message error\">Ocurri\u00F3 un error</p>\n \n <!-- Collapsible error details -->\n <div class=\"error-dropdown\" *ngIf=\"errorMessage\">\n <button \n type=\"button\" \n class=\"error-toggle-btn\"\n (click)=\"toggleErrorDetails()\"\n [attr.aria-expanded]=\"showErrorDetails\"\n aria-controls=\"error-details-content\"\n >\n <span>Ver detalles del error</span>\n <i class=\"fa\" [class.fa-chevron-down]=\"!showErrorDetails\" [class.fa-chevron-up]=\"showErrorDetails\"></i>\n </button>\n <div \n id=\"error-details-content\"\n class=\"error-details-content\"\n [class.expanded]=\"showErrorDetails\"\n >\n <p class=\"error-details-text\">{{ errorMessage }}</p>\n </div>\n </div>\n \n <div class=\"error-actions\">\n <button type=\"button\" class=\"btn action-btn retry-btn\" (click)=\"retry()\">\n <i class=\"fa fa-refresh\"></i>\n Reintentar\n </button>\n <button type=\"button\" class=\"btn action-btn close-btn\" (click)=\"close()\">\n Cerrar\n </button>\n </div>\n </ng-container>\n </div>\n </div>\n</div>\n", styles: [".tas-waiting-room{display:flex;flex-direction:column;min-height:300px;background:#ffffff;border-radius:5px;overflow:hidden}.waiting-room-content{flex:1;display:flex;align-items:center;justify-content:center;padding:32px 40px;background:#ffffff}.state-container{text-align:center;max-width:400px;width:100%}.state-icon{width:80px;height:80px;margin:0 auto 24px;border-radius:50%;display:flex;align-items:center;justify-content:center}.state-icon i{font-size:36px}.state-icon.loading{background:rgba(29,164,177,.1);border:2px solid #1da4b1}.state-icon.error{background:rgba(238,49,107,.1);border:2px solid #ee316b}.state-icon.error i{color:#ee316b}.spinner{width:40px;height:40px;border:3px solid #e9ecef;border-top-color:#1da4b1;border-radius:50%;animation:spin 1s linear infinite}.state-message{font-size:16px;font-weight:600;margin:0 0 8px;color:#212529;line-height:24px}.state-message.error{color:#ee316b}.state-submessage{font-size:14px;color:#6c757d;margin:0 0 24px;font-weight:400}.error-dropdown{margin:16px 0;width:100%}.error-toggle-btn{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;padding:10px 16px;font-size:13px;font-weight:500;color:#6c757d;background:transparent;border:1px solid #e9ecef;border-radius:6px;cursor:pointer;transition:all .2s ease}.error-toggle-btn:hover{background:#f8f9fa;border-color:#6c757d;color:#212529}.error-toggle-btn i{font-size:12px;transition:transform .2s ease}.error-details-content{max-height:0;overflow:hidden;transition:max-height .3s ease,padding .3s ease}.error-details-content.expanded{max-height:200px;padding-top:12px}.error-details-text{font-size:12px;color:#ee316b;margin:0;padding:12px 16px;background:rgba(238,49,107,.08);border-radius:8px;border:1px solid rgba(238,49,107,.2);text-align:left;word-break:break-word;max-height:150px;overflow-y:auto}.error-actions{display:flex;justify-content:center;gap:12px;margin-top:16px}.action-btn{padding:12px 32px;font-size:16px;font-weight:600;border-radius:4px;border:none;cursor:pointer;transition:all .2s ease;display:inline-flex;align-items:center;gap:10px}.action-btn i{font-size:16px}.action-btn.retry-btn{background:transparent;color:#6c757d;border:1px solid #e9ecef}.action-btn.retry-btn:hover{background:#f8f9fa;border-color:#6c757d;color:#212529}.action-btn.close-btn{background:#ee316b;color:#fff;border:none}.action-btn.close-btn:hover{background:#da124f}@keyframes spin{to{transform:rotate(360deg)}}@media (max-width: 576px){.tas-waiting-room{min-height:250px}.waiting-room-content{padding:24px}.state-icon{width:64px;height:64px}.state-icon i{font-size:28px}.spinner{width:32px;height:32px}.action-btn{padding:10px 24px;font-size:14px}}\n"], directives: [{ type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
|
|
1827
2145
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.12", ngImport: i0, type: TasWaitingRoomComponent, decorators: [{
|
|
1828
2146
|
type: Component,
|
|
1829
|
-
args: [{ selector: 'tas-waiting-room', template: "<div class=\"tas-waiting-room\">\n <div class=\"waiting-room-content\">\n <div class=\"state-container\">\n <!-- Loading states (show spinner) -->\n <ng-container *ngIf=\"state !== WaitingRoomState.ERROR\">\n <div class=\"state-icon loading\">\n <div class=\"spinner\"></div>\n </div>\n \n <!-- Requesting permissions -->\n <ng-container *ngIf=\"isRequestingPermissions\">\n <p class=\"state-message\">Solicitando permisos...</p>\n <p class=\"state-submessage\">Por favor, acept\u00E1 los permisos de c\u00E1mara y micr\u00F3fono.</p>\n </ng-container>\n \n <!-- Waiting for admission (permissions granted, not joinable yet) -->\n <ng-container *ngIf=\"isWaitingForAdmission\">\n <p class=\"state-message\">Medicina laboral va a admitirte</p>\n <p class=\"state-submessage\">Por favor, permanec\u00E9 en esta pantalla.</p>\n </ng-container>\n \n <!-- Getting token / Joining -->\n <ng-container *ngIf=\"state === WaitingRoomState.GETTING_TOKEN || state === WaitingRoomState.JOINING\">\n <p class=\"state-message\">Conectando...</p>\n </ng-container>\n \n <!-- Checking status (when not requesting permissions) -->\n <ng-container *ngIf=\"state === WaitingRoomState.CHECKING_STATUS && !isRequestingPermissions\">\n <p class=\"state-message\">Verificando estado...</p>\n </ng-container>\n </ng-container>\n\n <!-- Error state (show error icon and message) -->\n <ng-container *ngIf=\"state === WaitingRoomState.ERROR\">\n <div class=\"state-icon error\">\n <i class=\"fa fa-exclamation-triangle\"></i>\n </div>\n <p class=\"state-message error\">Ocurri\u00F3 un error</p>\n <
|
|
2147
|
+
args: [{ selector: 'tas-waiting-room', template: "<div class=\"tas-waiting-room\">\n <div class=\"waiting-room-content\">\n <div class=\"state-container\">\n <!-- Loading states (show spinner) -->\n <ng-container *ngIf=\"state !== WaitingRoomState.ERROR\">\n <div class=\"state-icon loading\">\n <div class=\"spinner\"></div>\n </div>\n \n <!-- Requesting permissions -->\n <ng-container *ngIf=\"isRequestingPermissions\">\n <p class=\"state-message\">Solicitando permisos...</p>\n <p class=\"state-submessage\">Por favor, acept\u00E1 los permisos de c\u00E1mara y micr\u00F3fono.</p>\n </ng-container>\n \n <!-- Waiting for admission (permissions granted, not joinable yet) -->\n <ng-container *ngIf=\"isWaitingForAdmission\">\n <p class=\"state-message\">Medicina laboral va a admitirte</p>\n <p class=\"state-submessage\">Por favor, permanec\u00E9 en esta pantalla.</p>\n </ng-container>\n \n <!-- Getting token / Joining -->\n <ng-container *ngIf=\"state === WaitingRoomState.GETTING_TOKEN || state === WaitingRoomState.JOINING\">\n <p class=\"state-message\">Conectando...</p>\n </ng-container>\n \n <!-- Checking status (when not requesting permissions) -->\n <ng-container *ngIf=\"state === WaitingRoomState.CHECKING_STATUS && !isRequestingPermissions\">\n <p class=\"state-message\">Verificando estado...</p>\n </ng-container>\n </ng-container>\n\n <!-- Error state (show error icon and message) -->\n <ng-container *ngIf=\"state === WaitingRoomState.ERROR\">\n <div class=\"state-icon error\">\n <i class=\"fa fa-exclamation-triangle\"></i>\n </div>\n <p class=\"state-message error\">Ocurri\u00F3 un error</p>\n \n <!-- Collapsible error details -->\n <div class=\"error-dropdown\" *ngIf=\"errorMessage\">\n <button \n type=\"button\" \n class=\"error-toggle-btn\"\n (click)=\"toggleErrorDetails()\"\n [attr.aria-expanded]=\"showErrorDetails\"\n aria-controls=\"error-details-content\"\n >\n <span>Ver detalles del error</span>\n <i class=\"fa\" [class.fa-chevron-down]=\"!showErrorDetails\" [class.fa-chevron-up]=\"showErrorDetails\"></i>\n </button>\n <div \n id=\"error-details-content\"\n class=\"error-details-content\"\n [class.expanded]=\"showErrorDetails\"\n >\n <p class=\"error-details-text\">{{ errorMessage }}</p>\n </div>\n </div>\n \n <div class=\"error-actions\">\n <button type=\"button\" class=\"btn action-btn retry-btn\" (click)=\"retry()\">\n <i class=\"fa fa-refresh\"></i>\n Reintentar\n </button>\n <button type=\"button\" class=\"btn action-btn close-btn\" (click)=\"close()\">\n Cerrar\n </button>\n </div>\n </ng-container>\n </div>\n </div>\n</div>\n", styles: [".tas-waiting-room{display:flex;flex-direction:column;min-height:300px;background:#ffffff;border-radius:5px;overflow:hidden}.waiting-room-content{flex:1;display:flex;align-items:center;justify-content:center;padding:32px 40px;background:#ffffff}.state-container{text-align:center;max-width:400px;width:100%}.state-icon{width:80px;height:80px;margin:0 auto 24px;border-radius:50%;display:flex;align-items:center;justify-content:center}.state-icon i{font-size:36px}.state-icon.loading{background:rgba(29,164,177,.1);border:2px solid #1da4b1}.state-icon.error{background:rgba(238,49,107,.1);border:2px solid #ee316b}.state-icon.error i{color:#ee316b}.spinner{width:40px;height:40px;border:3px solid #e9ecef;border-top-color:#1da4b1;border-radius:50%;animation:spin 1s linear infinite}.state-message{font-size:16px;font-weight:600;margin:0 0 8px;color:#212529;line-height:24px}.state-message.error{color:#ee316b}.state-submessage{font-size:14px;color:#6c757d;margin:0 0 24px;font-weight:400}.error-dropdown{margin:16px 0;width:100%}.error-toggle-btn{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;padding:10px 16px;font-size:13px;font-weight:500;color:#6c757d;background:transparent;border:1px solid #e9ecef;border-radius:6px;cursor:pointer;transition:all .2s ease}.error-toggle-btn:hover{background:#f8f9fa;border-color:#6c757d;color:#212529}.error-toggle-btn i{font-size:12px;transition:transform .2s ease}.error-details-content{max-height:0;overflow:hidden;transition:max-height .3s ease,padding .3s ease}.error-details-content.expanded{max-height:200px;padding-top:12px}.error-details-text{font-size:12px;color:#ee316b;margin:0;padding:12px 16px;background:rgba(238,49,107,.08);border-radius:8px;border:1px solid rgba(238,49,107,.2);text-align:left;word-break:break-word;max-height:150px;overflow-y:auto}.error-actions{display:flex;justify-content:center;gap:12px;margin-top:16px}.action-btn{padding:12px 32px;font-size:16px;font-weight:600;border-radius:4px;border:none;cursor:pointer;transition:all .2s ease;display:inline-flex;align-items:center;gap:10px}.action-btn i{font-size:16px}.action-btn.retry-btn{background:transparent;color:#6c757d;border:1px solid #e9ecef}.action-btn.retry-btn:hover{background:#f8f9fa;border-color:#6c757d;color:#212529}.action-btn.close-btn{background:#ee316b;color:#fff;border:none}.action-btn.close-btn:hover{background:#da124f}@keyframes spin{to{transform:rotate(360deg)}}@media (max-width: 576px){.tas-waiting-room{min-height:250px}.waiting-room-content{padding:24px}.state-icon{width:64px;height:64px}.state-icon i{font-size:28px}.spinner{width:32px;height:32px}.action-btn{padding:10px 24px;font-size:14px}}\n"] }]
|
|
1830
2148
|
}], ctorParameters: function () { return [{ type: i1.NgbActiveModal }, { type: TasService }, { type: GeolocationService }, { type: i1.NgbModal }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { roomType: [{
|
|
1831
2149
|
type: Input
|
|
1832
2150
|
}], entityId: [{
|
|
@@ -1850,9 +2168,6 @@ class TasButtonComponent {
|
|
|
1850
2168
|
// Style customization
|
|
1851
2169
|
this.variant = 'default';
|
|
1852
2170
|
this.buttonLabel = 'Iniciar TAS';
|
|
1853
|
-
// Skip status check - when true, button shows immediately without API call
|
|
1854
|
-
// Useful when parent component already knows the appointment is valid
|
|
1855
|
-
this.skipStatusCheck = false;
|
|
1856
2171
|
this.isLoading = false;
|
|
1857
2172
|
// Status check state
|
|
1858
2173
|
this.isCheckingStatus = false;
|
|
@@ -1897,12 +2212,6 @@ class TasButtonComponent {
|
|
|
1897
2212
|
this.videoCallModalRef = null;
|
|
1898
2213
|
}
|
|
1899
2214
|
}));
|
|
1900
|
-
// If skipStatusCheck is true, show button immediately without polling
|
|
1901
|
-
if (this.skipStatusCheck) {
|
|
1902
|
-
this.shouldShowButton = true;
|
|
1903
|
-
this.isJoinable = true;
|
|
1904
|
-
return;
|
|
1905
|
-
}
|
|
1906
2215
|
// Start status checking
|
|
1907
2216
|
this.startStatusPolling();
|
|
1908
2217
|
}
|
|
@@ -2026,7 +2335,7 @@ class TasButtonComponent {
|
|
|
2026
2335
|
}
|
|
2027
2336
|
}
|
|
2028
2337
|
TasButtonComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.12", ngImport: i0, type: TasButtonComponent, deps: [{ token: i1.NgbModal }, { token: TasService }, { token: TasUtilityService }], target: i0.ɵɵFactoryTarget.Component });
|
|
2029
|
-
TasButtonComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.12", type: TasButtonComponent, selector: "tas-btn", inputs: { roomType: "roomType", entityId: "entityId", tenant: "tenant", businessRole: "businessRole", currentUser: "currentUser", variant: "variant", buttonLabel: "buttonLabel"
|
|
2338
|
+
TasButtonComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.12", type: TasButtonComponent, selector: "tas-btn", inputs: { roomType: "roomType", entityId: "entityId", tenant: "tenant", businessRole: "businessRole", currentUser: "currentUser", variant: "variant", buttonLabel: "buttonLabel" }, ngImport: i0, template: "<span\n *ngIf=\"shouldShowButton\"\n [ngbTooltip]=\"isDisabled && disabledReason ? disabledReason : null\"\n container=\"body\"\n placement=\"top\"\n tooltipClass=\"tas-btn-tooltip\"\n>\n <button\n type=\"button\"\n class=\"btn btn-primary tas-btn\"\n [class.tas-btn--teal]=\"variant === 'teal'\"\n (click)=\"onClick()\"\n [disabled]=\"isDisabled\"\n >\n <i class=\"fa fa-video-camera\" aria-hidden=\"true\" *ngIf=\"!isLoading\"></i>\n <span *ngIf=\"!isLoading\">{{ buttonLabel }}</span>\n <span *ngIf=\"isLoading\">Processing...</span>\n </button>\n</span>\n", styles: [":host{display:inline-block}.tas-btn{background-color:#ee316b!important;color:#fff!important;border-color:#ee316b!important;margin-right:24px;display:flex;padding:6px 14px;justify-content:center;align-items:center;gap:7px;flex-shrink:0;flex-grow:0}.tas-btn:disabled{background-color:#ccc!important;border-color:#ccc!important;cursor:not-allowed}.tas-btn:hover:not(:disabled){background-color:#d62a5f!important;border-color:#d62a5f!important}.tas-btn i{margin-right:5px}.tas-btn--teal{background-color:#0097a7!important;border-color:#0097a7!important;border-radius:24px;font-weight:500;margin-right:0;padding:6px 14px}.tas-btn--teal:hover:not(:disabled){background-color:#00838f!important;border-color:#00838f!important}\n"], directives: [{ type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i1.NgbTooltip, selector: "[ngbTooltip]", inputs: ["animation", "autoClose", "placement", "triggers", "container", "disableTooltip", "tooltipClass", "openDelay", "closeDelay", "ngbTooltip"], outputs: ["shown", "hidden"], exportAs: ["ngbTooltip"] }] });
|
|
2030
2339
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.12", ngImport: i0, type: TasButtonComponent, decorators: [{
|
|
2031
2340
|
type: Component,
|
|
2032
2341
|
args: [{ selector: 'tas-btn', template: "<span\n *ngIf=\"shouldShowButton\"\n [ngbTooltip]=\"isDisabled && disabledReason ? disabledReason : null\"\n container=\"body\"\n placement=\"top\"\n tooltipClass=\"tas-btn-tooltip\"\n>\n <button\n type=\"button\"\n class=\"btn btn-primary tas-btn\"\n [class.tas-btn--teal]=\"variant === 'teal'\"\n (click)=\"onClick()\"\n [disabled]=\"isDisabled\"\n >\n <i class=\"fa fa-video-camera\" aria-hidden=\"true\" *ngIf=\"!isLoading\"></i>\n <span *ngIf=\"!isLoading\">{{ buttonLabel }}</span>\n <span *ngIf=\"isLoading\">Processing...</span>\n </button>\n</span>\n", styles: [":host{display:inline-block}.tas-btn{background-color:#ee316b!important;color:#fff!important;border-color:#ee316b!important;margin-right:24px;display:flex;padding:6px 14px;justify-content:center;align-items:center;gap:7px;flex-shrink:0;flex-grow:0}.tas-btn:disabled{background-color:#ccc!important;border-color:#ccc!important;cursor:not-allowed}.tas-btn:hover:not(:disabled){background-color:#d62a5f!important;border-color:#d62a5f!important}.tas-btn i{margin-right:5px}.tas-btn--teal{background-color:#0097a7!important;border-color:#0097a7!important;border-radius:24px;font-weight:500;margin-right:0;padding:6px 14px}.tas-btn--teal:hover:not(:disabled){background-color:#00838f!important;border-color:#00838f!important}\n"] }]
|
|
@@ -2044,8 +2353,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.12", ngImpo
|
|
|
2044
2353
|
type: Input
|
|
2045
2354
|
}], buttonLabel: [{
|
|
2046
2355
|
type: Input
|
|
2047
|
-
}], skipStatusCheck: [{
|
|
2048
|
-
type: Input
|
|
2049
2356
|
}] } });
|
|
2050
2357
|
|
|
2051
2358
|
class TasFloatingCallComponent {
|
|
@@ -2056,6 +2363,7 @@ class TasFloatingCallComponent {
|
|
|
2056
2363
|
this.isMuted = false;
|
|
2057
2364
|
this.subscriptions = new Subscription();
|
|
2058
2365
|
this.videoCallModalRef = null;
|
|
2366
|
+
this.feedbackShown = false; // Track if feedback modal has been shown
|
|
2059
2367
|
// Margin from screen edges (in pixels)
|
|
2060
2368
|
this.PIP_MARGIN = 20;
|
|
2061
2369
|
}
|
|
@@ -2077,12 +2385,46 @@ class TasFloatingCallComponent {
|
|
|
2077
2385
|
toggleMute() {
|
|
2078
2386
|
this.tasService.toggleMute();
|
|
2079
2387
|
}
|
|
2388
|
+
/**
|
|
2389
|
+
* Open feedback modal when call ends from PiP mode
|
|
2390
|
+
*/
|
|
2391
|
+
openFeedbackModal() {
|
|
2392
|
+
var _a;
|
|
2393
|
+
// Prevent opening feedback modal multiple times
|
|
2394
|
+
if (this.feedbackShown) {
|
|
2395
|
+
this.isVisible = false;
|
|
2396
|
+
return;
|
|
2397
|
+
}
|
|
2398
|
+
// Get videoCallId from service
|
|
2399
|
+
const videoCallId = this.tasService.videoCallId;
|
|
2400
|
+
// If no videoCallId, skip feedback and hide directly
|
|
2401
|
+
if (!videoCallId) {
|
|
2402
|
+
this.isVisible = false;
|
|
2403
|
+
return;
|
|
2404
|
+
}
|
|
2405
|
+
this.feedbackShown = true;
|
|
2406
|
+
const modalRef = this.modalService.open(TasFeedbackModalComponent, {
|
|
2407
|
+
centered: true,
|
|
2408
|
+
backdrop: true,
|
|
2409
|
+
keyboard: true,
|
|
2410
|
+
windowClass: 'tas-feedback-modal-wrapper',
|
|
2411
|
+
});
|
|
2412
|
+
modalRef.componentInstance.videoCallId = videoCallId;
|
|
2413
|
+
modalRef.componentInstance.tenant = (_a = this.tasService.tenant) !== null && _a !== void 0 ? _a : '';
|
|
2414
|
+
modalRef.componentInstance.businessRole = this.tasService.businessRole;
|
|
2415
|
+
// Hide floating call after feedback modal is closed/dismissed
|
|
2416
|
+
modalRef.result.finally(() => {
|
|
2417
|
+
this.isVisible = false;
|
|
2418
|
+
this.feedbackShown = false; // Reset for potential future calls
|
|
2419
|
+
});
|
|
2420
|
+
}
|
|
2080
2421
|
// Private Methods
|
|
2081
2422
|
setupSubscriptions() {
|
|
2082
2423
|
// Call state subscription
|
|
2083
2424
|
this.subscriptions.add(this.tasService.callState$.subscribe((state) => {
|
|
2084
|
-
if
|
|
2085
|
-
|
|
2425
|
+
// Only handle disconnect feedback if we're in PiP mode (isVisible)
|
|
2426
|
+
if (state === CallState.DISCONNECTED && this.isVisible) {
|
|
2427
|
+
this.openFeedbackModal();
|
|
2086
2428
|
}
|
|
2087
2429
|
}));
|
|
2088
2430
|
// View mode subscription
|
|
@@ -2204,10 +2546,13 @@ class TasIncomingAppointmentComponent {
|
|
|
2204
2546
|
this.appointments = [];
|
|
2205
2547
|
this.isLoading = true;
|
|
2206
2548
|
this.hasError = false;
|
|
2549
|
+
// The appointmentId from status API - only this appointment shows tas-btn
|
|
2550
|
+
this.activeAppointmentId = null;
|
|
2207
2551
|
this.subscriptions = new Subscription();
|
|
2208
2552
|
}
|
|
2209
2553
|
ngOnInit() {
|
|
2210
2554
|
this.loadAppointments();
|
|
2555
|
+
this.checkStatus();
|
|
2211
2556
|
}
|
|
2212
2557
|
ngOnDestroy() {
|
|
2213
2558
|
this.subscriptions.unsubscribe();
|
|
@@ -2225,8 +2570,9 @@ class TasIncomingAppointmentComponent {
|
|
|
2225
2570
|
const appointments = Array.isArray(response)
|
|
2226
2571
|
? response
|
|
2227
2572
|
: (response === null || response === void 0 ? void 0 : response.content) || [];
|
|
2228
|
-
//
|
|
2229
|
-
|
|
2573
|
+
// Deduplicate by id and sort by date and startTime ascending (earliest first)
|
|
2574
|
+
const uniqueAppointments = appointments.filter((appt, index, self) => index === self.findIndex((a) => a.id === appt.id));
|
|
2575
|
+
this.appointments = uniqueAppointments.sort((a, b) => {
|
|
2230
2576
|
const dateTimeA = `${a.date}T${a.startTime}`;
|
|
2231
2577
|
const dateTimeB = `${b.date}T${b.startTime}`;
|
|
2232
2578
|
return dateTimeB.localeCompare(dateTimeA);
|
|
@@ -2239,15 +2585,50 @@ class TasIncomingAppointmentComponent {
|
|
|
2239
2585
|
},
|
|
2240
2586
|
}));
|
|
2241
2587
|
}
|
|
2588
|
+
/**
|
|
2589
|
+
* Check status endpoint to get the active appointmentId
|
|
2590
|
+
*/
|
|
2591
|
+
checkStatus() {
|
|
2592
|
+
if (!this.tenant || !this.entityId) {
|
|
2593
|
+
return;
|
|
2594
|
+
}
|
|
2595
|
+
this.subscriptions.add(this.tasService
|
|
2596
|
+
.getProxyVideoStatus({
|
|
2597
|
+
roomType: this.roomType,
|
|
2598
|
+
entityId: this.entityId,
|
|
2599
|
+
tenant: this.tenant,
|
|
2600
|
+
businessRole: this.businessRole,
|
|
2601
|
+
})
|
|
2602
|
+
.subscribe({
|
|
2603
|
+
next: (response) => {
|
|
2604
|
+
var _a, _b;
|
|
2605
|
+
// Store the appointmentId from status - tas-btn only shows for this one
|
|
2606
|
+
this.activeAppointmentId = (_b = (_a = response === null || response === void 0 ? void 0 : response.content) === null || _a === void 0 ? void 0 : _a.appointmentId) !== null && _b !== void 0 ? _b : null;
|
|
2607
|
+
},
|
|
2608
|
+
error: () => {
|
|
2609
|
+
this.activeAppointmentId = null;
|
|
2610
|
+
},
|
|
2611
|
+
}));
|
|
2612
|
+
}
|
|
2242
2613
|
onEnterCall(appointment) {
|
|
2243
2614
|
this.enterCall.emit(appointment);
|
|
2244
2615
|
}
|
|
2245
2616
|
/**
|
|
2246
|
-
* Check if tas-btn should be shown for an appointment
|
|
2617
|
+
* Check if tas-btn should be shown for an appointment.
|
|
2618
|
+
* Only shows when appointment.id matches the activeAppointmentId from status API.
|
|
2619
|
+
* tas-btn handles its own polling for joinable state.
|
|
2247
2620
|
*/
|
|
2248
2621
|
shouldShowTasBtn(appointment) {
|
|
2249
|
-
|
|
2622
|
+
const hasValidStatus = appointment.status === AppointmentStatus.CONFIRMED ||
|
|
2250
2623
|
appointment.status === AppointmentStatus.ACTIVE;
|
|
2624
|
+
// Only show for the appointment that matches status API response
|
|
2625
|
+
return hasValidStatus && appointment.id === this.activeAppointmentId;
|
|
2626
|
+
}
|
|
2627
|
+
/**
|
|
2628
|
+
* TrackBy function for ngFor
|
|
2629
|
+
*/
|
|
2630
|
+
trackByAppointmentId(index, appointment) {
|
|
2631
|
+
return appointment.id;
|
|
2251
2632
|
}
|
|
2252
2633
|
/**
|
|
2253
2634
|
* Format date to Spanish format: "Lunes 8 de diciembre"
|
|
@@ -2279,17 +2660,17 @@ class TasIncomingAppointmentComponent {
|
|
|
2279
2660
|
*/
|
|
2280
2661
|
getOtherParticipant(appointment) {
|
|
2281
2662
|
if (!appointment.participants || appointment.participants.length === 0) {
|
|
2282
|
-
return appointment.title;
|
|
2663
|
+
return appointment.title;
|
|
2283
2664
|
}
|
|
2284
2665
|
const otherParticipant = appointment.participants.find(p => { var _a; return p.userId !== ((_a = this.currentUser) === null || _a === void 0 ? void 0 : _a.id); });
|
|
2285
2666
|
return (otherParticipant === null || otherParticipant === void 0 ? void 0 : otherParticipant.name) || appointment.title;
|
|
2286
2667
|
}
|
|
2287
2668
|
}
|
|
2288
2669
|
TasIncomingAppointmentComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.12", ngImport: i0, type: TasIncomingAppointmentComponent, deps: [{ token: TasService }], target: i0.ɵɵFactoryTarget.Component });
|
|
2289
|
-
TasIncomingAppointmentComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.12", type: TasIncomingAppointmentComponent, selector: "tas-incoming-appointment", inputs: { roomType: "roomType", entityId: "entityId", tenant: "tenant", businessRole: "businessRole", currentUser: "currentUser", fromDate: "fromDate", toDate: "toDate" }, outputs: { enterCall: "enterCall" }, ngImport: i0, template: "<div class=\"incoming-appointment-card\">\n <h3 class=\"card-title\">Pr\u00F3ximo turno</h3>\n\n <!-- Loading state -->\n <div class=\"card-content\" *ngIf=\"isLoading\">\n <div class=\"loading-spinner\"></div>\n </div>\n\n <!-- Empty state -->\n <div class=\"card-content empty-state\" *ngIf=\"!isLoading && appointments.length === 0\">\n <div class=\"icon-container\">\n <div class=\"icon-circle\">\n <i class=\"fa fa-calendar\" aria-hidden=\"true\"></i>\n </div>\n <span class=\"sparkle sparkle-1\">\u2726</span>\n <span class=\"sparkle sparkle-2\">\u2726</span>\n <span class=\"sparkle sparkle-3\">\u2726</span>\n <span class=\"sparkle sparkle-4\">\u2726</span>\n </div>\n <h4 class=\"empty-title\">Todav\u00EDa no ten\u00E9s turnos agendados</h4>\n <p class=\"empty-subtitle\">\n En caso de que Medicina Laboral requiera una consulta, lo ver\u00E1s en esta secci\u00F3n.\n </p>\n </div>\n\n <!-- Appointments list -->\n <div class=\"card-content appointment-state\" *ngIf=\"!isLoading && appointments.length > 0\">\n <div class=\"appointment-card\" *ngFor=\"let appt of appointments\">\n <div class=\"appointment-header\">\n <tas-avatar [name]=\"getOtherParticipant(appt)\" [size]=\"48\"></tas-avatar>\n <span class=\"doctor-name\">{{ getOtherParticipant(appt) }}</span>\n </div>\n <div class=\"appointment-details\">\n <div class=\"date-time\">\n <span class=\"date\">{{ formatAppointmentDate(appt) }}</span>\n <span class=\"time\">{{ formatTimeRange(appt) }}</span>\n </div>\n <tas-btn\n *ngIf=\"shouldShowTasBtn(appt)\"\n variant=\"teal\"\n buttonLabel=\"Ingresar\"\n [roomType]=\"appt.roomType\"\n [entityId]=\"appt.entityId\"\n [tenant]=\"tenant\"\n [businessRole]=\"businessRole\"\n [currentUser]=\"currentUser\"\n ></tas-btn>\n </div>\n </div>\n </div>\n</div>\n\n", styles: [":host{display:block}.incoming-appointment-card{background:#ffffff;border-radius:12px;box-shadow:0 2px 8px #00000014;padding:24px;min-width:360px}.card-title{font-size:16px;font-weight:400;color:#6b7280;margin:0 0 24px}.card-content{display:flex;flex-direction:column;align-items:center}.loading-spinner{width:40px;height:40px;border:3px solid #e0f7fa;border-top-color:#0097a7;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.empty-state{text-align:center;padding:20px 0}.icon-container{position:relative;width:120px;height:120px;margin-bottom:24px}.icon-circle{width:100%;height:100%;background:#e0f7fa;border-radius:50%;display:flex;align-items:center;justify-content:center}.icon-circle i{font-size:40px;color:#0097a7}.sparkle{position:absolute;color:#0097a7;font-size:12px}.sparkle-1{top:10px;right:5px}.sparkle-2{top:0;right:30px}.sparkle-3{top:25px;left:0}.sparkle-4{bottom:20px;right:0}.empty-title{font-size:18px;font-weight:600;color:#1f2937;margin:0 0 12px}.empty-subtitle{font-size:14px;color:#6b7280;margin:0;max-width:320px;line-height:1.5}.appointment-state{align-items:stretch;gap:16px}.appointment-card{border-radius:12px;border:1px solid var(--Primary-White-Uell50, #8ED1D8);background:#F1FAFA;padding:1.5rem}.appointment-header{display:flex;align-items:center;gap:12px;margin-bottom:16px}.doctor-name{overflow:hidden;color:var(--Neutral-GreyDark, #383E52);text-overflow:ellipsis;font-size:16px;font-style:normal;font-weight:600;line-height:24px;letter-spacing:.016px}.appointment-details{display:flex;justify-content:space-between;align-items:flex-end}.date-time{display:flex;flex-direction:column;gap:4px}.date{color:var(--Neutral-GreyDark, #383E52);font-size:12px;font-style:normal;font-weight:600;line-height:16px;letter-spacing:.06px}.time{font-size:14px;color:var(--Neutral-GreyDark, #383E52);font-family:Inter;font-size:10px;font-style:normal;font-weight:400;line-height:14px;letter-spacing:.04px}\n"], components: [{ type: TasAvatarComponent, selector: "tas-avatar", inputs: ["name", "size"] }, { type: TasButtonComponent, selector: "tas-btn", inputs: ["roomType", "entityId", "tenant", "businessRole", "currentUser", "variant", "buttonLabel"
|
|
2670
|
+
TasIncomingAppointmentComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.3.12", type: TasIncomingAppointmentComponent, selector: "tas-incoming-appointment", inputs: { roomType: "roomType", entityId: "entityId", tenant: "tenant", businessRole: "businessRole", currentUser: "currentUser", fromDate: "fromDate", toDate: "toDate" }, outputs: { enterCall: "enterCall" }, ngImport: i0, template: "<div class=\"incoming-appointment-card\">\n <h3 class=\"card-title\">Pr\u00F3ximo turno</h3>\n\n <!-- Loading state -->\n <div class=\"card-content\" *ngIf=\"isLoading\">\n <div class=\"loading-spinner\"></div>\n </div>\n\n <!-- Empty state -->\n <div class=\"card-content empty-state\" *ngIf=\"!isLoading && appointments.length === 0\">\n <div class=\"icon-container\">\n <div class=\"icon-circle\">\n <i class=\"fa fa-calendar\" aria-hidden=\"true\"></i>\n </div>\n <span class=\"sparkle sparkle-1\">\u2726</span>\n <span class=\"sparkle sparkle-2\">\u2726</span>\n <span class=\"sparkle sparkle-3\">\u2726</span>\n <span class=\"sparkle sparkle-4\">\u2726</span>\n </div>\n <h4 class=\"empty-title\">Todav\u00EDa no ten\u00E9s turnos agendados</h4>\n <p class=\"empty-subtitle\">\n En caso de que Medicina Laboral requiera una consulta, lo ver\u00E1s en esta secci\u00F3n.\n </p>\n </div>\n\n <!-- Appointments list -->\n <div class=\"card-content appointment-state\" *ngIf=\"!isLoading && appointments.length > 0\">\n <div class=\"appointment-card\" *ngFor=\"let appt of appointments; trackBy: trackByAppointmentId\">\n <div class=\"appointment-header\">\n <tas-avatar [name]=\"getOtherParticipant(appt)\" [size]=\"48\"></tas-avatar>\n <span class=\"doctor-name\">{{ getOtherParticipant(appt) }}</span>\n </div>\n <div class=\"appointment-details\">\n <div class=\"date-time\">\n <span class=\"date\">{{ formatAppointmentDate(appt) }}</span>\n <span class=\"time\">{{ formatTimeRange(appt) }}</span>\n </div>\n <tas-btn\n *ngIf=\"shouldShowTasBtn(appt)\"\n variant=\"teal\"\n buttonLabel=\"Ingresar\"\n [roomType]=\"appt.roomType\"\n [entityId]=\"appt.entityId\"\n [tenant]=\"tenant\"\n [businessRole]=\"businessRole\"\n [currentUser]=\"currentUser\"\n ></tas-btn>\n </div>\n </div>\n </div>\n</div>\n\n", styles: [":host{display:block}.incoming-appointment-card{background:#ffffff;border-radius:12px;box-shadow:0 2px 8px #00000014;padding:24px;min-width:360px}.card-title{font-size:16px;font-weight:400;color:#6b7280;margin:0 0 24px}.card-content{display:flex;flex-direction:column;align-items:center}.loading-spinner{width:40px;height:40px;border:3px solid #e0f7fa;border-top-color:#0097a7;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.empty-state{text-align:center;padding:20px 0}.icon-container{position:relative;width:120px;height:120px;margin-bottom:24px}.icon-circle{width:100%;height:100%;background:#e0f7fa;border-radius:50%;display:flex;align-items:center;justify-content:center}.icon-circle i{font-size:40px;color:#0097a7}.sparkle{position:absolute;color:#0097a7;font-size:12px}.sparkle-1{top:10px;right:5px}.sparkle-2{top:0;right:30px}.sparkle-3{top:25px;left:0}.sparkle-4{bottom:20px;right:0}.empty-title{font-size:18px;font-weight:600;color:#1f2937;margin:0 0 12px}.empty-subtitle{font-size:14px;color:#6b7280;margin:0;max-width:320px;line-height:1.5}.appointment-state{align-items:stretch;gap:16px}.appointment-card{border-radius:12px;border:1px solid var(--Primary-White-Uell50, #8ED1D8);background:#F1FAFA;padding:1.5rem}.appointment-header{display:flex;align-items:center;gap:12px;margin-bottom:16px}.doctor-name{overflow:hidden;color:var(--Neutral-GreyDark, #383E52);text-overflow:ellipsis;font-size:16px;font-style:normal;font-weight:600;line-height:24px;letter-spacing:.016px}.appointment-details{display:flex;justify-content:space-between;align-items:flex-end}.date-time{display:flex;flex-direction:column;gap:4px}.date{color:var(--Neutral-GreyDark, #383E52);font-size:12px;font-style:normal;font-weight:600;line-height:16px;letter-spacing:.06px}.time{font-size:14px;color:var(--Neutral-GreyDark, #383E52);font-family:Inter;font-size:10px;font-style:normal;font-weight:400;line-height:14px;letter-spacing:.04px}\n"], components: [{ type: TasAvatarComponent, selector: "tas-avatar", inputs: ["name", "size"] }, { type: TasButtonComponent, selector: "tas-btn", inputs: ["roomType", "entityId", "tenant", "businessRole", "currentUser", "variant", "buttonLabel"] }], directives: [{ type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
|
|
2290
2671
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.12", ngImport: i0, type: TasIncomingAppointmentComponent, decorators: [{
|
|
2291
2672
|
type: Component,
|
|
2292
|
-
args: [{ selector: 'tas-incoming-appointment', template: "<div class=\"incoming-appointment-card\">\n <h3 class=\"card-title\">Pr\u00F3ximo turno</h3>\n\n <!-- Loading state -->\n <div class=\"card-content\" *ngIf=\"isLoading\">\n <div class=\"loading-spinner\"></div>\n </div>\n\n <!-- Empty state -->\n <div class=\"card-content empty-state\" *ngIf=\"!isLoading && appointments.length === 0\">\n <div class=\"icon-container\">\n <div class=\"icon-circle\">\n <i class=\"fa fa-calendar\" aria-hidden=\"true\"></i>\n </div>\n <span class=\"sparkle sparkle-1\">\u2726</span>\n <span class=\"sparkle sparkle-2\">\u2726</span>\n <span class=\"sparkle sparkle-3\">\u2726</span>\n <span class=\"sparkle sparkle-4\">\u2726</span>\n </div>\n <h4 class=\"empty-title\">Todav\u00EDa no ten\u00E9s turnos agendados</h4>\n <p class=\"empty-subtitle\">\n En caso de que Medicina Laboral requiera una consulta, lo ver\u00E1s en esta secci\u00F3n.\n </p>\n </div>\n\n <!-- Appointments list -->\n <div class=\"card-content appointment-state\" *ngIf=\"!isLoading && appointments.length > 0\">\n <div class=\"appointment-card\" *ngFor=\"let appt of appointments\">\n <div class=\"appointment-header\">\n <tas-avatar [name]=\"getOtherParticipant(appt)\" [size]=\"48\"></tas-avatar>\n <span class=\"doctor-name\">{{ getOtherParticipant(appt) }}</span>\n </div>\n <div class=\"appointment-details\">\n <div class=\"date-time\">\n <span class=\"date\">{{ formatAppointmentDate(appt) }}</span>\n <span class=\"time\">{{ formatTimeRange(appt) }}</span>\n </div>\n <tas-btn\n *ngIf=\"shouldShowTasBtn(appt)\"\n variant=\"teal\"\n buttonLabel=\"Ingresar\"\n [roomType]=\"appt.roomType\"\n [entityId]=\"appt.entityId\"\n [tenant]=\"tenant\"\n [businessRole]=\"businessRole\"\n [currentUser]=\"currentUser\"\n ></tas-btn>\n </div>\n </div>\n </div>\n</div>\n\n", styles: [":host{display:block}.incoming-appointment-card{background:#ffffff;border-radius:12px;box-shadow:0 2px 8px #00000014;padding:24px;min-width:360px}.card-title{font-size:16px;font-weight:400;color:#6b7280;margin:0 0 24px}.card-content{display:flex;flex-direction:column;align-items:center}.loading-spinner{width:40px;height:40px;border:3px solid #e0f7fa;border-top-color:#0097a7;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.empty-state{text-align:center;padding:20px 0}.icon-container{position:relative;width:120px;height:120px;margin-bottom:24px}.icon-circle{width:100%;height:100%;background:#e0f7fa;border-radius:50%;display:flex;align-items:center;justify-content:center}.icon-circle i{font-size:40px;color:#0097a7}.sparkle{position:absolute;color:#0097a7;font-size:12px}.sparkle-1{top:10px;right:5px}.sparkle-2{top:0;right:30px}.sparkle-3{top:25px;left:0}.sparkle-4{bottom:20px;right:0}.empty-title{font-size:18px;font-weight:600;color:#1f2937;margin:0 0 12px}.empty-subtitle{font-size:14px;color:#6b7280;margin:0;max-width:320px;line-height:1.5}.appointment-state{align-items:stretch;gap:16px}.appointment-card{border-radius:12px;border:1px solid var(--Primary-White-Uell50, #8ED1D8);background:#F1FAFA;padding:1.5rem}.appointment-header{display:flex;align-items:center;gap:12px;margin-bottom:16px}.doctor-name{overflow:hidden;color:var(--Neutral-GreyDark, #383E52);text-overflow:ellipsis;font-size:16px;font-style:normal;font-weight:600;line-height:24px;letter-spacing:.016px}.appointment-details{display:flex;justify-content:space-between;align-items:flex-end}.date-time{display:flex;flex-direction:column;gap:4px}.date{color:var(--Neutral-GreyDark, #383E52);font-size:12px;font-style:normal;font-weight:600;line-height:16px;letter-spacing:.06px}.time{font-size:14px;color:var(--Neutral-GreyDark, #383E52);font-family:Inter;font-size:10px;font-style:normal;font-weight:400;line-height:14px;letter-spacing:.04px}\n"] }]
|
|
2673
|
+
args: [{ selector: 'tas-incoming-appointment', template: "<div class=\"incoming-appointment-card\">\n <h3 class=\"card-title\">Pr\u00F3ximo turno</h3>\n\n <!-- Loading state -->\n <div class=\"card-content\" *ngIf=\"isLoading\">\n <div class=\"loading-spinner\"></div>\n </div>\n\n <!-- Empty state -->\n <div class=\"card-content empty-state\" *ngIf=\"!isLoading && appointments.length === 0\">\n <div class=\"icon-container\">\n <div class=\"icon-circle\">\n <i class=\"fa fa-calendar\" aria-hidden=\"true\"></i>\n </div>\n <span class=\"sparkle sparkle-1\">\u2726</span>\n <span class=\"sparkle sparkle-2\">\u2726</span>\n <span class=\"sparkle sparkle-3\">\u2726</span>\n <span class=\"sparkle sparkle-4\">\u2726</span>\n </div>\n <h4 class=\"empty-title\">Todav\u00EDa no ten\u00E9s turnos agendados</h4>\n <p class=\"empty-subtitle\">\n En caso de que Medicina Laboral requiera una consulta, lo ver\u00E1s en esta secci\u00F3n.\n </p>\n </div>\n\n <!-- Appointments list -->\n <div class=\"card-content appointment-state\" *ngIf=\"!isLoading && appointments.length > 0\">\n <div class=\"appointment-card\" *ngFor=\"let appt of appointments; trackBy: trackByAppointmentId\">\n <div class=\"appointment-header\">\n <tas-avatar [name]=\"getOtherParticipant(appt)\" [size]=\"48\"></tas-avatar>\n <span class=\"doctor-name\">{{ getOtherParticipant(appt) }}</span>\n </div>\n <div class=\"appointment-details\">\n <div class=\"date-time\">\n <span class=\"date\">{{ formatAppointmentDate(appt) }}</span>\n <span class=\"time\">{{ formatTimeRange(appt) }}</span>\n </div>\n <tas-btn\n *ngIf=\"shouldShowTasBtn(appt)\"\n variant=\"teal\"\n buttonLabel=\"Ingresar\"\n [roomType]=\"appt.roomType\"\n [entityId]=\"appt.entityId\"\n [tenant]=\"tenant\"\n [businessRole]=\"businessRole\"\n [currentUser]=\"currentUser\"\n ></tas-btn>\n </div>\n </div>\n </div>\n</div>\n\n", styles: [":host{display:block}.incoming-appointment-card{background:#ffffff;border-radius:12px;box-shadow:0 2px 8px #00000014;padding:24px;min-width:360px}.card-title{font-size:16px;font-weight:400;color:#6b7280;margin:0 0 24px}.card-content{display:flex;flex-direction:column;align-items:center}.loading-spinner{width:40px;height:40px;border:3px solid #e0f7fa;border-top-color:#0097a7;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.empty-state{text-align:center;padding:20px 0}.icon-container{position:relative;width:120px;height:120px;margin-bottom:24px}.icon-circle{width:100%;height:100%;background:#e0f7fa;border-radius:50%;display:flex;align-items:center;justify-content:center}.icon-circle i{font-size:40px;color:#0097a7}.sparkle{position:absolute;color:#0097a7;font-size:12px}.sparkle-1{top:10px;right:5px}.sparkle-2{top:0;right:30px}.sparkle-3{top:25px;left:0}.sparkle-4{bottom:20px;right:0}.empty-title{font-size:18px;font-weight:600;color:#1f2937;margin:0 0 12px}.empty-subtitle{font-size:14px;color:#6b7280;margin:0;max-width:320px;line-height:1.5}.appointment-state{align-items:stretch;gap:16px}.appointment-card{border-radius:12px;border:1px solid var(--Primary-White-Uell50, #8ED1D8);background:#F1FAFA;padding:1.5rem}.appointment-header{display:flex;align-items:center;gap:12px;margin-bottom:16px}.doctor-name{overflow:hidden;color:var(--Neutral-GreyDark, #383E52);text-overflow:ellipsis;font-size:16px;font-style:normal;font-weight:600;line-height:24px;letter-spacing:.016px}.appointment-details{display:flex;justify-content:space-between;align-items:flex-end}.date-time{display:flex;flex-direction:column;gap:4px}.date{color:var(--Neutral-GreyDark, #383E52);font-size:12px;font-style:normal;font-weight:600;line-height:16px;letter-spacing:.06px}.time{font-size:14px;color:var(--Neutral-GreyDark, #383E52);font-family:Inter;font-size:10px;font-style:normal;font-weight:400;line-height:14px;letter-spacing:.04px}\n"] }]
|
|
2293
2674
|
}], ctorParameters: function () { return [{ type: TasService }]; }, propDecorators: { roomType: [{
|
|
2294
2675
|
type: Input
|
|
2295
2676
|
}], entityId: [{
|
|
@@ -2355,12 +2736,14 @@ TasUellSdkModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", versio
|
|
|
2355
2736
|
TasFloatingCallComponent,
|
|
2356
2737
|
TasWaitingRoomComponent,
|
|
2357
2738
|
TasAvatarComponent,
|
|
2358
|
-
TasIncomingAppointmentComponent
|
|
2739
|
+
TasIncomingAppointmentComponent,
|
|
2740
|
+
TasFeedbackModalComponent], imports: [CommonModule, FormsModule, NgbTooltipModule], exports: [TasButtonComponent,
|
|
2359
2741
|
TasVideocallComponent,
|
|
2360
2742
|
TasFloatingCallComponent,
|
|
2361
2743
|
TasWaitingRoomComponent,
|
|
2362
2744
|
TasAvatarComponent,
|
|
2363
|
-
TasIncomingAppointmentComponent
|
|
2745
|
+
TasIncomingAppointmentComponent,
|
|
2746
|
+
TasFeedbackModalComponent] });
|
|
2364
2747
|
TasUellSdkModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.3.12", ngImport: i0, type: TasUellSdkModule, imports: [[CommonModule, FormsModule, NgbTooltipModule]] });
|
|
2365
2748
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.12", ngImport: i0, type: TasUellSdkModule, decorators: [{
|
|
2366
2749
|
type: NgModule,
|
|
@@ -2372,6 +2755,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.12", ngImpo
|
|
|
2372
2755
|
TasWaitingRoomComponent,
|
|
2373
2756
|
TasAvatarComponent,
|
|
2374
2757
|
TasIncomingAppointmentComponent,
|
|
2758
|
+
TasFeedbackModalComponent,
|
|
2375
2759
|
],
|
|
2376
2760
|
imports: [CommonModule, FormsModule, NgbTooltipModule],
|
|
2377
2761
|
exports: [
|
|
@@ -2381,6 +2765,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.12", ngImpo
|
|
|
2381
2765
|
TasWaitingRoomComponent,
|
|
2382
2766
|
TasAvatarComponent,
|
|
2383
2767
|
TasIncomingAppointmentComponent,
|
|
2768
|
+
TasFeedbackModalComponent,
|
|
2384
2769
|
],
|
|
2385
2770
|
}]
|
|
2386
2771
|
}] });
|
|
@@ -2393,5 +2778,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.12", ngImpo
|
|
|
2393
2778
|
* Generated bundle index. Do not edit.
|
|
2394
2779
|
*/
|
|
2395
2780
|
|
|
2396
|
-
export { AppointmentStatus, CallState, GeoStatus, GeolocationService, RoomUserStatus, TAS_CONFIG, TAS_HTTP_CLIENT, TasAvatarComponent, TasBusinessRole, TasButtonComponent, TasFloatingCallComponent, TasIncomingAppointmentComponent, TasRoomType, TasService, TasSessionType, TasUellSdkModule, TasUserRole, TasUtilityService, TasVideocallComponent, TasWaitingRoomComponent, UserCallAction, UserGeoViewState, UserStatus, VideoSessionStatus, ViewMode, WaitingRoomState };
|
|
2781
|
+
export { AppointmentStatus, CallState, FeedbackMotiveType, GeoStatus, GeolocationService, RoomUserStatus, TAS_CONFIG, TAS_HTTP_CLIENT, TasAvatarComponent, TasBusinessRole, TasButtonComponent, TasFeedbackModalComponent, TasFloatingCallComponent, TasIncomingAppointmentComponent, TasRoomType, TasService, TasSessionType, TasUellSdkModule, TasUserRole, TasUtilityService, TasVideocallComponent, TasWaitingRoomComponent, UserCallAction, UserGeoViewState, UserStatus, VideoSessionStatus, ViewMode, WaitingRoomState };
|
|
2397
2782
|
//# sourceMappingURL=tas-uell-sdk.mjs.map
|