clinic-connect-widget 1.0.6 → 1.0.7
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/dist/clinic-widget.es.js +83 -19
- package/dist/clinic-widget.umd.js +1 -1
- package/package.json +1 -1
package/dist/clinic-widget.es.js
CHANGED
|
@@ -225,7 +225,7 @@ const consultationHtml = `<div class="td-consultation-modal">\r
|
|
|
225
225
|
</div>\r
|
|
226
226
|
\r
|
|
227
227
|
</div>`;
|
|
228
|
-
const patientFormHtml = '<div class="td-overlay-wrapper">\r\n <div class="td-modal-centered" id="td-patient-form">\r\n <header class="td-header">\r\n <div class="td-header-title">\r\n <div class="td-icon-box">\r\n <span class="material-symbols-outlined">medical_services</span>\r\n </div>\r\n <span>Tư vấn trực tuyến</span>\r\n </div>\r\n <button class="td-close-btn" id="td-form-close">\r\n <span class="material-symbols-outlined">close</span>\r\n </button>\r\n </header>\r\n\r\n <div class="td-content">\r\n <div class="td-form-container">\r\n <div class="td-form-page-header">\r\n <h1 class="td-form-title">Kết nối với Bác sĩ</h1>\r\n <p class="td-form-desc">Vui lòng nhập thông tin để bắt đầu tư vấn trực tuyến.</p>\r\n </div>\r\n\r\n <form class="td-form" id="td-info-form">\r\n <div class="td-input-group">\r\n <label class="td-label">Họ và tên
|
|
228
|
+
const patientFormHtml = '<div class="td-overlay-wrapper">\r\n <div class="td-modal-centered" id="td-patient-form">\r\n <header class="td-header">\r\n <div class="td-header-title">\r\n <div class="td-icon-box">\r\n <span class="material-symbols-outlined">medical_services</span>\r\n </div>\r\n <span>Tư vấn trực tuyến</span>\r\n </div>\r\n <button class="td-close-btn" id="td-form-close">\r\n <span class="material-symbols-outlined">close</span>\r\n </button>\r\n </header>\r\n\r\n <div class="td-content">\r\n <div class="td-form-container">\r\n <div class="td-form-page-header">\r\n <h1 class="td-form-title">Kết nối với Bác sĩ</h1>\r\n <p class="td-form-desc">Vui lòng nhập thông tin để bắt đầu tư vấn trực tuyến.</p>\r\n </div>\r\n\r\n <form class="td-form" id="td-info-form">\r\n <div class="td-input-group">\r\n <label class="td-label">Họ và tên <span style="color: red;">*</span></label>\r\n <input type="text" class="td-input" name="name" placeholder="VD: Nguyễn Văn A" required />\r\n </div>\r\n\r\n <div class="td-input-group">\r\n <label class="td-label">Số điện thoại <span style="color: red;">*</span></label>\r\n <div style="position: relative;">\r\n <input type="tel" class="td-input" name="phone" placeholder="VD: 0912 345 678" required />\r\n <!-- <div\r\n style="position: absolute; right: 12px; top: 50%; transform: translateY(-50%); color: var(--td-primary);">\r\n <span class="material-symbols-outlined" style="font-size: 20px;">smartphone</span>\r\n </div> -->\r\n </div>\r\n </div>\r\n\r\n <!-- <div class="td-input-group">\r\n <label class="td-label">Triệu chứng của bạn?</label>\r\n <div class="td-chips-group">\r\n <button type="button" class="td-chip active">Sốt <span class="material-symbols-outlined"\r\n style="font-size: 16px;">check</span></button>\r\n <button type="button" class="td-chip">Ho / Cảm cúm</button>\r\n <button type="button" class="td-chip">Đau đầu</button>\r\n <button type="button" class="td-chip">Đau bụng</button>\r\n <button type="button" class="td-chip">Đau bụng</button>\r\n </div>\r\n </div> -->\r\n\r\n <!-- <div class="td-input-group">\r\n <label class="td-label">Mô tả thêm <span\r\n style="color: var(--td-text-sub); font-weight: 400;">(Tùy\r\n chọn)</span></label>\r\n <textarea class="td-textarea" name="symptoms"\r\n placeholder="Bạn đang cảm thấy như thế nào?"></textarea>\r\n </div> -->\r\n\r\n <div style="margin-top: 8px;">\r\n <button type="submit" class="td-btn td-btn-primary" id="td-form-submit">\r\n Bắt đầu tư vấn\r\n <span class="material-symbols-outlined" style="margin-left: 8px;">arrow_forward</span>\r\n </button>\r\n </div>\r\n\r\n <div class="td-secure-note">\r\n <span class="material-symbols-outlined" style="font-size: 14px;">lock</span>\r\n Thông tin y tế được bảo mật 100%\r\n </div>\r\n </form>\r\n </div>\r\n </div>\r\n </div>\r\n</div>';
|
|
229
229
|
const postConsultationHtml = `<div class="td-overlay-wrapper">\r
|
|
230
230
|
<div class="td-modal-centered" id="td-summary-view">\r
|
|
231
231
|
<header class="td-header">\r
|
|
@@ -433,7 +433,7 @@ const render = (template, data) => {
|
|
|
433
433
|
output = output.replace(/{{badgeText}}/g, badgeText);
|
|
434
434
|
output = output.replace(/{{btnStyle}}/g, btnStyle);
|
|
435
435
|
const showVideo = !!data.urlVideo && data.permissionsGranted;
|
|
436
|
-
const innerContent = showVideo ? `<video src="${data.urlVideo}" autoplay loop
|
|
436
|
+
const innerContent = showVideo ? `<video src="${data.urlVideo}" autoplay loop playsinline style="width: 100%; height: 100%; object-fit: cover;"></video>` : "";
|
|
437
437
|
const videoContainerHtml = `
|
|
438
438
|
<div id="td-video-container" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 0; overflow: hidden; background: #000;">
|
|
439
439
|
${innerContent}
|
|
@@ -28086,10 +28086,12 @@ class LiveKitService {
|
|
|
28086
28086
|
}
|
|
28087
28087
|
refreshRemoteTracks() {
|
|
28088
28088
|
if (!this.room || !this.wrapper) return;
|
|
28089
|
+
console.log("[LiveKit] Refreshing remote tracks for participants:", this.room.remoteParticipants.size);
|
|
28089
28090
|
this.room.remoteParticipants.forEach((participant) => {
|
|
28090
28091
|
participant.videoTrackPublications.forEach((pub) => {
|
|
28091
28092
|
if (pub.isSubscribed && pub.track && pub.kind === "video") {
|
|
28092
|
-
|
|
28093
|
+
console.log("[LiveKit] Found subscribed video track for:", participant.identity);
|
|
28094
|
+
this.handleTrackSubscribed(pub.track, participant);
|
|
28093
28095
|
}
|
|
28094
28096
|
});
|
|
28095
28097
|
});
|
|
@@ -28108,6 +28110,21 @@ class LiveKitService {
|
|
|
28108
28110
|
console.error("[LiveKit] Media Device Error:", e2);
|
|
28109
28111
|
});
|
|
28110
28112
|
}
|
|
28113
|
+
/**
|
|
28114
|
+
* Check if any remote participant has a video track
|
|
28115
|
+
*/
|
|
28116
|
+
hasRemoteVideo() {
|
|
28117
|
+
if (!this.room) return false;
|
|
28118
|
+
let hasVideo = false;
|
|
28119
|
+
this.room.remoteParticipants.forEach((p) => {
|
|
28120
|
+
p.videoTrackPublications.forEach((pub) => {
|
|
28121
|
+
if (pub.isSubscribed && pub.kind === "video") {
|
|
28122
|
+
hasVideo = true;
|
|
28123
|
+
}
|
|
28124
|
+
});
|
|
28125
|
+
});
|
|
28126
|
+
return hasVideo;
|
|
28127
|
+
}
|
|
28111
28128
|
async publishLocalTracks() {
|
|
28112
28129
|
try {
|
|
28113
28130
|
await this.room.localParticipant.setCameraEnabled(true);
|
|
@@ -28141,17 +28158,14 @@ class LiveKitService {
|
|
|
28141
28158
|
handleTrackSubscribed(track, participant) {
|
|
28142
28159
|
console.log("[LiveKit] Track subscribed:", track.kind, participant.identity);
|
|
28143
28160
|
if (track.kind === "video") {
|
|
28161
|
+
if (!this.wrapper.isConnected) {
|
|
28162
|
+
console.warn("[LiveKit] WARNING: Wrapper is DETACHED from DOM! Video update may not be visible.");
|
|
28163
|
+
}
|
|
28144
28164
|
const remoteContainer = this.wrapper.querySelector(".td-main-video");
|
|
28145
|
-
console.log("[LiveKit]
|
|
28165
|
+
console.log("[LiveKit] Remote Container found:", remoteContainer, "Is Connected:", remoteContainer == null ? void 0 : remoteContainer.isConnected);
|
|
28146
28166
|
if (remoteContainer) {
|
|
28147
|
-
|
|
28148
|
-
|
|
28149
|
-
placeholder.style.opacity = "0";
|
|
28150
|
-
setTimeout(() => placeholder.remove(), 500);
|
|
28151
|
-
} else {
|
|
28152
|
-
const existingVideos = remoteContainer.querySelectorAll("video");
|
|
28153
|
-
existingVideos.forEach((v) => v.remove());
|
|
28154
|
-
}
|
|
28167
|
+
console.log("[LiveKit] Clearing container content. Current innerHTML length:", remoteContainer.innerHTML.length);
|
|
28168
|
+
remoteContainer.innerHTML = "";
|
|
28155
28169
|
remoteContainer.style.backgroundImage = "none";
|
|
28156
28170
|
const videoEl = track.attach();
|
|
28157
28171
|
videoEl.style.width = "100%";
|
|
@@ -28309,6 +28323,19 @@ class ClinicWidget {
|
|
|
28309
28323
|
if (prev.status !== current.status || prev.doctorOnline !== current.doctorOnline || prev.permissionsGranted !== current.permissionsGranted) {
|
|
28310
28324
|
this.render(current.status, current);
|
|
28311
28325
|
this.checkInlineTrigger();
|
|
28326
|
+
if (current.status === WidgetStates.CONSULTATION) {
|
|
28327
|
+
if (prev.status !== WidgetStates.CONSULTATION) {
|
|
28328
|
+
this.joinVideoCall();
|
|
28329
|
+
}
|
|
28330
|
+
if (this.livekit) {
|
|
28331
|
+
const modal = this.shadowRoot.querySelector(".td-consultation-modal");
|
|
28332
|
+
if (modal) {
|
|
28333
|
+
console.log("[Widget] State Changed -> Updating LiveKit wrapper to new DOM");
|
|
28334
|
+
this.livekit.updateWrapper(modal);
|
|
28335
|
+
this.livekit.refreshRemoteTracks();
|
|
28336
|
+
}
|
|
28337
|
+
}
|
|
28338
|
+
}
|
|
28312
28339
|
}
|
|
28313
28340
|
}
|
|
28314
28341
|
render(status, stateOverride = null) {
|
|
@@ -28405,6 +28432,25 @@ class ClinicWidget {
|
|
|
28405
28432
|
});
|
|
28406
28433
|
}
|
|
28407
28434
|
if (form) {
|
|
28435
|
+
const inputs = form.querySelectorAll("input");
|
|
28436
|
+
inputs.forEach((input) => {
|
|
28437
|
+
input.addEventListener("invalid", () => {
|
|
28438
|
+
if (input.validity.valueMissing) {
|
|
28439
|
+
if (input.name === "name") {
|
|
28440
|
+
input.setCustomValidity("Vui lòng nhập họ và tên của bạn.");
|
|
28441
|
+
} else if (input.name === "phone") {
|
|
28442
|
+
input.setCustomValidity("Vui lòng nhập số điện thoại.");
|
|
28443
|
+
} else {
|
|
28444
|
+
input.setCustomValidity("Vui lòng điền thông tin vào trường này.");
|
|
28445
|
+
}
|
|
28446
|
+
} else {
|
|
28447
|
+
input.setCustomValidity("Dữ liệu không hợp lệ.");
|
|
28448
|
+
}
|
|
28449
|
+
});
|
|
28450
|
+
input.addEventListener("input", () => {
|
|
28451
|
+
input.setCustomValidity("");
|
|
28452
|
+
});
|
|
28453
|
+
});
|
|
28408
28454
|
form.addEventListener("submit", async (e2) => {
|
|
28409
28455
|
e2.preventDefault();
|
|
28410
28456
|
const formData = new FormData(form);
|
|
@@ -28435,7 +28481,6 @@ class ClinicWidget {
|
|
|
28435
28481
|
});
|
|
28436
28482
|
}
|
|
28437
28483
|
} else if (status === WidgetStates.CONSULTATION) {
|
|
28438
|
-
this.joinVideoCall();
|
|
28439
28484
|
const modal = this.shadowRoot.querySelector(".td-consultation-modal");
|
|
28440
28485
|
if (this.livekit && modal) {
|
|
28441
28486
|
this.livekit.updateWrapper(modal);
|
|
@@ -28619,11 +28664,25 @@ class ClinicWidget {
|
|
|
28619
28664
|
if (!state.permissionsGranted) return;
|
|
28620
28665
|
const videoUrl = this.config.urlVideo || state.urlVideo;
|
|
28621
28666
|
if (!videoUrl) return;
|
|
28667
|
+
if (this.livekit && this.livekit.hasRemoteVideo()) {
|
|
28668
|
+
console.log("[Widget] Remote video active, skipping intro video injection.");
|
|
28669
|
+
return;
|
|
28670
|
+
}
|
|
28622
28671
|
const placeholder = this.shadowRoot.querySelector("#td-video-placeholder");
|
|
28623
28672
|
const videoContainer = this.shadowRoot.querySelector("#td-video-container") || this.shadowRoot.querySelector(".td-main-video");
|
|
28624
28673
|
const existingVideo = this.shadowRoot.querySelector("video");
|
|
28625
28674
|
if (existingVideo) {
|
|
28626
|
-
if (existingVideo.
|
|
28675
|
+
if (existingVideo.muted) {
|
|
28676
|
+
console.log("[Widget] Found muted existing video, attempting to unmute...");
|
|
28677
|
+
existingVideo.muted = false;
|
|
28678
|
+
}
|
|
28679
|
+
if (existingVideo.paused) {
|
|
28680
|
+
existingVideo.play().catch((e2) => {
|
|
28681
|
+
console.warn("[Widget] Play existing video failed, fallback to muted:", e2);
|
|
28682
|
+
existingVideo.muted = true;
|
|
28683
|
+
existingVideo.play();
|
|
28684
|
+
});
|
|
28685
|
+
}
|
|
28627
28686
|
if (placeholder) placeholder.remove();
|
|
28628
28687
|
return;
|
|
28629
28688
|
}
|
|
@@ -28637,7 +28696,7 @@ class ClinicWidget {
|
|
|
28637
28696
|
videoEl.src = videoUrl;
|
|
28638
28697
|
videoEl.autoplay = true;
|
|
28639
28698
|
videoEl.loop = true;
|
|
28640
|
-
videoEl.muted =
|
|
28699
|
+
videoEl.muted = false;
|
|
28641
28700
|
videoEl.playsInline = true;
|
|
28642
28701
|
videoEl.style.cssText = "width: 100%; height: 100%; object-fit: cover; position: absolute; top: 0; left: 0; z-index: 0;";
|
|
28643
28702
|
if (placeholder) {
|
|
@@ -28650,7 +28709,11 @@ class ClinicWidget {
|
|
|
28650
28709
|
videoContainer.prepend(videoEl);
|
|
28651
28710
|
}
|
|
28652
28711
|
}
|
|
28653
|
-
videoEl.play().catch((e2) =>
|
|
28712
|
+
videoEl.play().catch((e2) => {
|
|
28713
|
+
console.warn("[Widget] Autoplay with sound failed, falling back to muted.", e2);
|
|
28714
|
+
videoEl.muted = true;
|
|
28715
|
+
videoEl.play().catch((err) => console.error("[Widget] Autoplay completely failed:", err));
|
|
28716
|
+
});
|
|
28654
28717
|
}
|
|
28655
28718
|
async attemptVideoConnection() {
|
|
28656
28719
|
if (!this.videoToken || !this.livekit) {
|
|
@@ -28690,7 +28753,7 @@ class ClinicWidget {
|
|
|
28690
28753
|
var _a;
|
|
28691
28754
|
const state = store.getState();
|
|
28692
28755
|
const { user, doctor } = state;
|
|
28693
|
-
const userId = "
|
|
28756
|
+
const userId = "guest_6rakxwiai";
|
|
28694
28757
|
this.videoToken = null;
|
|
28695
28758
|
if (this.socket) {
|
|
28696
28759
|
console.log("Joining video call room with user:", user == null ? void 0 : user.name);
|
|
@@ -28754,9 +28817,10 @@ class ClinicWidget {
|
|
|
28754
28817
|
closeModal();
|
|
28755
28818
|
store.setState({
|
|
28756
28819
|
permissionsGranted: true,
|
|
28757
|
-
urlVideo: this.config.urlVideo
|
|
28820
|
+
urlVideo: this.config.urlVideo,
|
|
28821
|
+
// Force switch to consultation if not already (should be handled by logic, but ensuring state reflects it)
|
|
28822
|
+
status: WidgetStates.CONSULTATION
|
|
28758
28823
|
});
|
|
28759
|
-
this.render(WidgetStates.CONSULTATION, store.getState());
|
|
28760
28824
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
28761
28825
|
if (!this.livekit) {
|
|
28762
28826
|
this.livekit = new LiveKitService();
|