clinic-connect-widget 1.0.1 → 1.0.3
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 +119 -119
- package/dist/clinic-widget.es.js +464 -374
- package/dist/clinic-widget.umd.js +1 -1
- package/package.json +32 -31
package/dist/clinic-widget.es.js
CHANGED
|
@@ -105,355 +105,356 @@ const CONFIG = {
|
|
|
105
105
|
LIVEKIT_URL: "wss://livekit.longvan.vn"
|
|
106
106
|
};
|
|
107
107
|
const styles = ':host{--td-primary: #0e65f1;--td-primary-hover: #0b50c0;--td-bg-light: #ffffff;--td-bg-dark: #1a202c;--td-text-main: #0d131c;--td-text-sub: #49699c;--td-border: #e7ecf4;--td-success: #22c55e;--td-danger: #ef4444;--font-family: "Inter", system-ui, -apple-system, sans-serif;--z-index: 9999;font-family:var(--font-family);color:var(--td-text-main)}*{box-sizing:border-box;margin:0;padding:0}.hidden{display:none!important}.material-symbols-outlined{font-family:Material Symbols Outlined;font-weight:400;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr}.td-widget-fab{position:fixed;bottom:24px;right:24px;display:flex;align-items:center;gap:12px;z-index:var(--z-index);cursor:pointer;font-family:var(--font-family)}.td-fab-card{background:var(--td-bg-light);border:1px solid var(--td-border);border-radius:12px;box-shadow:0 8px 30px #0000001f;display:flex;align-items:center;padding:6px 16px 6px 6px;transition:transform .2s ease,box-shadow .2s ease;max-width:320px;position:relative;overflow:hidden}.td-fab-card:hover{transform:translateY(-4px);box-shadow:0 20px 40px #00000026}.td-avatar-container{width:64px;height:64px;border-radius:8px;overflow:hidden;position:relative;flex-shrink:0}.td-avatar{width:100%;height:100%;background-size:cover;background-position:center}.td-status-dot{position:absolute;bottom:4px;right:4px;width:14px;height:14px;background:var(--td-success);border:2px solid #fff;border-radius:50%}.td-info{margin-left:12px;display:flex;flex-direction:column}.td-brand{display:flex;align-items:center;gap:4px;font-size:10px;font-weight:700;text-transform:uppercase;color:var(--td-primary);letter-spacing:.05em}.td-cta{font-size:15px;font-weight:700;color:var(--td-text-main);margin-top:2px}.td-sub{font-size:12px;color:var(--td-text-sub);margin-top:2px}.td-close-fab{position:absolute;top:4px;right:4px;opacity:0;cursor:pointer;background:none;border:none;color:#94a3b8;transition:opacity .2s;padding:4px}.td-fab-card:hover .td-close-fab{opacity:1}.td-widget-expanded{position:fixed;bottom:100px;right:24px;width:380px;max-height:80vh;background:var(--td-bg-light);border-radius:12px;box-shadow:0 25px 50px -12px #00000040;border:1px solid var(--td-border);display:flex;flex-direction:column;overflow:hidden;z-index:var(--z-index);animation:slideUp .3s cubic-bezier(.16,1,.3,1)}@keyframes slideUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}.td-header{display:flex;align-items:center;justify-content:space-between;padding:16px;border-bottom:1px solid var(--td-border)}.td-header-title{display:flex;align-items:center;gap:12px;font-weight:700;font-size:18px}.td-icon-box{width:32px;height:32px;background:#0e65f11a;color:var(--td-primary);border-radius:8px;display:flex;align-items:center;justify-content:center}.td-close-btn{background:transparent;border:none;cursor:pointer;color:#64748b;padding:4px;border-radius:50%;display:flex;align-items:center;justify-content:center}.td-close-btn:hover{background:#f1f5f9}.td-content{flex:1;overflow-y:auto;padding:0}.td-doctor-profile{display:flex;flex-direction:column;align-items:center;padding:32px 24px 24px}.td-online-badge{display:inline-flex;align-items:center;gap:6px;padding:4px 12px;background:#f0fdf4;border:1px solid #dcfce7;border-radius:999px}.td-pulse{width:8px;height:8px;background:var(--td-success);border-radius:50%}.td-badge-text{font-size:12px;font-weight:600;color:#15803d;text-transform:uppercase}.td-trust-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px;padding:0 24px 24px}.td-trust-item{background:#f8f9fc;border:1px solid var(--td-border);border-radius:12px;padding:12px;text-align:center;display:flex;flex-direction:column;align-items:center;gap:8px}.td-trust-title{font-size:12px;font-weight:700}.td-actions{padding:0 24px 24px;display:flex;flex-direction:column;gap:12px}.td-btn{width:100%;height:48px;border-radius:8px;font-size:16px;font-weight:700;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all .2s;border:none}.td-btn-primary{background:var(--td-primary);color:#fff;box-shadow:0 10px 15px -3px #0e65f133}.td-btn-primary:hover{background:var(--td-primary-hover)}.td-btn-outline{background:transparent;border:1px solid var(--td-border);color:var(--td-text-main);font-size:14px}.td-btn-outline:hover{background:#f8f9fc}.td-footer{padding:12px;text-align:center;background:#f8f9fc;border-top:1px solid var(--td-border);font-size:12px;color:var(--td-text-sub)}.td-consultation-modal{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:90vw;height:90vh;max-width:1200px;max-height:800px;background:var(--td-bg-light);border-radius:16px;box-shadow:0 25px 50px -12px #00000080;border:1px solid var(--td-border);display:flex;overflow:hidden;z-index:var(--z-index);animation:fadeIn .3s ease-out}@keyframes fadeIn{0%{opacity:0;transform:translate(-50%,-48%)}to{opacity:1;transform:translate(-50%,-50%)}}.td-video-panel{flex:1;background:#000;position:relative;display:flex;align-items:center;justify-content:center;overflow:hidden}.td-main-video{width:100%;height:100%;background-size:cover;background-position:center}.td-overlay{position:absolute;top:0;right:0;bottom:0;left:0;background:linear-gradient(to bottom,rgba(0,0,0,.3),transparent,rgba(0,0,0,.6))}.td-video-header{position:absolute;top:16px;left:16px;right:16px;display:flex;justify-content:space-between;z-index:10}.td-badge-glass{background:#0006;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);padding:6px 12px;border-radius:20px;display:flex;align-items:center;gap:8px;color:#fff;font-size:12px;font-weight:500}.td-pip{position:absolute;top:24px;right:24px;width:160px;aspect-ratio:16/9;background:#333;border-radius:8px;border:2px solid rgba(255,255,255,.2);overflow:hidden;z-index:20;background-size:cover;background-position:center;box-shadow:0 4px 6px #0000004d}.td-pip-label{position:absolute;bottom:4px;left:4px;background:#0009;color:#fff;font-size:10px;padding:2px 6px;border-radius:4px}.td-controls{position:absolute;bottom:32px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:16px;z-index:20}.td-ctrl-btn{width:48px;height:48px;border-radius:50%;background:#fff3;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);border:none;color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s}.td-ctrl-btn:hover{background:#fff;color:var(--td-primary)}.td-ctrl-end{height:48px;padding:0 24px;border-radius:24px;background:var(--td-danger);color:#fff;display:flex;align-items:center;gap:8px;font-weight:600;border:none;cursor:pointer}.td-ctrl-end:hover{background:#dc2626}.td-chat-panel{width:400px;background:var(--td-bg-light);border-left:1px solid var(--td-border);display:flex;flex-direction:column}.td-chat-header{padding:16px;border-bottom:1px solid var(--td-border)}.td-chat-doc-info{display:flex;gap:12px;align-items:center;margin-bottom:12px}.td-chat-avatar{width:48px;height:48px;border-radius:50%;background-size:cover;background-position:center}.td-chat-timer{background:#f8f9fc;padding:8px 12px;border-radius:8px;display:flex;justify-content:space-between;align-items:center}.td-timer-val{font-family:monospace;font-weight:700;font-size:16px}.td-chat-messages{flex:1;overflow-y:auto;padding:16px;background:#f8f9fc;display:flex;flex-direction:column;gap:16px}.td-msg-system{text-align:center;font-size:12px;color:var(--td-text-sub);background:#e2e8f0;padding:4px 12px;border-radius:12px;align-self:center;max-width:90%}.td-msg-row{display:flex;gap:8px}.td-msg-row.own{flex-direction:row-reverse}.td-msg-bubble{padding:10px 14px;border-radius:12px;font-size:14px;line-height:1.5;max-width:85%}.td-msg-row.doctor .td-msg-bubble{background:#fff;border:1px solid var(--td-border);border-top-left-radius:2px}.td-msg-row.own .td-msg-bubble{background:#e0f2fe;color:var(--td-text-main);border:1px solid #bae6fd;border-top-right-radius:2px}.td-msg-time{font-size:10px;color:var(--td-text-sub);margin-top:2px}.td-chat-input-area{padding:16px;border-top:1px solid var(--td-border);background:var(--td-bg-light)}.td-input-wrapper{display:flex;gap:8px;align-items:flex-end}.td-chat-input{flex:1;border:1px solid var(--td-border);border-radius:8px;padding:10px;font-family:inherit;font-size:14px;resize:none;height:48px}.td-send-btn{width:48px;height:48px;background:var(--td-primary);color:#fff;border:none;border-radius:8px;cursor:pointer;display:flex;align-items:center;justify-content:center}.td-send-btn:hover{background:var(--td-primary-hover)}@media (max-width: 768px){.td-consultation-modal{width:100vw;height:100vh;border-radius:0;flex-direction:column}.td-video-panel{height:40vh}.td-chat-panel{width:100%;flex:1}}.td-form-container{display:flex;flex-direction:column;height:100%;padding:0 24px}.td-form-page-header{padding:24px 0 16px}.td-form-title{font-size:24px;font-weight:700;color:var(--td-text-main);margin-bottom:8px}.td-form-desc{font-size:14px;color:var(--td-text-sub);line-height:normal}.td-form{display:flex;flex-direction:column;gap:20px;padding-bottom:24px}.td-input-group{display:flex;flex-direction:column;gap:8px}.td-label{font-size:14px;font-weight:500;color:var(--td-text-main)}.td-input{width:100%;height:48px;padding:0 16px;border-radius:8px;border:1px solid var(--td-border);background:#f8f9fc;font-family:inherit;font-size:16px;color:var(--td-text-main);transition:all .2s}.td-input:focus{outline:none;border-color:var(--td-primary);box-shadow:0 0 0 2px #0e65f133}.td-textarea{width:100%;min-height:100px;padding:16px;border-radius:8px;border:1px solid var(--td-border);background:#f8f9fc;font-family:inherit;font-size:16px;color:var(--td-text-main);resize:none}.td-textarea:focus{outline:none;border-color:var(--td-primary)}.td-chips-group{display:flex;flex-wrap:wrap;gap:8px}.td-chip{padding:8px 16px;border-radius:99px;font-size:14px;font-weight:500;cursor:pointer;border:1px solid var(--td-border);background:#fff;color:var(--td-text-sub);transition:all .2s}.td-chip:hover{border-color:var(--td-primary);color:var(--td-primary);background:#0e65f10d}.td-chip.active{border-color:var(--td-primary);background:#0e65f11a;color:var(--td-primary);display:flex;align-items:center;gap:6px}.td-secure-note{display:flex;align-items:center;justify-content:center;gap:6px;font-size:12px;color:var(--td-text-sub);opacity:.8;margin-top:8px}.td-summary-container{display:flex;flex-direction:column;height:100%}.td-success-header{display:flex;flex-direction:column;align-items:center;padding:32px 24px;text-align:center}.td-success-icon{width:80px;height:80px;background:#0e65f11a;color:var(--td-primary);border-radius:50%;display:flex;align-items:center;justify-content:center;margin-bottom:16px}.td-summary-title{font-size:24px;font-weight:700;margin-bottom:8px}.td-summary-desc{font-size:14px;color:var(--td-text-sub);max-width:360px;margin:0 auto}.td-summary-card{margin:0 24px;padding:16px;border-radius:12px;border:1px solid var(--td-border);background:#f8f9fc;display:flex;align-items:center;gap:16px}.td-summary-details{padding:8px 24px}.td-detail-row{display:flex;justify-content:space-between;padding:12px 0;border-bottom:1px dashed var(--td-border);font-size:14px}.td-note-box{margin:16px 24px;padding:16px;background:#0e65f10d;border:1px solid rgba(14,101,241,.1);border-radius:8px}.td-note-title{font-size:14px;font-weight:700;margin-bottom:8px;display:flex;align-items:center;gap:8px}.td-grid-actions{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:12px}';
|
|
108
|
-
const collapsedHtml = `<div class="td-widget-fab" id="td-widget-fab"
|
|
109
|
-
<div class="td-fab-card"
|
|
110
|
-
<button class="td-close-fab" id="td-fab-close"
|
|
111
|
-
<span class="material-symbols-outlined" style="font-size: 16px;">close</span
|
|
112
|
-
</button
|
|
113
|
-
<div class="td-avatar-container"
|
|
114
|
-
<div class="td-avatar" style="background-image: url('{{doctorAvatar}}');"></div
|
|
115
|
-
<div class="td-status-dot" style="background: #22c55e;"></div
|
|
116
|
-
</div
|
|
117
|
-
<div class="td-info"
|
|
118
|
-
<div class="td-brand"
|
|
119
|
-
<span class="material-symbols-outlined" style="font-size: 14px;">local_hospital</span
|
|
120
|
-
<span>
|
|
121
|
-
</div
|
|
122
|
-
<h3 class="td-cta">Khám online ngay</h3
|
|
123
|
-
<p class="td-sub">Bác sĩ đang Online</p
|
|
124
|
-
</div
|
|
125
|
-
</div
|
|
108
|
+
const collapsedHtml = `<div class="td-widget-fab" id="td-widget-fab">\r
|
|
109
|
+
<div class="td-fab-card">\r
|
|
110
|
+
<button class="td-close-fab" id="td-fab-close">\r
|
|
111
|
+
<span class="material-symbols-outlined" style="font-size: 16px;">close</span>\r
|
|
112
|
+
</button>\r
|
|
113
|
+
<div class="td-avatar-container">\r
|
|
114
|
+
<div class="td-avatar" style="background-image: url('{{doctorAvatar}}');"></div>\r
|
|
115
|
+
<div class="td-status-dot" style="background: #22c55e;"></div>\r
|
|
116
|
+
</div>\r
|
|
117
|
+
<div class="td-info">\r
|
|
118
|
+
<div class="td-brand">\r
|
|
119
|
+
<span class="material-symbols-outlined" style="font-size: 14px;">local_hospital</span>\r
|
|
120
|
+
<span>Tư vấn ngay</span>\r
|
|
121
|
+
</div>\r
|
|
122
|
+
<h3 class="td-cta">Khám online ngay</h3>\r
|
|
123
|
+
<p class="td-sub">Bác sĩ đang Online</p>\r
|
|
124
|
+
</div>\r
|
|
125
|
+
</div>\r
|
|
126
126
|
</div>`;
|
|
127
|
-
const expandedHtml = `<div class="td-widget-expanded" id="td-widget-expanded"
|
|
128
|
-
<header class="td-header"
|
|
129
|
-
<div class="td-header-title"
|
|
130
|
-
<div class="td-icon-box"
|
|
131
|
-
<span class="material-symbols-outlined">medical_services</span
|
|
132
|
-
</div
|
|
133
|
-
<span>Clinic</span
|
|
134
|
-
</div
|
|
135
|
-
<button class="td-close-btn" id="td-expanded-close"
|
|
136
|
-
<span class="material-symbols-outlined">close</span
|
|
137
|
-
</button
|
|
138
|
-
</header
|
|
139
|
-
|
|
140
|
-
<div class="td-content"
|
|
141
|
-
<div class="td-doctor-profile"
|
|
142
|
-
<div class="td-doctor-avatar" style="background-image: url('{{doctorAvatar}}');"
|
|
143
|
-
<div class="td-status-dot"
|
|
144
|
-
style="width: 20px; height: 20px; border-width: 3px; bottom: 4px; right: 4px; background: #22c55e;"
|
|
145
|
-
</div
|
|
146
|
-
</div
|
|
147
|
-
<h1 class="td-doctor-name">{{doctorName}}</h1
|
|
148
|
-
<p class="td-doctor-specialty">{{doctorSpecialty}}</p
|
|
149
|
-
|
|
150
|
-
<div class="td-online-badge"
|
|
151
|
-
<span class="td-pulse"></span
|
|
152
|
-
<span class="td-badge-text">Đang trực tuyến</span
|
|
153
|
-
</div
|
|
154
|
-
</div
|
|
155
|
-
|
|
156
|
-
<div class="td-trust-grid"
|
|
157
|
-
<div class="td-trust-item"
|
|
158
|
-
<div class="td-icon-box"
|
|
159
|
-
<span class="material-symbols-outlined">verified</span
|
|
160
|
-
</div
|
|
161
|
-
<h3 class="td-trust-title">Bác sĩ được xác thực</h3
|
|
162
|
-
</div
|
|
163
|
-
<div class="td-trust-item"
|
|
164
|
-
<div class="td-icon-box"
|
|
165
|
-
<span class="material-symbols-outlined">security</span
|
|
166
|
-
</div
|
|
167
|
-
<h3 class="td-trust-title">Bảo mật thông tin</h3
|
|
168
|
-
</div
|
|
169
|
-
</div
|
|
170
|
-
|
|
171
|
-
<div class="td-actions"
|
|
172
|
-
<button class="td-btn td-btn-primary" id="td-btn-consult"
|
|
173
|
-
<span class="material-symbols-outlined" style="margin-right: 8px;">videocam</span
|
|
174
|
-
Bắt đầu tư vấn ngay
|
|
175
|
-
</button
|
|
176
|
-
<button class="td-btn td-btn-outline" id="td-btn-schedule"
|
|
177
|
-
Đặt lịch hẹn sau
|
|
178
|
-
</button
|
|
179
|
-
</div
|
|
180
|
-
</div
|
|
181
|
-
|
|
182
|
-
<div class="td-footer"
|
|
183
|
-
Powered by <b>Clinic Connect</b
|
|
184
|
-
</div
|
|
127
|
+
const expandedHtml = `<div class="td-widget-expanded" id="td-widget-expanded">\r
|
|
128
|
+
<header class="td-header">\r
|
|
129
|
+
<div class="td-header-title">\r
|
|
130
|
+
<div class="td-icon-box">\r
|
|
131
|
+
<span class="material-symbols-outlined">medical_services</span>\r
|
|
132
|
+
</div>\r
|
|
133
|
+
<span>Clinic</span>\r
|
|
134
|
+
</div>\r
|
|
135
|
+
<button class="td-close-btn" id="td-expanded-close">\r
|
|
136
|
+
<span class="material-symbols-outlined">close</span>\r
|
|
137
|
+
</button>\r
|
|
138
|
+
</header>\r
|
|
139
|
+
\r
|
|
140
|
+
<div class="td-content">\r
|
|
141
|
+
<div class="td-doctor-profile">\r
|
|
142
|
+
<div class="td-doctor-avatar" style="background-image: url('{{doctorAvatar}}');">\r
|
|
143
|
+
<div class="td-status-dot"\r
|
|
144
|
+
style="width: 20px; height: 20px; border-width: 3px; bottom: 4px; right: 4px; background: #22c55e;">\r
|
|
145
|
+
</div>\r
|
|
146
|
+
</div>\r
|
|
147
|
+
<h1 class="td-doctor-name">{{doctorName}}</h1>\r
|
|
148
|
+
<p class="td-doctor-specialty">{{doctorSpecialty}}</p>\r
|
|
149
|
+
\r
|
|
150
|
+
<div class="td-online-badge">\r
|
|
151
|
+
<span class="td-pulse"></span>\r
|
|
152
|
+
<span class="td-badge-text">Đang trực tuyến</span>\r
|
|
153
|
+
</div>\r
|
|
154
|
+
</div>\r
|
|
155
|
+
\r
|
|
156
|
+
<div class="td-trust-grid">\r
|
|
157
|
+
<div class="td-trust-item">\r
|
|
158
|
+
<div class="td-icon-box">\r
|
|
159
|
+
<span class="material-symbols-outlined">verified</span>\r
|
|
160
|
+
</div>\r
|
|
161
|
+
<h3 class="td-trust-title">Bác sĩ được xác thực</h3>\r
|
|
162
|
+
</div>\r
|
|
163
|
+
<div class="td-trust-item">\r
|
|
164
|
+
<div class="td-icon-box">\r
|
|
165
|
+
<span class="material-symbols-outlined">security</span>\r
|
|
166
|
+
</div>\r
|
|
167
|
+
<h3 class="td-trust-title">Bảo mật thông tin</h3>\r
|
|
168
|
+
</div>\r
|
|
169
|
+
</div>\r
|
|
170
|
+
\r
|
|
171
|
+
<div class="td-actions">\r
|
|
172
|
+
<button class="td-btn td-btn-primary" id="td-btn-consult">\r
|
|
173
|
+
<span class="material-symbols-outlined" style="margin-right: 8px;">videocam</span>\r
|
|
174
|
+
Bắt đầu tư vấn ngay\r
|
|
175
|
+
</button>\r
|
|
176
|
+
<button class="td-btn td-btn-outline" id="td-btn-schedule">\r
|
|
177
|
+
Đặt lịch hẹn sau\r
|
|
178
|
+
</button>\r
|
|
179
|
+
</div>\r
|
|
180
|
+
</div>\r
|
|
181
|
+
\r
|
|
182
|
+
<div class="td-footer">\r
|
|
183
|
+
Powered by <b>Clinic Connect</b>\r
|
|
184
|
+
</div>\r
|
|
185
185
|
</div>`;
|
|
186
|
-
const consultationHtml = `<div class="td-consultation-modal"
|
|
187
|
-
<!-- Left: Video Panel
|
|
188
|
-
<div class="td-video-panel"
|
|
189
|
-
<div class="td-main-video"
|
|
190
|
-
style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuC54B68xwAiOWZFQNVMIVKNBTKwo-nPCTsEgkaekhx9PbGDRCwcy18cFeALhbiaeOyaIS88c9RZst2sRWUyKTeSif-HqEfgU5Aoht-mk6Tjtuts2M4XmoMaxNSAi7qjKoQRYx61I4MolGfjCkxRN0lW5rlSlQfMwY0T8K1440SLM9hW_4u0N1Vg6kQTcSUitouGRmWktBHr-yH5QTu78AC1IOZLd5-HyWw0NUb6uESPpNZoZHoIEKIjFhsGBnCRmzHCUP9Ex6mP0XvW');"
|
|
191
|
-
</div
|
|
192
|
-
<div class="td-overlay"></div
|
|
193
|
-
|
|
194
|
-
<div class="td-video-header"
|
|
195
|
-
<div class="td-badge-glass"
|
|
196
|
-
<span class="material-symbols-outlined"
|
|
197
|
-
style="color: #ef4444; font-size: 16px;">radio_button_checked</span
|
|
198
|
-
REC
|
|
199
|
-
</div
|
|
200
|
-
<div class="td-badge-glass"
|
|
201
|
-
<span class="material-symbols-outlined" style="font-size: 16px;">signal_cellular_alt</span
|
|
202
|
-
HD Quality
|
|
203
|
-
</div
|
|
204
|
-
</div
|
|
205
|
-
|
|
206
|
-
<div class="td-pip"
|
|
207
|
-
style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuDnITTxxwp-J6t7xkVqnh6C9NPQS8i_k6_4F4IfzTfDM_ZgdX2HRvc947W1BU0LJYKOAA8qtHglPXseoj0Vk4sLfoRnKDOz88bUmK_ytSEvP_oYeeDKGRCfcEa6ugQO6aKR2l9W8glr63dswTODHe4am5IB3FRknTxNnZ0XuDU0z1xzkDMGpspRrXWwdoi8j9teleBeRsmkCdSe8dVb0q1r5eS8YjQ-nzo5f3qBB2-Eo9zyegUSaOCXhuaqSmqpVyICmTLvoIppbKNo');"
|
|
208
|
-
<span class="td-pip-label">You</span
|
|
209
|
-
</div
|
|
210
|
-
|
|
211
|
-
<div class="td-controls"
|
|
212
|
-
<button class="td-ctrl-btn"
|
|
213
|
-
<span class="material-symbols-outlined">mic</span
|
|
214
|
-
</button
|
|
215
|
-
<button class="td-ctrl-btn"
|
|
216
|
-
<span class="material-symbols-outlined">videocam</span
|
|
217
|
-
</button
|
|
218
|
-
<button class="td-ctrl-end" id="td-consult-end"
|
|
219
|
-
<span class="material-symbols-outlined">call_end</span
|
|
220
|
-
Kết thúc
|
|
221
|
-
</button
|
|
222
|
-
<button class="td-ctrl-btn"
|
|
223
|
-
<span class="material-symbols-outlined">settings</span
|
|
224
|
-
</button
|
|
225
|
-
</div
|
|
226
|
-
</div
|
|
227
|
-
|
|
228
|
-
<!-- Right: Chat Panel
|
|
229
|
-
<div class="td-chat-panel"
|
|
230
|
-
<div class="td-chat-header"
|
|
231
|
-
<div class="td-chat-doc-info"
|
|
232
|
-
<div class="td-chat-avatar" style="background-image: url('{{doctorAvatar}}');"></div
|
|
233
|
-
<div
|
|
234
|
-
<h3 style="font-size: 16px; font-weight: 700;">{{doctorName}}</h3
|
|
235
|
-
<p style="font-size: 12px; color: var(--td-text-sub);">Online • Verified</p
|
|
236
|
-
</div
|
|
237
|
-
</div
|
|
238
|
-
<div class="td-chat-timer"
|
|
239
|
-
<div style="display: flex; align-items: center; gap: 4px; font-size: 12px;"
|
|
240
|
-
<span class="material-symbols-outlined"
|
|
241
|
-
style="font-size: 16px; color: var(--td-primary);">timer</span
|
|
242
|
-
SESSION
|
|
243
|
-
</div
|
|
244
|
-
<div class="td-timer-val">00:03:12</div
|
|
245
|
-
</div
|
|
246
|
-
</div
|
|
247
|
-
|
|
248
|
-
<div class="td-chat-messages"
|
|
249
|
-
<div class="td-msg-system"
|
|
250
|
-
Consultation started
|
|
251
|
-
</div
|
|
252
|
-
<div class="td-msg-row doctor"
|
|
253
|
-
<div class="td-msg-bubble"
|
|
254
|
-
Xin chào. Tôi có thể giúp gì cho bạn hôm nay
|
|
255
|
-
<div class="td-msg-time">14:01</div
|
|
256
|
-
</div
|
|
257
|
-
</div
|
|
258
|
-
<div class="td-msg-row own"
|
|
259
|
-
<div class="td-msg-bubble"
|
|
260
|
-
Chào bác sĩ, tôi bị đau đầu
|
|
261
|
-
<div class="td-msg-time">14:02</div
|
|
262
|
-
</div
|
|
263
|
-
</div
|
|
264
|
-
</div
|
|
265
|
-
|
|
266
|
-
<div class="td-chat-input-area"
|
|
267
|
-
<div class="td-input-wrapper"
|
|
268
|
-
<textarea class="td-chat-input" placeholder="Nhập tin nhắn..."></textarea
|
|
269
|
-
<button class="td-send-btn"
|
|
270
|
-
<span class="material-symbols-outlined">send</span
|
|
271
|
-
</button
|
|
272
|
-
</div
|
|
273
|
-
<div style="text-align: center; margin-top: 8px; font-size: 10px; color: var(--td-text-sub);"
|
|
274
|
-
<span class="material-symbols-outlined" style="font-size: 10px; vertical-align: middle;">lock</span
|
|
275
|
-
Mã hóa đầu cuối
|
|
276
|
-
</div
|
|
277
|
-
</div
|
|
278
|
-
</div
|
|
186
|
+
const consultationHtml = `<div class="td-consultation-modal">\r
|
|
187
|
+
<!-- Left: Video Panel -->\r
|
|
188
|
+
<div class="td-video-panel">\r
|
|
189
|
+
<div class="td-main-video"\r
|
|
190
|
+
style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuC54B68xwAiOWZFQNVMIVKNBTKwo-nPCTsEgkaekhx9PbGDRCwcy18cFeALhbiaeOyaIS88c9RZst2sRWUyKTeSif-HqEfgU5Aoht-mk6Tjtuts2M4XmoMaxNSAi7qjKoQRYx61I4MolGfjCkxRN0lW5rlSlQfMwY0T8K1440SLM9hW_4u0N1Vg6kQTcSUitouGRmWktBHr-yH5QTu78AC1IOZLd5-HyWw0NUb6uESPpNZoZHoIEKIjFhsGBnCRmzHCUP9Ex6mP0XvW');">\r
|
|
191
|
+
</div>\r
|
|
192
|
+
<div class="td-overlay"></div>\r
|
|
193
|
+
\r
|
|
194
|
+
<div class="td-video-header">\r
|
|
195
|
+
<div class="td-badge-glass">\r
|
|
196
|
+
<span class="material-symbols-outlined"\r
|
|
197
|
+
style="color: #ef4444; font-size: 16px;">radio_button_checked</span>\r
|
|
198
|
+
REC\r
|
|
199
|
+
</div>\r
|
|
200
|
+
<div class="td-badge-glass">\r
|
|
201
|
+
<span class="material-symbols-outlined" style="font-size: 16px;">signal_cellular_alt</span>\r
|
|
202
|
+
HD Quality\r
|
|
203
|
+
</div>\r
|
|
204
|
+
</div>\r
|
|
205
|
+
\r
|
|
206
|
+
<div class="td-pip"\r
|
|
207
|
+
style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuDnITTxxwp-J6t7xkVqnh6C9NPQS8i_k6_4F4IfzTfDM_ZgdX2HRvc947W1BU0LJYKOAA8qtHglPXseoj0Vk4sLfoRnKDOz88bUmK_ytSEvP_oYeeDKGRCfcEa6ugQO6aKR2l9W8glr63dswTODHe4am5IB3FRknTxNnZ0XuDU0z1xzkDMGpspRrXWwdoi8j9teleBeRsmkCdSe8dVb0q1r5eS8YjQ-nzo5f3qBB2-Eo9zyegUSaOCXhuaqSmqpVyICmTLvoIppbKNo');">\r
|
|
208
|
+
<span class="td-pip-label">You</span>\r
|
|
209
|
+
</div>\r
|
|
210
|
+
\r
|
|
211
|
+
<div class="td-controls">\r
|
|
212
|
+
<button class="td-ctrl-btn">\r
|
|
213
|
+
<span class="material-symbols-outlined">mic</span>\r
|
|
214
|
+
</button>\r
|
|
215
|
+
<button class="td-ctrl-btn">\r
|
|
216
|
+
<span class="material-symbols-outlined">videocam</span>\r
|
|
217
|
+
</button>\r
|
|
218
|
+
<button class="td-ctrl-end" id="td-consult-end">\r
|
|
219
|
+
<span class="material-symbols-outlined">call_end</span>\r
|
|
220
|
+
Kết thúc\r
|
|
221
|
+
</button>\r
|
|
222
|
+
<button class="td-ctrl-btn">\r
|
|
223
|
+
<span class="material-symbols-outlined">settings</span>\r
|
|
224
|
+
</button>\r
|
|
225
|
+
</div>\r
|
|
226
|
+
</div>\r
|
|
227
|
+
\r
|
|
228
|
+
<!-- Right: Chat Panel -->\r
|
|
229
|
+
<div class="td-chat-panel">\r
|
|
230
|
+
<div class="td-chat-header">\r
|
|
231
|
+
<div class="td-chat-doc-info">\r
|
|
232
|
+
<div class="td-chat-avatar" style="background-image: url('{{doctorAvatar}}');"></div>\r
|
|
233
|
+
<div>\r
|
|
234
|
+
<h3 style="font-size: 16px; font-weight: 700;">{{doctorName}}</h3>\r
|
|
235
|
+
<p style="font-size: 12px; color: var(--td-text-sub);">Online • Verified</p>\r
|
|
236
|
+
</div>\r
|
|
237
|
+
</div>\r
|
|
238
|
+
<div class="td-chat-timer">\r
|
|
239
|
+
<div style="display: flex; align-items: center; gap: 4px; font-size: 12px;">\r
|
|
240
|
+
<span class="material-symbols-outlined"\r
|
|
241
|
+
style="font-size: 16px; color: var(--td-primary);">timer</span>\r
|
|
242
|
+
SESSION\r
|
|
243
|
+
</div>\r
|
|
244
|
+
<div class="td-timer-val">00:03:12</div>\r
|
|
245
|
+
</div>\r
|
|
246
|
+
</div>\r
|
|
247
|
+
\r
|
|
248
|
+
<div class="td-chat-messages">\r
|
|
249
|
+
<div class="td-msg-system">\r
|
|
250
|
+
Consultation started\r
|
|
251
|
+
</div>\r
|
|
252
|
+
<div class="td-msg-row doctor">\r
|
|
253
|
+
<div class="td-msg-bubble">\r
|
|
254
|
+
Xin chào. Tôi có thể giúp gì cho bạn hôm nay?\r
|
|
255
|
+
<div class="td-msg-time">14:01</div>\r
|
|
256
|
+
</div>\r
|
|
257
|
+
</div>\r
|
|
258
|
+
<div class="td-msg-row own">\r
|
|
259
|
+
<div class="td-msg-bubble">\r
|
|
260
|
+
Chào bác sĩ, tôi bị đau đầu...\r
|
|
261
|
+
<div class="td-msg-time">14:02</div>\r
|
|
262
|
+
</div>\r
|
|
263
|
+
</div>\r
|
|
264
|
+
</div>\r
|
|
265
|
+
\r
|
|
266
|
+
<div class="td-chat-input-area">\r
|
|
267
|
+
<div class="td-input-wrapper">\r
|
|
268
|
+
<textarea class="td-chat-input" placeholder="Nhập tin nhắn..."></textarea>\r
|
|
269
|
+
<button class="td-send-btn">\r
|
|
270
|
+
<span class="material-symbols-outlined">send</span>\r
|
|
271
|
+
</button>\r
|
|
272
|
+
</div>\r
|
|
273
|
+
<div style="text-align: center; margin-top: 8px; font-size: 10px; color: var(--td-text-sub);">\r
|
|
274
|
+
<span class="material-symbols-outlined" style="font-size: 10px; vertical-align: middle;">lock</span>\r
|
|
275
|
+
Mã hóa đầu cuối\r
|
|
276
|
+
</div>\r
|
|
277
|
+
</div>\r
|
|
278
|
+
</div>\r
|
|
279
279
|
</div>`;
|
|
280
|
-
const patientFormHtml = '<div class="td-widget-expanded" id="td-patient-form">\n <header class="td-header">\n <div class="td-header-title">\n <div class="td-icon-box">\n <span class="material-symbols-outlined">medical_services</span>\n </div>\n <span>
|
|
281
|
-
const postConsultationHtml = `<div class="td-widget-expanded" id="td-summary-view"
|
|
282
|
-
<header class="td-header"
|
|
283
|
-
<h2 class="td-header-title" style="font-size: 16px;">Tổng kết tư vấn</h2
|
|
284
|
-
<button class="td-close-btn" id="td-summary-close"
|
|
285
|
-
<span class="material-symbols-outlined">close</span
|
|
286
|
-
</button
|
|
287
|
-
</header
|
|
288
|
-
|
|
289
|
-
<div class="td-content"
|
|
290
|
-
<div class="td-summary-container"
|
|
291
|
-
<div class="td-success-header"
|
|
292
|
-
<div class="td-success-icon"
|
|
293
|
-
<span class="material-symbols-outlined" style="font-size: 48px;">check_circle</span
|
|
294
|
-
</div
|
|
295
|
-
<h3 class="td-summary-title">Tư vấn Hoàn tất</h3
|
|
296
|
-
<p class="td-summary-desc">Cuộc hẹn đã được ghi nhận vào hồ sơ sức khỏe của bạn.</p
|
|
297
|
-
</div
|
|
298
|
-
|
|
299
|
-
<div class="td-summary-card"
|
|
300
|
-
<div class="td-chat-avatar"
|
|
301
|
-
style="background-image: url('{{doctorAvatar}}'); width: 56px; height: 56px;"></div
|
|
302
|
-
<div
|
|
303
|
-
<div style="font-weight: 700;">{{doctorName}}</div
|
|
304
|
-
<div style="color: var(--td-text-sub); font-size: 14px;">{{doctorSpecialty}}</div
|
|
305
|
-
</div
|
|
306
|
-
<div style="margin-left: auto; color: var(--td-primary);"
|
|
307
|
-
<span class="material-symbols-outlined">verified_user</span
|
|
308
|
-
</div
|
|
309
|
-
</div
|
|
310
|
-
|
|
311
|
-
<div class="td-summary-details"
|
|
312
|
-
<div class="td-detail-row"
|
|
313
|
-
<span style="color: var(--td-text-sub); display: flex; gap: 6px;"
|
|
314
|
-
<span class="material-symbols-outlined" style="font-size: 18px;">tag</span> Mã tư vấn
|
|
315
|
-
</span
|
|
316
|
-
<span style="font-weight: 600;">#TD-93821</span
|
|
317
|
-
</div
|
|
318
|
-
<div class="td-detail-row"
|
|
319
|
-
<span style="color: var(--td-text-sub); display: flex; gap: 6px;"
|
|
320
|
-
<span class="material-symbols-outlined" style="font-size: 18px;">schedule</span> Thời gian
|
|
321
|
-
</span
|
|
322
|
-
<span style="font-weight: 600;">10:00 AM, Today</span
|
|
323
|
-
</div
|
|
324
|
-
</div
|
|
325
|
-
|
|
326
|
-
<div class="td-note-box"
|
|
327
|
-
<div class="td-note-title"
|
|
328
|
-
<span class="material-symbols-outlined"
|
|
329
|
-
style="font-size: 16px; color: var(--td-primary);">clinical_notes</span
|
|
330
|
-
Ghi chú bác
|
|
331
|
-
</div
|
|
332
|
-
<p style="font-size: 14px; line-height: 1.5;">Bệnh nhân cần nghỉ ngơi và uống nhiều nước. Hạn chế vận
|
|
333
|
-
động mạnh trong 2 ngày tới.</p
|
|
334
|
-
</div
|
|
335
|
-
|
|
336
|
-
<div class="td-actions"
|
|
337
|
-
<div class="td-grid-actions"
|
|
338
|
-
<button class="td-btn td-btn-outline" style="font-size: 12px; height: 40px; margin-bottom: 0;"
|
|
339
|
-
Xem kết luận
|
|
340
|
-
</button
|
|
341
|
-
<button class="td-btn td-btn-outline" style="font-size: 12px; height: 40px; margin-bottom: 0;"
|
|
342
|
-
Tải đơn thuốc
|
|
343
|
-
</button
|
|
344
|
-
</div
|
|
345
|
-
<button class="td-btn td-btn-primary"
|
|
346
|
-
<span class="material-symbols-outlined" style="margin-right: 8px;">calendar_month</span
|
|
347
|
-
Đặt lịch tái khám
|
|
348
|
-
</button
|
|
349
|
-
</div
|
|
350
|
-
</div
|
|
351
|
-
</div
|
|
280
|
+
const patientFormHtml = '<div class="td-widget-expanded" 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</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</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 </div>\r\n </div> -->\r\n\r\n <div class="td-input-group">\r\n <label class="td-label">Mô tả thêm <span 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>';
|
|
281
|
+
const postConsultationHtml = `<div class="td-widget-expanded" id="td-summary-view">\r
|
|
282
|
+
<header class="td-header">\r
|
|
283
|
+
<h2 class="td-header-title" style="font-size: 16px;">Tổng kết tư vấn</h2>\r
|
|
284
|
+
<button class="td-close-btn" id="td-summary-close">\r
|
|
285
|
+
<span class="material-symbols-outlined">close</span>\r
|
|
286
|
+
</button>\r
|
|
287
|
+
</header>\r
|
|
288
|
+
\r
|
|
289
|
+
<div class="td-content">\r
|
|
290
|
+
<div class="td-summary-container">\r
|
|
291
|
+
<div class="td-success-header">\r
|
|
292
|
+
<div class="td-success-icon">\r
|
|
293
|
+
<span class="material-symbols-outlined" style="font-size: 48px;">check_circle</span>\r
|
|
294
|
+
</div>\r
|
|
295
|
+
<h3 class="td-summary-title">Tư vấn Hoàn tất</h3>\r
|
|
296
|
+
<p class="td-summary-desc">Cuộc hẹn đã được ghi nhận vào hồ sơ sức khỏe của bạn.</p>\r
|
|
297
|
+
</div>\r
|
|
298
|
+
\r
|
|
299
|
+
<div class="td-summary-card">\r
|
|
300
|
+
<div class="td-chat-avatar"\r
|
|
301
|
+
style="background-image: url('{{doctorAvatar}}'); width: 56px; height: 56px;"></div>\r
|
|
302
|
+
<div>\r
|
|
303
|
+
<div style="font-weight: 700;">{{doctorName}}</div>\r
|
|
304
|
+
<div style="color: var(--td-text-sub); font-size: 14px;">{{doctorSpecialty}}</div>\r
|
|
305
|
+
</div>\r
|
|
306
|
+
<div style="margin-left: auto; color: var(--td-primary);">\r
|
|
307
|
+
<span class="material-symbols-outlined">verified_user</span>\r
|
|
308
|
+
</div>\r
|
|
309
|
+
</div>\r
|
|
310
|
+
\r
|
|
311
|
+
<div class="td-summary-details">\r
|
|
312
|
+
<div class="td-detail-row">\r
|
|
313
|
+
<span style="color: var(--td-text-sub); display: flex; gap: 6px;">\r
|
|
314
|
+
<span class="material-symbols-outlined" style="font-size: 18px;">tag</span> Mã tư vấn\r
|
|
315
|
+
</span>\r
|
|
316
|
+
<span style="font-weight: 600;">#TD-93821</span>\r
|
|
317
|
+
</div>\r
|
|
318
|
+
<div class="td-detail-row">\r
|
|
319
|
+
<span style="color: var(--td-text-sub); display: flex; gap: 6px;">\r
|
|
320
|
+
<span class="material-symbols-outlined" style="font-size: 18px;">schedule</span> Thời gian\r
|
|
321
|
+
</span>\r
|
|
322
|
+
<span style="font-weight: 600;">10:00 AM, Today</span>\r
|
|
323
|
+
</div>\r
|
|
324
|
+
</div>\r
|
|
325
|
+
\r
|
|
326
|
+
<div class="td-note-box">\r
|
|
327
|
+
<div class="td-note-title">\r
|
|
328
|
+
<span class="material-symbols-outlined"\r
|
|
329
|
+
style="font-size: 16px; color: var(--td-primary);">clinical_notes</span>\r
|
|
330
|
+
Ghi chú bác sĩ\r
|
|
331
|
+
</div>\r
|
|
332
|
+
<p style="font-size: 14px; line-height: 1.5;">Bệnh nhân cần nghỉ ngơi và uống nhiều nước. Hạn chế vận\r
|
|
333
|
+
động mạnh trong 2 ngày tới.</p>\r
|
|
334
|
+
</div>\r
|
|
335
|
+
\r
|
|
336
|
+
<div class="td-actions">\r
|
|
337
|
+
<div class="td-grid-actions">\r
|
|
338
|
+
<button class="td-btn td-btn-outline" style="font-size: 12px; height: 40px; margin-bottom: 0;">\r
|
|
339
|
+
Xem kết luận\r
|
|
340
|
+
</button>\r
|
|
341
|
+
<button class="td-btn td-btn-outline" style="font-size: 12px; height: 40px; margin-bottom: 0;">\r
|
|
342
|
+
Tải đơn thuốc\r
|
|
343
|
+
</button>\r
|
|
344
|
+
</div>\r
|
|
345
|
+
<button class="td-btn td-btn-primary">\r
|
|
346
|
+
<span class="material-symbols-outlined" style="margin-right: 8px;">calendar_month</span>\r
|
|
347
|
+
Đặt lịch tái khám\r
|
|
348
|
+
</button>\r
|
|
349
|
+
</div>\r
|
|
350
|
+
</div>\r
|
|
351
|
+
</div>\r
|
|
352
352
|
</div>`;
|
|
353
|
-
const inlineTriggerHtml = `<style
|
|
354
|
-
@keyframes td-pulse-animation {
|
|
355
|
-
0% {
|
|
356
|
-
box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.4)
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
70% {
|
|
360
|
-
box-shadow: 0 0 0 6px rgba(34, 197, 94, 0)
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
100% {
|
|
364
|
-
box-shadow: 0 0 0 0 rgba(34, 197, 94, 0)
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
.td-inline-container {
|
|
369
|
-
display: flex
|
|
370
|
-
flex-direction: column
|
|
371
|
-
padding: 14px
|
|
372
|
-
background: #f8faff
|
|
373
|
-
border: 1px solid #e2e8f0
|
|
374
|
-
border-radius: 10px
|
|
375
|
-
font-family: system-ui, -apple-system, sans-serif
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
.td-inline-row {
|
|
379
|
-
display: flex
|
|
380
|
-
align-items: center
|
|
381
|
-
gap: 12px
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
.td-inline-footer {
|
|
385
|
-
display: flex
|
|
386
|
-
align-items: center
|
|
387
|
-
justify-content: space-between
|
|
388
|
-
margin-top: 14px
|
|
389
|
-
padding-top: 12px
|
|
390
|
-
border-top: 1px solid #e5e7eb
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
.td-inline-status {
|
|
394
|
-
display: flex
|
|
395
|
-
align-items: center
|
|
396
|
-
gap: 8px
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
.td-inline-pulse {
|
|
400
|
-
width: 8px
|
|
401
|
-
height: 8px
|
|
402
|
-
background: #22c55e
|
|
403
|
-
border-radius: 50
|
|
404
|
-
animation: td-pulse-animation 2s infinite
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
.td-inline-text {
|
|
408
|
-
color: #16a34a
|
|
409
|
-
font-weight: 600
|
|
410
|
-
font-size: 13px
|
|
411
|
-
}
|
|
412
|
-
</style
|
|
413
|
-
<div class="td-inline-container"
|
|
414
|
-
<!-- Top: Doctor Info
|
|
415
|
-
<div class="td-inline-row"
|
|
416
|
-
<div
|
|
417
|
-
style="width: 48px; height: 48px; border-radius: 50%; background-image: url('{{doctorAvatar}}'); background-size: cover; background-position: center; flex-shrink: 0; background-color: #e5e7eb; border: 2px solid white; box-shadow: 0 2px 4px rgba(0,0,0,0.05);"
|
|
418
|
-
</div
|
|
419
|
-
<div style="flex: 1; min-width: 0;"
|
|
420
|
-
<div
|
|
421
|
-
style="font-weight: 700; font-size: 15px; color: #1f2937; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;"
|
|
422
|
-
{{doctorName}}</div
|
|
423
|
-
<div
|
|
424
|
-
style="font-size: 13px; color: #6b7280; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;"
|
|
425
|
-
{{doctorSpecialty}}</div
|
|
426
|
-
</div
|
|
427
|
-
</div
|
|
428
|
-
|
|
429
|
-
<!-- Bottom: Status & Action
|
|
430
|
-
<div class="td-inline-footer"
|
|
431
|
-
<div class="td-inline-status"
|
|
432
|
-
<span class="td-inline-pulse"></span
|
|
433
|
-
<span class="td-inline-text">Bác sĩ đang Online</span
|
|
434
|
-
</div
|
|
435
|
-
|
|
436
|
-
<button id="td-inline-btn" style="
|
|
437
|
-
background: #0066ff;
|
|
438
|
-
color: white;
|
|
439
|
-
border: none;
|
|
440
|
-
padding: 8px 16px;
|
|
441
|
-
border-radius: 6px;
|
|
442
|
-
font-weight: 600;
|
|
443
|
-
font-size: 13px;
|
|
444
|
-
cursor: pointer;
|
|
445
|
-
display: flex;
|
|
446
|
-
align-items: center;
|
|
447
|
-
gap: 6px
|
|
448
|
-
white-space: nowrap
|
|
449
|
-
transition: all 0.2s
|
|
450
|
-
box-shadow: 0 2px 5px rgba(0,102,255,0.2)
|
|
451
|
-
" onmouseover="this.style.background='#0052cc'" onmouseout="this.style.background='#0066ff'"
|
|
452
|
-
<span class="material-symbols-outlined" style="font-size: 18px;">videocam</span
|
|
453
|
-
Tư vấn ngay
|
|
454
|
-
</button
|
|
455
|
-
</div
|
|
353
|
+
const inlineTriggerHtml = `<style>\r
|
|
354
|
+
@keyframes td-pulse-animation {\r
|
|
355
|
+
0% {\r
|
|
356
|
+
box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.4);\r
|
|
357
|
+
}\r
|
|
358
|
+
\r
|
|
359
|
+
70% {\r
|
|
360
|
+
box-shadow: 0 0 0 6px rgba(34, 197, 94, 0);\r
|
|
361
|
+
}\r
|
|
362
|
+
\r
|
|
363
|
+
100% {\r
|
|
364
|
+
box-shadow: 0 0 0 0 rgba(34, 197, 94, 0);\r
|
|
365
|
+
}\r
|
|
366
|
+
}\r
|
|
367
|
+
\r
|
|
368
|
+
.td-inline-container {\r
|
|
369
|
+
display: flex;\r
|
|
370
|
+
flex-direction: column;\r
|
|
371
|
+
padding: 14px;\r
|
|
372
|
+
background: #f8faff;\r
|
|
373
|
+
border: 1px solid #e2e8f0;\r
|
|
374
|
+
border-radius: 10px;\r
|
|
375
|
+
font-family: system-ui, -apple-system, sans-serif;\r
|
|
376
|
+
}\r
|
|
377
|
+
\r
|
|
378
|
+
.td-inline-row {\r
|
|
379
|
+
display: flex;\r
|
|
380
|
+
align-items: center;\r
|
|
381
|
+
gap: 12px;\r
|
|
382
|
+
}\r
|
|
383
|
+
\r
|
|
384
|
+
.td-inline-footer {\r
|
|
385
|
+
display: flex;\r
|
|
386
|
+
align-items: center;\r
|
|
387
|
+
justify-content: space-between;\r
|
|
388
|
+
margin-top: 14px;\r
|
|
389
|
+
padding-top: 12px;\r
|
|
390
|
+
border-top: 1px solid #e5e7eb;\r
|
|
391
|
+
}\r
|
|
392
|
+
\r
|
|
393
|
+
.td-inline-status {\r
|
|
394
|
+
display: flex;\r
|
|
395
|
+
align-items: center;\r
|
|
396
|
+
gap: 8px;\r
|
|
397
|
+
}\r
|
|
398
|
+
\r
|
|
399
|
+
.td-inline-pulse {\r
|
|
400
|
+
width: 8px;\r
|
|
401
|
+
height: 8px;\r
|
|
402
|
+
background: #22c55e;\r
|
|
403
|
+
border-radius: 50%;\r
|
|
404
|
+
animation: td-pulse-animation 2s infinite;\r
|
|
405
|
+
}\r
|
|
406
|
+
\r
|
|
407
|
+
.td-inline-text {\r
|
|
408
|
+
color: #16a34a;\r
|
|
409
|
+
font-weight: 600;\r
|
|
410
|
+
font-size: 13px;\r
|
|
411
|
+
}\r
|
|
412
|
+
</style>\r
|
|
413
|
+
<div class="td-inline-container">\r
|
|
414
|
+
<!-- Top: Doctor Info -->\r
|
|
415
|
+
<div class="td-inline-row">\r
|
|
416
|
+
<div\r
|
|
417
|
+
style="width: 48px; height: 48px; border-radius: 50%; background-image: url('{{doctorAvatar}}'); background-size: cover; background-position: center; flex-shrink: 0; background-color: #e5e7eb; border: 2px solid white; box-shadow: 0 2px 4px rgba(0,0,0,0.05);">\r
|
|
418
|
+
</div>\r
|
|
419
|
+
<div style="flex: 1; min-width: 0;">\r
|
|
420
|
+
<div\r
|
|
421
|
+
style="font-weight: 700; font-size: 15px; color: #1f2937; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">\r
|
|
422
|
+
{{doctorName}}</div>\r
|
|
423
|
+
<div\r
|
|
424
|
+
style="font-size: 13px; color: #6b7280; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">\r
|
|
425
|
+
{{doctorSpecialty}}</div>\r
|
|
426
|
+
</div>\r
|
|
427
|
+
</div>\r
|
|
428
|
+
\r
|
|
429
|
+
<!-- Bottom: Status & Action -->\r
|
|
430
|
+
<div class="td-inline-footer">\r
|
|
431
|
+
<div class="td-inline-status">\r
|
|
432
|
+
<span class="td-inline-pulse"></span>\r
|
|
433
|
+
<span class="td-inline-text">Bác sĩ đang Online</span>\r
|
|
434
|
+
</div>\r
|
|
435
|
+
\r
|
|
436
|
+
<button id="td-inline-btn" type="button" style="\r
|
|
437
|
+
background: #0066ff; \r
|
|
438
|
+
color: white; \r
|
|
439
|
+
border: none; \r
|
|
440
|
+
padding: 8px 16px; \r
|
|
441
|
+
border-radius: 6px; \r
|
|
442
|
+
font-weight: 600; \r
|
|
443
|
+
font-size: 13px; \r
|
|
444
|
+
cursor: pointer; \r
|
|
445
|
+
display: flex; \r
|
|
446
|
+
align-items: center; \r
|
|
447
|
+
gap: 6px;\r
|
|
448
|
+
white-space: nowrap;\r
|
|
449
|
+
transition: all 0.2s;\r
|
|
450
|
+
box-shadow: 0 2px 5px rgba(0,102,255,0.2);\r
|
|
451
|
+
" onmouseover="this.style.background='#0052cc'" onmouseout="this.style.background='#0066ff'">\r
|
|
452
|
+
<span class="material-symbols-outlined" style="font-size: 18px;">videocam</span>\r
|
|
453
|
+
Tư vấn ngay\r
|
|
454
|
+
</button>\r
|
|
455
|
+
</div>\r
|
|
456
456
|
</div>`;
|
|
457
|
+
const permissionModalHtml = '<div class="modalOverlay" id="td-permission-modal">\r\n <div class="modal">\r\n <div class="modalHead">\r\n <h2>Cho phép truy cập Microphone và Camera</h2>\r\n <button id="td-perm-close" class="btn btn-icon" aria-label="Đóng">\r\n <span class="material-symbols-outlined" style="font-size: 20px;">close</span>\r\n </button>\r\n </div>\r\n <div class="modalBody">\r\n Website sắp yêu cầu quyền <strong>Microphone</strong> và <strong>Camera</strong> để:\r\n <ul>\r\n <li>Gọi video trong phiên tư vấn.</li>\r\n <li>Kiểm tra thiết bị trước khi bắt đầu.</li>\r\n </ul>\r\n <div class="note">\r\n Sau khi bấm “Tiếp tục”, trình duyệt sẽ hiện thông báo hệ thống. Vui lòng chọn <strong>Allow</strong>.\r\n </div>\r\n </div>\r\n <div class="modalFoot">\r\n <button id="td-perm-cancel" class="btn">Hủy</button>\r\n <button id="td-perm-allow" class="btn primary">Tiếp tục</button>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<style>\r\n #td-permission-modal {\r\n --bg: var(--td-bg-light, #ffffff);\r\n --text: var(--td-text-main, #0d131c);\r\n --muted: var(--td-text-sub, #49699c);\r\n --border: var(--td-border, #e7ecf4);\r\n --primary: var(--td-primary, #0e65f1);\r\n --primary-hover: var(--td-primary-hover, #0b50c0);\r\n --shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);\r\n --radius: 12px;\r\n font-family: var(--font-family, system-ui, -apple-system, sans-serif);\r\n z-index: 2147483647;\r\n }\r\n\r\n /* Modal Overlay */\r\n .modalOverlay {\r\n position: fixed;\r\n inset: 0;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n background: rgba(0, 0, 0, .5);\r\n backdrop-filter: blur(2px);\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n padding: 18px;\r\n z-index: 2147483647;\r\n }\r\n\r\n .modal {\r\n width: min(500px, 94vw);\r\n border-radius: var(--radius);\r\n border: 1px solid var(--border);\r\n background: var(--bg);\r\n box-shadow: var(--shadow);\r\n overflow: hidden;\r\n color: var(--text);\r\n font-size: 14px;\r\n line-height: 1.5;\r\n }\r\n\r\n .modalHead {\r\n padding: 16px 20px;\r\n border-bottom: 1px solid var(--border);\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: 10px;\r\n background: #f8f9fc;\r\n }\r\n\r\n .modalHead h2 {\r\n margin: 0;\r\n font-size: 16px;\r\n font-weight: 700;\r\n color: var(--text);\r\n }\r\n\r\n .modalBody {\r\n padding: 20px;\r\n color: var(--text);\r\n }\r\n\r\n .modalBody ul {\r\n margin: 12px 0 16px 0;\r\n padding-left: 20px;\r\n color: var(--muted);\r\n }\r\n\r\n .modalBody li {\r\n margin-bottom: 6px;\r\n }\r\n\r\n .modalFoot {\r\n padding: 16px 20px;\r\n display: flex;\r\n gap: 12px;\r\n justify-content: flex-end;\r\n border-top: 1px solid var(--border);\r\n background: #f8f9fc;\r\n }\r\n\r\n .note {\r\n margin-top: 12px;\r\n color: var(--muted);\r\n font-size: 13px;\r\n background: rgba(14, 101, 241, 0.05);\r\n padding: 10px;\r\n border-radius: 8px;\r\n border: 1px dashed var(--primary);\r\n }\r\n\r\n .note strong {\r\n color: var(--primary);\r\n }\r\n\r\n /* Buttons */\r\n .btn {\r\n appearance: none;\r\n border: 1px solid var(--border);\r\n background: white;\r\n color: var(--text);\r\n padding: 10px 16px;\r\n border-radius: 8px;\r\n cursor: pointer;\r\n transition: all 0.2s;\r\n font-weight: 600;\r\n font-family: inherit;\r\n font-size: 14px;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n }\r\n\r\n .btn:hover {\r\n background: #f1f5f9;\r\n border-color: #cbd5e1;\r\n }\r\n\r\n .btn-icon {\r\n padding: 4px;\r\n border: none;\r\n background: transparent;\r\n color: #64748b;\r\n border-radius: 50%;\r\n }\r\n\r\n .btn-icon:hover {\r\n background: rgba(0, 0, 0, 0.05);\r\n border-color: transparent;\r\n color: var(--text);\r\n }\r\n\r\n .btn.primary {\r\n background: var(--primary);\r\n border-color: var(--primary);\r\n color: white;\r\n }\r\n\r\n .btn.primary:hover {\r\n background: var(--primary-hover);\r\n border-color: var(--primary-hover);\r\n }\r\n</style>';
|
|
457
458
|
const render = (template, data) => {
|
|
458
459
|
var _a, _b, _c;
|
|
459
460
|
let output = template;
|
|
@@ -471,6 +472,7 @@ const getConsultationHtml = (data) => render(consultationHtml, data);
|
|
|
471
472
|
const getPatientFormHtml = (data) => render(patientFormHtml, data);
|
|
472
473
|
const getPostConsultationHtml = (data) => render(postConsultationHtml, data);
|
|
473
474
|
const getInlineTriggerHtml = (data) => render(inlineTriggerHtml, data);
|
|
475
|
+
const getPermissionModalHtml = (data) => render(permissionModalHtml, data);
|
|
474
476
|
const PACKET_TYPES = /* @__PURE__ */ Object.create(null);
|
|
475
477
|
PACKET_TYPES["open"] = "0";
|
|
476
478
|
PACKET_TYPES["close"] = "1";
|
|
@@ -28017,6 +28019,41 @@ class LiveKitService {
|
|
|
28017
28019
|
constructor() {
|
|
28018
28020
|
this.room = null;
|
|
28019
28021
|
this.wrapper = null;
|
|
28022
|
+
this.preAcquiredTracks = [];
|
|
28023
|
+
}
|
|
28024
|
+
/**
|
|
28025
|
+
* Request Camera & Mic permissions early
|
|
28026
|
+
*/
|
|
28027
|
+
async requestPermissions() {
|
|
28028
|
+
try {
|
|
28029
|
+
console.log("[LiveKit] Requesting early permissions...");
|
|
28030
|
+
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
|
|
28031
|
+
this.preAcquiredTracks = stream.getTracks();
|
|
28032
|
+
console.log("[LiveKit] Permissions granted, tracks cached:", this.preAcquiredTracks.length);
|
|
28033
|
+
return true;
|
|
28034
|
+
} catch (error) {
|
|
28035
|
+
console.error("[LiveKit] Permission request failed:", error);
|
|
28036
|
+
console.log(`Không thể xin quyền: ${error.name} - ${error.message}`);
|
|
28037
|
+
return false;
|
|
28038
|
+
}
|
|
28039
|
+
}
|
|
28040
|
+
/**
|
|
28041
|
+
* Check if permissions are already granted
|
|
28042
|
+
* @returns {Promise<boolean>}
|
|
28043
|
+
*/
|
|
28044
|
+
async checkPermissions() {
|
|
28045
|
+
if (!navigator.permissions || !navigator.permissions.query) {
|
|
28046
|
+
return false;
|
|
28047
|
+
}
|
|
28048
|
+
try {
|
|
28049
|
+
const cam = await navigator.permissions.query({ name: "camera" });
|
|
28050
|
+
const mic = await navigator.permissions.query({ name: "microphone" });
|
|
28051
|
+
console.log("[LiveKit] Permissions check:", { cam: cam.state, mic: mic.state });
|
|
28052
|
+
return cam.state === "granted" && mic.state === "granted";
|
|
28053
|
+
} catch (error) {
|
|
28054
|
+
console.warn("[LiveKit] Permission check not supported or failed:", error);
|
|
28055
|
+
return false;
|
|
28056
|
+
}
|
|
28020
28057
|
}
|
|
28021
28058
|
/**
|
|
28022
28059
|
* Connect to LiveKit Room
|
|
@@ -28064,8 +28101,23 @@ class LiveKitService {
|
|
|
28064
28101
|
async publishLocalTracks() {
|
|
28065
28102
|
const localContainer = this.wrapper.querySelector(".td-pip");
|
|
28066
28103
|
try {
|
|
28067
|
-
|
|
28068
|
-
|
|
28104
|
+
let videoPub = null;
|
|
28105
|
+
if (this.preAcquiredTracks && this.preAcquiredTracks.length > 0) {
|
|
28106
|
+
console.log("[LiveKit] Publishing pre-acquired tracks...");
|
|
28107
|
+
const videoTrack = this.preAcquiredTracks.find((t) => t.kind === "video");
|
|
28108
|
+
const audioTrack = this.preAcquiredTracks.find((t) => t.kind === "audio");
|
|
28109
|
+
if (videoTrack) {
|
|
28110
|
+
videoPub = await this.room.localParticipant.publishTrack(videoTrack, { source: "camera" });
|
|
28111
|
+
}
|
|
28112
|
+
if (audioTrack) {
|
|
28113
|
+
await this.room.localParticipant.publishTrack(audioTrack, { source: "microphone" });
|
|
28114
|
+
}
|
|
28115
|
+
this.preAcquiredTracks = [];
|
|
28116
|
+
} else {
|
|
28117
|
+
await this.room.localParticipant.setCameraEnabled(true);
|
|
28118
|
+
await this.room.localParticipant.setMicrophoneEnabled(true);
|
|
28119
|
+
videoPub = Array.from(this.room.localParticipant.videoTrackPublications.values()).find((p) => p.source === "camera");
|
|
28120
|
+
}
|
|
28069
28121
|
if (videoPub && videoPub.track && localContainer) {
|
|
28070
28122
|
localContainer.innerHTML = "";
|
|
28071
28123
|
localContainer.style.backgroundImage = "none";
|
|
@@ -28076,24 +28128,15 @@ class LiveKitService {
|
|
|
28076
28128
|
videoEl.style.transform = "scale(-1, 1)";
|
|
28077
28129
|
localContainer.appendChild(videoEl);
|
|
28078
28130
|
console.log("[LiveKit] Local video attached");
|
|
28131
|
+
} else {
|
|
28132
|
+
console.warn("[LiveKit] No Video Publication found to attach!");
|
|
28079
28133
|
}
|
|
28080
28134
|
} catch (e2) {
|
|
28081
|
-
console.error("[LiveKit] Failed to
|
|
28082
|
-
if (e2.name === "NotAllowedError") {
|
|
28083
|
-
alert("Không thể mở Camera. Hãy kiểm tra icon ổ khóa trên thanh địa chỉ hoặc cài đặt Quyền Riêng Tư (Privacy) của máy tính.");
|
|
28084
|
-
}
|
|
28085
|
-
}
|
|
28086
|
-
try {
|
|
28087
|
-
const devices = await Room.getLocalDevices("audioinput");
|
|
28088
|
-
console.log("[LiveKit] Audio Input Devices:", devices);
|
|
28089
|
-
if (devices.length === 0) {
|
|
28090
|
-
alert("Không tìm thấy Micro nào trên thiết bị của bạn!");
|
|
28091
|
-
}
|
|
28092
|
-
await this.room.localParticipant.setMicrophoneEnabled(true);
|
|
28093
|
-
} catch (e2) {
|
|
28094
|
-
console.error("[LiveKit] Failed to enable Microphone:", e2);
|
|
28135
|
+
console.error("[LiveKit] Failed to publish tracks:", e2);
|
|
28095
28136
|
if (e2.name === "NotAllowedError") {
|
|
28096
|
-
alert("Không thể mở
|
|
28137
|
+
alert("Không thể mở Camera/Microphone. Hãy kiểm tra icon ổ khóa trên thanh địa chỉ hoặc cài đặt quyền riêng tư.");
|
|
28138
|
+
} else {
|
|
28139
|
+
alert(`Lỗi Camera: ${e2.message}`);
|
|
28097
28140
|
}
|
|
28098
28141
|
}
|
|
28099
28142
|
}
|
|
@@ -28262,7 +28305,9 @@ class ClinicWidget {
|
|
|
28262
28305
|
}
|
|
28263
28306
|
if (consultBtn) {
|
|
28264
28307
|
consultBtn.addEventListener("click", () => {
|
|
28265
|
-
|
|
28308
|
+
consultBtn.addEventListener("click", () => {
|
|
28309
|
+
store.setState({ status: WidgetStates.PATIENT_FORM });
|
|
28310
|
+
});
|
|
28266
28311
|
});
|
|
28267
28312
|
}
|
|
28268
28313
|
} else if (status === WidgetStates.PATIENT_FORM) {
|
|
@@ -28274,16 +28319,16 @@ class ClinicWidget {
|
|
|
28274
28319
|
});
|
|
28275
28320
|
}
|
|
28276
28321
|
if (form) {
|
|
28277
|
-
form.addEventListener("submit", (e2) => {
|
|
28322
|
+
form.addEventListener("submit", async (e2) => {
|
|
28278
28323
|
e2.preventDefault();
|
|
28279
28324
|
const formData = new FormData(form);
|
|
28280
28325
|
const name = formData.get("name");
|
|
28281
28326
|
const phone = formData.get("phone");
|
|
28282
|
-
this.triggerCallback("onConsultationStarted", { name, phone });
|
|
28283
28327
|
store.setState({
|
|
28284
|
-
user: { name, phone }
|
|
28285
|
-
status: WidgetStates.CONSULTATION
|
|
28328
|
+
user: { name, phone }
|
|
28286
28329
|
});
|
|
28330
|
+
this.triggerCallback("onConsultationStarted", { name, phone });
|
|
28331
|
+
store.setState({ status: WidgetStates.CONSULTATION });
|
|
28287
28332
|
});
|
|
28288
28333
|
}
|
|
28289
28334
|
} else if (status === WidgetStates.CONSULTATION) {
|
|
@@ -28291,7 +28336,9 @@ class ClinicWidget {
|
|
|
28291
28336
|
const endBtn = this.shadowRoot.getElementById("td-consult-end");
|
|
28292
28337
|
if (endBtn) {
|
|
28293
28338
|
endBtn.addEventListener("click", () => {
|
|
28294
|
-
this.
|
|
28339
|
+
if (this.livekit) {
|
|
28340
|
+
this.livekit.disconnect();
|
|
28341
|
+
}
|
|
28295
28342
|
store.setState({ status: WidgetStates.COMPLETED });
|
|
28296
28343
|
});
|
|
28297
28344
|
}
|
|
@@ -28353,7 +28400,9 @@ class ClinicWidget {
|
|
|
28353
28400
|
const btn = el.querySelector("#td-inline-btn");
|
|
28354
28401
|
if (btn) {
|
|
28355
28402
|
btn.addEventListener("click", () => {
|
|
28356
|
-
|
|
28403
|
+
btn.addEventListener("click", () => {
|
|
28404
|
+
store.setState({ status: WidgetStates.PATIENT_FORM });
|
|
28405
|
+
});
|
|
28357
28406
|
});
|
|
28358
28407
|
}
|
|
28359
28408
|
} else {
|
|
@@ -28394,18 +28443,26 @@ class ClinicWidget {
|
|
|
28394
28443
|
store.setState({ doctorOnline: isOnline });
|
|
28395
28444
|
}
|
|
28396
28445
|
});
|
|
28397
|
-
this.socket.on("customer:token", (data) => {
|
|
28446
|
+
this.socket.on("customer:token", async (data) => {
|
|
28398
28447
|
console.log("Received token from server:", data);
|
|
28399
28448
|
if (data.token) {
|
|
28400
28449
|
if (!this.livekit) {
|
|
28401
28450
|
this.livekit = new LiveKitService();
|
|
28402
28451
|
}
|
|
28403
|
-
const
|
|
28404
|
-
|
|
28405
|
-
|
|
28406
|
-
|
|
28452
|
+
const connectLiveKit = () => {
|
|
28453
|
+
const modal = this.shadowRoot.querySelector(".td-consultation-modal");
|
|
28454
|
+
if (modal) {
|
|
28455
|
+
console.log("[Widget] LiveKit Params:", { url: CONFIG.LIVEKIT_URL, token: data.token });
|
|
28456
|
+
this.livekit.connect(CONFIG.LIVEKIT_URL, data.token, modal);
|
|
28457
|
+
} else {
|
|
28458
|
+
console.error("Consultation modal not found for video rendering");
|
|
28459
|
+
}
|
|
28460
|
+
};
|
|
28461
|
+
const granted = await this.livekit.checkPermissions();
|
|
28462
|
+
if (granted) {
|
|
28463
|
+
connectLiveKit();
|
|
28407
28464
|
} else {
|
|
28408
|
-
|
|
28465
|
+
this.showPermissionModal(connectLiveKit);
|
|
28409
28466
|
}
|
|
28410
28467
|
}
|
|
28411
28468
|
});
|
|
@@ -28454,6 +28511,39 @@ class ClinicWidget {
|
|
|
28454
28511
|
this.socket = null;
|
|
28455
28512
|
}
|
|
28456
28513
|
}
|
|
28514
|
+
showPermissionModal(onSuccessCallback) {
|
|
28515
|
+
if (this.shadowRoot.getElementById("td-permission-modal")) return;
|
|
28516
|
+
const state = store.getState();
|
|
28517
|
+
const div = document.createElement("div");
|
|
28518
|
+
div.innerHTML = getPermissionModalHtml(state);
|
|
28519
|
+
Array.from(div.children).forEach((child) => {
|
|
28520
|
+
this.shadowRoot.appendChild(child);
|
|
28521
|
+
});
|
|
28522
|
+
const modalEl = this.shadowRoot.getElementById("td-permission-modal");
|
|
28523
|
+
const closeBtn = modalEl.querySelector("#td-perm-close");
|
|
28524
|
+
const cancelBtn = modalEl.querySelector("#td-perm-cancel");
|
|
28525
|
+
const allowBtn = modalEl.querySelector("#td-perm-allow");
|
|
28526
|
+
const closeModal = () => {
|
|
28527
|
+
modalEl.remove();
|
|
28528
|
+
};
|
|
28529
|
+
const onAllow = async () => {
|
|
28530
|
+
closeModal();
|
|
28531
|
+
if (!this.livekit) {
|
|
28532
|
+
this.livekit = new LiveKitService();
|
|
28533
|
+
}
|
|
28534
|
+
const ok = await this.livekit.requestPermissions();
|
|
28535
|
+
if (ok) {
|
|
28536
|
+
if (typeof onSuccessCallback === "function") {
|
|
28537
|
+
onSuccessCallback();
|
|
28538
|
+
}
|
|
28539
|
+
} else {
|
|
28540
|
+
console.warn("User denied permissions via browser prompt");
|
|
28541
|
+
}
|
|
28542
|
+
};
|
|
28543
|
+
if (closeBtn) closeBtn.addEventListener("click", closeModal);
|
|
28544
|
+
if (cancelBtn) cancelBtn.addEventListener("click", closeModal);
|
|
28545
|
+
if (allowBtn) allowBtn.addEventListener("click", onAllow);
|
|
28546
|
+
}
|
|
28457
28547
|
}
|
|
28458
28548
|
(function() {
|
|
28459
28549
|
const currentScript = document.querySelector("script[data-widget-id]");
|