clinic-connect-widget 1.0.4 → 1.0.5

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.
@@ -104,7 +104,7 @@ const CONFIG = {
104
104
  // LiveKit Server URL
105
105
  LIVEKIT_URL: "wss://livekit.longvan.vn"
106
106
  };
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}@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-overlay-wrapper{position:fixed;top:0;right:0;bottom:0;left:0;background:#0006;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:var(--z-index);display:flex;align-items:center;justify-content:center;animation:fadeInOpacity .3s ease-out}.td-modal-centered{background:var(--td-bg-light);width:90%;max-width:480px;border-radius:16px;box-shadow:0 25px 50px -12px #00000040;border:1px solid var(--td-border);display:flex;flex-direction:column;overflow:hidden;max-height:90vh;animation:scaleIn .3s cubic-bezier(.16,1,.3,1);position:relative}@keyframes fadeInOpacity{0%{opacity:0}to{opacity:1}}@keyframes scaleIn{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(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-avatar{width:56px;height:56px;border-radius:50%;background-size:cover;background-position:center;flex-shrink:0}.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}';
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-online-badge.td-offline{background:#f3f4f6;border-color:#e5e7eb}.td-online-badge.td-offline .td-badge-text{color:#6b7280}.td-online-badge.td-offline .td-pulse{background:#9ca3af;animation:none}.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}@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-overlay-wrapper{position:fixed;top:0;right:0;bottom:0;left:0;background:#0006;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);z-index:var(--z-index);display:flex;align-items:center;justify-content:center;animation:fadeInOpacity .3s ease-out}.td-modal-centered{background:var(--td-bg-light);width:90%;max-width:480px;border-radius:16px;box-shadow:0 25px 50px -12px #00000040;border:1px solid var(--td-border);display:flex;flex-direction:column;overflow:hidden;max-height:90vh;animation:scaleIn .3s cubic-bezier(.16,1,.3,1);position:relative}@keyframes fadeInOpacity{0%{opacity:0}to{opacity:1}}@keyframes scaleIn{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(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-avatar{width:56px;height:56px;border-radius:50%;background-size:cover;background-position:center;flex-shrink:0}.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
108
  const collapsedHtml = `<div class="td-widget-fab" id="td-widget-fab">\r
109
109
  <div class="td-fab-card">\r
110
110
  <button class="td-close-fab" id="td-fab-close">\r
@@ -112,7 +112,7 @@ const collapsedHtml = `<div class="td-widget-fab" id="td-widget-fab">\r
112
112
  </button>\r
113
113
  <div class="td-avatar-container">\r
114
114
  <div class="td-avatar" style="background-image: url('{{doctorAvatar}}');"></div>\r
115
- <div class="td-status-dot" style="background: #22c55e;"></div>\r
115
+ <div class="td-status-dot" style="background: {{statusDotColor}};"></div>\r
116
116
  </div>\r
117
117
  <div class="td-info">\r
118
118
  <div class="td-brand">\r
@@ -120,7 +120,7 @@ const collapsedHtml = `<div class="td-widget-fab" id="td-widget-fab">\r
120
120
  <span>Tư vấn ngay</span>\r
121
121
  </div>\r
122
122
  <h3 class="td-cta">Khám online ngay</h3>\r
123
- <p class="td-sub">Bác sĩ đang Online</p>\r
123
+ <p class="td-sub">{{statusText}}</p>\r
124
124
  </div>\r
125
125
  </div>\r
126
126
  </div>`;
@@ -148,9 +148,9 @@ const expandedHtml = `<div class="td-overlay-wrapper">\r
148
148
  <h1 class="td-doctor-name">{{doctorName}}</h1>\r
149
149
  <p class="td-doctor-specialty">{{doctorSpecialty}}</p>\r
150
150
  \r
151
- <div class="td-online-badge">\r
152
- <span class="td-pulse"></span>\r
153
- <span class="td-badge-text">Đang trực tuyến</span>\r
151
+ <div class="{{badgeClass}}">\r
152
+ <span class="td-pulse" style="{{pulseStyle}}"></span>\r
153
+ <span class="td-badge-text">{{badgeText}}</span>\r
154
154
  </div>\r
155
155
  </div>\r
156
156
  \r
@@ -170,13 +170,13 @@ const expandedHtml = `<div class="td-overlay-wrapper">\r
170
170
  </div>\r
171
171
  \r
172
172
  <div class="td-actions">\r
173
- <button class="td-btn td-btn-primary" id="td-btn-consult">\r
173
+ <button class="td-btn td-btn-primary" id="td-btn-consult" style="{{btnStyle}}">\r
174
174
  <span class="material-symbols-outlined" style="margin-right: 8px;">videocam</span>\r
175
175
  Bắt đầu tư vấn ngay\r
176
176
  </button>\r
177
- <button class="td-btn td-btn-outline" id="td-btn-schedule">\r
177
+ <!-- <button class="td-btn td-btn-outline" id="td-btn-schedule">\r
178
178
  Đặt lịch hẹn sau\r
179
- </button>\r
179
+ </button> -->\r
180
180
  </div>\r
181
181
  </div>\r
182
182
  \r
@@ -188,8 +188,110 @@ const expandedHtml = `<div class="td-overlay-wrapper">\r
188
188
  const consultationHtml = `<div class="td-consultation-modal">\r
189
189
  <!-- Left: Video Panel -->\r
190
190
  <div class="td-video-panel">\r
191
- <div class="td-main-video"\r
192
- style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuC54B68xwAiOWZFQNVMIVKNBTKwo-nPCTsEgkaekhx9PbGDRCwcy18cFeALhbiaeOyaIS88c9RZst2sRWUyKTeSif-HqEfgU5Aoht-mk6Tjtuts2M4XmoMaxNSAi7qjKoQRYx61I4MolGfjCkxRN0lW5rlSlQfMwY0T8K1440SLM9hW_4u0N1Vg6kQTcSUitouGRmWktBHr-yH5QTu78AC1IOZLd5-HyWw0NUb6uESPpNZoZHoIEKIjFhsGBnCRmzHCUP9Ex6mP0XvW');">\r
191
+ <div class="td-main-video">\r
192
+ <div id="td-video-placeholder" style="\r
193
+ position: absolute;\r
194
+ top: 0; left: 0; width: 100%; height: 100%;\r
195
+ background: radial-gradient(circle at center, #1f2937 0%, #111827 100%);\r
196
+ display: flex;\r
197
+ flex-direction: column;\r
198
+ align-items: center;\r
199
+ justify-content: center;\r
200
+ color: white;\r
201
+ z-index: 10;\r
202
+ transition: opacity 0.5s ease;\r
203
+ font-family: 'Inter', system-ui, sans-serif;\r
204
+ ">\r
205
+ <!-- Ripple Container -->\r
206
+ <div\r
207
+ style="position: relative; width: 80px; height: 80px; display: flex; align-items: center; justify-content: center; margin-bottom: 24px;">\r
208
+ <div class="td-ripple" style="animation-delay: 0s;"></div>\r
209
+ <div class="td-ripple" style="animation-delay: 0.5s;"></div>\r
210
+ <div class="td-ripple" style="animation-delay: 1s;"></div>\r
211
+ \r
212
+ <!-- Main Icon Circle with Glass Effect -->\r
213
+ <div style="\r
214
+ position: relative;\r
215
+ width: 60px; height: 60px;\r
216
+ background: rgba(255, 255, 255, 0.1);\r
217
+ backdrop-filter: blur(8px);\r
218
+ -webkit-backdrop-filter: blur(8px);\r
219
+ border: 1px solid rgba(255, 255, 255, 0.2);\r
220
+ border-radius: 50%;\r
221
+ display: flex; align-items: center; justify-content: center;\r
222
+ z-index: 2;\r
223
+ box-shadow: 0 4px 12px rgba(0,0,0,0.2);\r
224
+ ">\r
225
+ <span class="material-symbols-outlined"\r
226
+ style="font-size: 30px; color: #60a5fa; text-shadow: 0 0 10px rgba(96, 165, 250, 0.5);">videocam</span>\r
227
+ </div>\r
228
+ </div>\r
229
+ \r
230
+ <div style="text-align: center;">\r
231
+ <div\r
232
+ style="font-size: 16px; font-weight: 600; color: #f3f4f6; margin-bottom: 6px; letter-spacing: 0.5px;">\r
233
+ Đang kết nối</div>\r
234
+ <div\r
235
+ style="font-size: 13px; color: #9ca3af; display: flex; align-items: center; gap: 4px; justify-content: center;">\r
236
+ Vui lòng đợi trong giây lát\r
237
+ <span class="td-dots"></span>\r
238
+ </div>\r
239
+ </div>\r
240
+ \r
241
+ <style>\r
242
+ .td-ripple {\r
243
+ position: absolute;\r
244
+ border: 2px solid rgba(96, 165, 250, 0.6);\r
245
+ width: 100%;\r
246
+ height: 100%;\r
247
+ border-radius: 50%;\r
248
+ opacity: 0;\r
249
+ animation: td-ripple-anim 2s cubic-bezier(0, 0.2, 0.8, 1) infinite;\r
250
+ }\r
251
+ \r
252
+ @keyframes td-ripple-anim {\r
253
+ 0% {\r
254
+ transform: scale(0.8);\r
255
+ opacity: 0;\r
256
+ }\r
257
+ \r
258
+ 5% {\r
259
+ opacity: 0.5;\r
260
+ }\r
261
+ \r
262
+ 100% {\r
263
+ transform: scale(2.2);\r
264
+ opacity: 0;\r
265
+ }\r
266
+ }\r
267
+ \r
268
+ .td-dots::after {\r
269
+ content: '.';\r
270
+ animation: td-dots-anim 1.5s steps(4, end) infinite;\r
271
+ }\r
272
+ \r
273
+ @keyframes td-dots-anim {\r
274
+ \r
275
+ 0%,\r
276
+ 20% {\r
277
+ content: '.';\r
278
+ }\r
279
+ \r
280
+ 40% {\r
281
+ content: '..';\r
282
+ }\r
283
+ \r
284
+ 60% {\r
285
+ content: '...';\r
286
+ }\r
287
+ \r
288
+ 80%,\r
289
+ 100% {\r
290
+ content: '';\r
291
+ }\r
292
+ }\r
293
+ </style>\r
294
+ </div>\r
193
295
  </div>\r
194
296
  <div class="td-overlay"></div>\r
195
297
  \r
@@ -221,9 +323,6 @@ const consultationHtml = `<div class="td-consultation-modal">\r
221
323
  <span class="material-symbols-outlined">call_end</span>\r
222
324
  Kết thúc\r
223
325
  </button>\r
224
- <button class="td-ctrl-btn">\r
225
- <span class="material-symbols-outlined">settings</span>\r
226
- </button>\r
227
326
  </div>\r
228
327
  </div>\r
229
328
  \r
@@ -248,7 +347,7 @@ const postConsultationHtml = `<div class="td-overlay-wrapper">\r
248
347
  <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
249
348
  </div>\r
250
349
  \r
251
- <div class="td-summary-card">\r
350
+ <!-- <div class="td-summary-card">\r
252
351
  <div class="td-summary-avatar" style="background-image: url('{{doctorAvatar}}');"></div>\r
253
352
  <div>\r
254
353
  <div style="font-weight: 700;">{{doctorName}}</div>\r
@@ -298,7 +397,7 @@ const postConsultationHtml = `<div class="td-overlay-wrapper">\r
298
397
  <span class="material-symbols-outlined" style="margin-right: 8px;">calendar_month</span>\r
299
398
  Đặt lịch tái khám\r
300
399
  </button>\r
301
- </div>\r
400
+ </div> -->\r
302
401
  </div>\r
303
402
  </div>\r
304
403
  </div>\r
@@ -382,8 +481,8 @@ const inlineTriggerHtml = `<style>\r
382
481
  <!-- Bottom: Status & Action -->\r
383
482
  <div class="td-inline-footer">\r
384
483
  <div class="td-inline-status">\r
385
- <span class="td-inline-pulse"></span>\r
386
- <span class="td-inline-text">Bác sĩ đang Online</span>\r
484
+ <span class="td-inline-pulse" style="{{pulseStyle}}"></span>\r
485
+ <span class="td-inline-text" style="{{inlineTextStyle}}">{{statusText}}</span>\r
387
486
  </div>\r
388
487
  \r
389
488
  <button id="td-inline-btn" type="button" style="\r
@@ -401,9 +500,9 @@ const inlineTriggerHtml = `<style>\r
401
500
  white-space: nowrap;\r
402
501
  transition: all 0.2s;\r
403
502
  box-shadow: 0 2px 5px rgba(0,102,255,0.2);\r
503
+ {{inlineBtnStyle}}\r
404
504
  " onmouseover="this.style.background='#0052cc'" onmouseout="this.style.background='#0066ff'">\r
405
- <span class="material-symbols-outlined" style="font-size: 18px;">videocam</span>\r
406
- Tư vấn ngay\r
505
+ {{inlineBtnContent}}\r
407
506
  </button>\r
408
507
  </div>\r
409
508
  </div>`;
@@ -414,9 +513,28 @@ const render = (template, data) => {
414
513
  const doctorName = ((_a = data.doctor) == null ? void 0 : _a.name) || "Bác sĩ Hà Ngọc Mạnh";
415
514
  const doctorSpecialty = ((_b = data.doctor) == null ? void 0 : _b.specialty) || "Nam học - Hiếm muộn";
416
515
  const doctorAvatar = ((_c = data.doctor) == null ? void 0 : _c.avatarUrl) || "";
516
+ const isOnline = !!data.doctorOnline;
517
+ const statusDotColor = isOnline ? "#22c55e" : "#9ca3af";
518
+ const statusText = isOnline ? "Bác sĩ đang Online" : "Bác sĩ Offline";
519
+ const badgeClass = isOnline ? "td-online-badge" : "td-online-badge td-offline";
520
+ const badgeText = isOnline ? "Đang trực tuyến" : "Đang ngoại tuyến";
521
+ const btnStyle = isOnline ? "" : "opacity: 0.5; cursor: not-allowed; background: #9ca3af;";
522
+ const pulseStyle = isOnline ? "" : "background: #9ca3af; animation: none;";
523
+ const inlineTextStyle = isOnline ? "" : "color: #6b7280;";
524
+ const inlineBtnStyle = isOnline ? "" : "opacity: 0.5; cursor: not-allowed; background: #9ca3af;";
525
+ const inlineBtnHtml = isOnline ? '<span class="material-symbols-outlined" style="font-size: 18px;">videocam</span> Tư vấn ngay' : '<span class="material-symbols-outlined" style="font-size: 18px;">videocam_off</span> Bác sĩ Offline';
417
526
  output = output.replace(/{{doctorName}}/g, doctorName);
418
527
  output = output.replace(/{{doctorSpecialty}}/g, doctorSpecialty);
419
528
  output = output.replace(/{{doctorAvatar}}/g, doctorAvatar);
529
+ output = output.replace(/{{statusDotColor}}/g, statusDotColor);
530
+ output = output.replace(/{{statusText}}/g, statusText);
531
+ output = output.replace(/{{badgeClass}}/g, badgeClass);
532
+ output = output.replace(/{{badgeText}}/g, badgeText);
533
+ output = output.replace(/{{btnStyle}}/g, btnStyle);
534
+ output = output.replace(/{{pulseStyle}}/g, pulseStyle);
535
+ output = output.replace(/{{inlineTextStyle}}/g, inlineTextStyle);
536
+ output = output.replace(/{{inlineBtnStyle}}/g, inlineBtnStyle);
537
+ output = output.replace(/{{inlineBtnContent}}/g, inlineBtnHtml);
420
538
  return output;
421
539
  };
422
540
  const getCollapsedHtml = (data) => render(collapsedHtml, data);
@@ -28099,8 +28217,15 @@ class LiveKitService {
28099
28217
  const remoteContainer = this.wrapper.querySelector(".td-main-video");
28100
28218
  console.log("[LiveKit] Looking for remote container (.td-main-video):", remoteContainer);
28101
28219
  if (remoteContainer) {
28220
+ const placeholder = remoteContainer.querySelector("#td-video-placeholder");
28221
+ if (placeholder) {
28222
+ placeholder.style.opacity = "0";
28223
+ setTimeout(() => placeholder.remove(), 500);
28224
+ } else {
28225
+ const existingVideos = remoteContainer.querySelectorAll("video");
28226
+ existingVideos.forEach((v) => v.remove());
28227
+ }
28102
28228
  remoteContainer.style.backgroundImage = "none";
28103
- remoteContainer.innerHTML = "";
28104
28229
  const videoEl = track.attach();
28105
28230
  videoEl.style.width = "100%";
28106
28231
  videoEl.style.height = "100%";
@@ -28215,6 +28340,7 @@ class ClinicWidget {
28215
28340
  // Default open state
28216
28341
  });
28217
28342
  this.checkInlineTrigger();
28343
+ this.recheckDoctorStatus();
28218
28344
  } else {
28219
28345
  store.setState({ status: WidgetStates.IDLE, error: "Init failed" });
28220
28346
  }
@@ -28249,8 +28375,9 @@ class ClinicWidget {
28249
28375
  this.shadowRoot.appendChild(styleTag);
28250
28376
  }
28251
28377
  handleStateChange({ prev, current }) {
28252
- if (prev.status !== current.status) {
28378
+ if (prev.status !== current.status || prev.doctorOnline !== current.doctorOnline) {
28253
28379
  this.render(current.status);
28380
+ this.checkInlineTrigger();
28254
28381
  }
28255
28382
  }
28256
28383
  render(status) {
@@ -28286,6 +28413,7 @@ class ClinicWidget {
28286
28413
  this.bindEvents(status);
28287
28414
  }
28288
28415
  bindEvents(status) {
28416
+ const state = store.getState();
28289
28417
  if (status === WidgetStates.COLLAPSED) {
28290
28418
  const fab = this.shadowRoot.getElementById("td-widget-fab");
28291
28419
  const closeFab = this.shadowRoot.getElementById("td-fab-close");
@@ -28318,10 +28446,17 @@ class ClinicWidget {
28318
28446
  });
28319
28447
  }
28320
28448
  if (consultBtn) {
28449
+ if (!state.doctorOnline) {
28450
+ consultBtn.style.opacity = "0.5";
28451
+ consultBtn.style.cursor = "not-allowed";
28452
+ consultBtn.title = "Bác sĩ hiện đang Offline";
28453
+ }
28321
28454
  consultBtn.addEventListener("click", () => {
28322
- consultBtn.addEventListener("click", () => {
28323
- store.setState({ status: WidgetStates.PATIENT_FORM });
28324
- });
28455
+ if (!state.doctorOnline) {
28456
+ console.log("Bác Offline, không thể bắt đầu tư vấn.");
28457
+ return;
28458
+ }
28459
+ store.setState({ status: WidgetStates.PATIENT_FORM });
28325
28460
  });
28326
28461
  }
28327
28462
  } else if (status === WidgetStates.PATIENT_FORM) {
@@ -28459,10 +28594,18 @@ class ClinicWidget {
28459
28594
  el.innerHTML = getInlineTriggerHtml(state);
28460
28595
  const btn = el.querySelector("#td-inline-btn");
28461
28596
  if (btn) {
28597
+ if (!state.doctorOnline) {
28598
+ btn.style.opacity = "0.6";
28599
+ btn.style.cursor = "not-allowed";
28600
+ btn.style.background = "#9ca3af";
28601
+ btn.innerHTML = '<span class="material-symbols-outlined" style="font-size: 18px;">videocam_off</span> Bác sĩ Offline';
28602
+ }
28462
28603
  btn.addEventListener("click", () => {
28463
- btn.addEventListener("click", () => {
28464
- store.setState({ status: WidgetStates.PATIENT_FORM });
28465
- });
28604
+ if (!state.doctorOnline) {
28605
+ console.log("Bác Offline, không thể bắt đầu tư vấn.");
28606
+ return;
28607
+ }
28608
+ store.setState({ status: WidgetStates.PATIENT_FORM });
28466
28609
  });
28467
28610
  }
28468
28611
  } else {
@@ -28470,15 +28613,21 @@ class ClinicWidget {
28470
28613
  }
28471
28614
  }
28472
28615
  }
28616
+ /**
28617
+ * Helper to generate random ID for guests
28618
+ */
28619
+ generateGuestId() {
28620
+ return "guest_" + Math.random().toString(36).substr(2, 9);
28621
+ }
28473
28622
  startSocket() {
28474
28623
  const state = store.getState();
28475
28624
  const url2 = state.config.socketUrl || CONFIG.SOCKET_URL;
28476
- const { user, doctor } = state;
28625
+ const { user } = state;
28477
28626
  if (this.socket) return;
28478
28627
  this.socket = new WebSocketService(url2);
28479
28628
  this.socket.connect(() => {
28480
28629
  console.log("Socket connected, registering...");
28481
- const userId = "20.183299.4158";
28630
+ const userId = (user == null ? void 0 : user.id) || this.generateGuestId();
28482
28631
  this.socket.emit("register", {
28483
28632
  userId,
28484
28633
  userName: (user == null ? void 0 : user.name) || "Guest",
@@ -28486,25 +28635,9 @@ class ClinicWidget {
28486
28635
  });
28487
28636
  });
28488
28637
  this.socket.on("users-online", (users) => {
28489
- var _a;
28490
- console.log("[Widget] Received users-online event:", users);
28491
- const onlineUsers = Array.isArray(users) && Array.isArray(users[0]) ? users[0] : users;
28492
- const currentDoctorId = (_a = store.getState().doctor) == null ? void 0 : _a.id;
28493
- console.log("[Widget] Current Doctor ID (Config):", currentDoctorId);
28494
- if (currentDoctorId && Array.isArray(onlineUsers)) {
28495
- const doctorEntry = onlineUsers.find(
28496
- (u) => {
28497
- var _a2;
28498
- return String(u.userId) === String(currentDoctorId) && ((_a2 = u.role) == null ? void 0 : _a2.toUpperCase()) === "DOCTOR";
28499
- }
28500
- );
28501
- const isOnline = !!doctorEntry;
28502
- console.log("[Widget] Doctor Status:", isOnline ? "Online" : "Offline", "Doctor Entry:", doctorEntry);
28503
- store.setState({ doctorOnline: isOnline });
28504
- }
28638
+ this.handleOnlineUsers(users);
28505
28639
  });
28506
28640
  this.socket.on("customer:token", async (data) => {
28507
- console.log("Received token from server:", data);
28508
28641
  if (data.token) {
28509
28642
  if (!this.livekit) {
28510
28643
  this.livekit = new LiveKitService();
@@ -28514,6 +28647,35 @@ class ClinicWidget {
28514
28647
  }
28515
28648
  });
28516
28649
  }
28650
+ /**
28651
+ * Handle users-online event from socket
28652
+ * @param {Array} users
28653
+ */
28654
+ handleOnlineUsers(users) {
28655
+ const onlineUsers = Array.isArray(users) && Array.isArray(users[0]) ? users[0] : users;
28656
+ store.setState({ onlineUsers });
28657
+ this.recheckDoctorStatus(onlineUsers);
28658
+ }
28659
+ /**
28660
+ * Re-evaluates if the configured doctor is online based on current state
28661
+ * @param {Array} [usersList] - Optional optimization to pass list if already known
28662
+ */
28663
+ recheckDoctorStatus(usersList) {
28664
+ var _a;
28665
+ const state = store.getState();
28666
+ const currentDoctorId = (_a = state.doctor) == null ? void 0 : _a.id;
28667
+ const onlineUsers = usersList || state.onlineUsers || [];
28668
+ if (!currentDoctorId) return;
28669
+ const doctorEntry = onlineUsers.find((u) => {
28670
+ const isIdMatch = String(u.userId) === String(currentDoctorId);
28671
+ u.role ? u.role.toUpperCase() === "DOCTOR" : true;
28672
+ return isIdMatch && (u.role ? u.role.toUpperCase() === "DOCTOR" : true);
28673
+ });
28674
+ const isOnline = !!doctorEntry;
28675
+ if (state.doctorOnline !== isOnline) {
28676
+ store.setState({ doctorOnline: isOnline });
28677
+ }
28678
+ }
28517
28679
  async attemptVideoConnection() {
28518
28680
  if (!this.videoToken || !this.livekit) {
28519
28681
  return;
@@ -28543,6 +28705,7 @@ class ClinicWidget {
28543
28705
  }
28544
28706
  }
28545
28707
  joinVideoCall() {
28708
+ var _a;
28546
28709
  const state = store.getState();
28547
28710
  const { user, doctor } = state;
28548
28711
  const userId = "20.183299.4158";
@@ -28558,8 +28721,7 @@ class ClinicWidget {
28558
28721
  participantId: userId,
28559
28722
  participantName: user.name,
28560
28723
  doctorId: doctor.id,
28561
- productId: "147365",
28562
- // Hardcoded as requested
28724
+ productId: state.productId || ((_a = state.config) == null ? void 0 : _a.productId),
28563
28725
  type: "video"
28564
28726
  };
28565
28727
  console.log("[Widget] Emitting customer:join with payload:", joinPayload);
@@ -28628,8 +28790,8 @@ class ClinicWidget {
28628
28790
  const locale = currentScript ? currentScript.getAttribute("data-locale") || "vi" : "vi";
28629
28791
  const storeId = currentScript ? currentScript.getAttribute("data-store-id") || "longvan-store" : "longvan-store";
28630
28792
  const orgId = currentScript ? currentScript.getAttribute("data-org-id") || "longvan-org" : "longvan-org";
28631
- const doctorId = currentScript ? currentScript.getAttribute("data-doctor-id") || "20.184041.6561" : "20.184041.6561";
28632
- const productId = currentScript ? currentScript.getAttribute("data-product-id") || "product-1" : "product-1";
28793
+ const doctorId = currentScript ? currentScript.getAttribute("data-doctor-id") : null;
28794
+ const productId = currentScript ? currentScript.getAttribute("data-product-id") : null;
28633
28795
  const config = {
28634
28796
  widgetId,
28635
28797
  triggerElementId,