{"id":7887,"date":"2025-11-13T10:23:10","date_gmt":"2025-11-13T10:23:10","guid":{"rendered":"https:\/\/aiggroup.com.vn\/?page_id=7887"},"modified":"2025-11-13T10:25:32","modified_gmt":"2025-11-13T10:25:32","slug":"tro-ly-hanh-chinh-ao-24-7","status":"publish","type":"page","link":"https:\/\/aiggroup.com.vn\/en\/tro-ly-hanh-chinh-ao-24-7\/","title":{"rendered":"Tr\u1ee3 l\u00fd H\u00e0nh ch\u00ednh \u1ea2o 24\/7"},"content":{"rendered":"<section data-bb-version=\"5.2.6\" id=\"bt_bb_section69d4e3c390c1c\" class=\"bt_bb_section bt_bb_layout_boxed_1400 bt_bb_top_spacing_large\"  data-bt-override-class=\"{&quot;bt_bb_top_spacing_&quot;:{&quot;current_class&quot;:&quot;bt_bb_top_spacing_large&quot;,&quot;xxl&quot;:&quot;large&quot;}}\"><div class=\"bt_bb_port\"><div class=\"bt_bb_cell\"><div class=\"bt_bb_cell_inner\"><div class=\"bt_bb_row\"  data-bt-override-class=\"{}\"><div class=\"bt_bb_row_holder\" ><div data-bb-version=\"5.2.6\"  class=\"bt_bb_column col-xxl-12 col-xl-12 col-xs-12 col-sm-12 col-md-12 col-lg-12 bt_bb_vertical_align_top bt_bb_align_left bt_bb_padding_normal\" style=\"; --column-width:12;\" data-width=\"12\" data-bt-override-class=\"{}\"><div class=\"bt_bb_column_content\"><div class=\"bt_bb_column_content_inner\"><div class=\"bt_bb_shortcode\">    <style>\r\n      .edbb-wrap{font-family:system-ui,Segoe UI,Roboto,Arial,sans-serif;max-width:960px;margin:2rem auto}\r\n      .edbb-card{border:1px solid #e5e7eb;border-radius:16px;overflow:hidden;box-shadow:0 10px 30px rgba(15,23,42,.18);background:#0b0b0f;color:#f9fafb}\r\n      .edbb-header{padding:16px 20px;border-bottom:1px solid rgba(148,163,184,.4);display:flex;align-items:center;justify-content:space-between;background:#050816}\r\n      .edbb-header-left{display:flex;align-items:center;gap:12px}\r\n      .edbb-logo{width:40px;height:40px;border-radius:999px;background:#111827;display:flex;align-items:center;justify-content:center}\r\n      .edbb-title{font-size:18px;font-weight:700}\r\n      .edbb-sub{font-size:13px;color:#fbbf24;font-weight:600}\r\n      .edbb-body{display:flex;flex-direction:column;}\r\n      .edbb-tabs{display:flex;gap:8px;padding:10px 14px;border-bottom:1px solid rgba(148,163,184,.4);background:linear-gradient(to right,#020617,#020617,#0b1120)}\r\n      .edbb-tab{padding:8px 12px;border-radius:999px;font-size:13px;border:0;background:#111827;color:#e5e7eb;cursor:pointer;display:flex;align-items:center;gap:6px}\r\n      .edbb-tab-active{background:#fbbf24;color:#020617;font-weight:700;box-shadow:0 8px 20px rgba(251,191,36,.45)}\r\n      .edbb-main{flex:1;display:flex;flex-direction:column;gap:12px;padding:10px 14px}\r\n      .edbb-chat-layout{flex:1;display:flex;flex-direction:row;gap:12px}\r\n      .edbb-chat-col{flex:2;display:flex;flex-direction:column;min-width:0}\r\n      .edbb-side-col{flex:1;min-width:0;display:flex;flex-direction:column;gap:10px}\r\n      .edbb-log{flex:1;max-height:60vh;overflow-y:auto;padding:8px 4px}\r\n      .edbb-msg-row{display:flex;margin-bottom:10px;gap:8px}\r\n      .edbb-msg-user{justify-content:flex-end}\r\n      .edbb-avatar{width:32px;height:32px;border-radius:999px;display:flex;align-items:center;justify-content:center;background:#111827;font-size:14px}\r\n      .edbb-avatar-bot{color:#fbbf24}\r\n      .edbb-bubble{max-width:100%;padding:8px 12px;border-radius:14px;font-size:14px;line-height:1.5;white-space:pre-wrap;word-wrap:break-word}\r\n      .edbb-bubble-user{background:#2563eb;color:#eff6ff;border-bottom-right-radius:4px}\r\n      .edbb-bubble-bot{background:#111827;color:#e5e7eb;border-bottom-left-radius:4px;border:1px solid rgba(148,163,184,.4)}\r\n      .edbb-input-wrap{border-top:1px solid rgba(148,163,184,.4);padding:10px 14px;background:#020617}\r\n      .edbb-input-row{display:flex;gap:8px}\r\n      .edbb-input{flex:1;min-height:46px;max-height:90px;padding:10px 12px;border-radius:12px;border:1px solid rgba(148,163,184,.7);background:#020617;color:#e5e7eb;font-size:14px;resize:vertical}\r\n      .edbb-input:focus{outline:none;border-color:#fbbf24;box-shadow:0 0 0 1px #fbbf24}\r\n      .edbb-btn{padding:10px 14px;border-radius:999px;border:0;background:#fbbf24;color:#111827;font-size:14px;font-weight:600;cursor:pointer;display:flex;align-items:center;gap:6px;white-space:nowrap}\r\n      .edbb-btn:disabled{opacity:.5;cursor:not-allowed}\r\n      .edbb-suggestions{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}\r\n      .edbb-suggestion-btn{border-radius:999px;border:1px solid rgba(148,163,184,.6);background:transparent;color:#e5e7eb;font-size:12px;padding:4px 8px;cursor:pointer}\r\n      .edbb-suggestion-btn:hover{background:#111827}\r\n      .edbb-panel{border-radius:12px;background:#020617;border:1px solid rgba(148,163,184,.35);padding:10px 12px;font-size:13px}\r\n      .edbb-panel-title{font-weight:600;margin-bottom:6px;color:#e5e7eb}\r\n      .edbb-panel-sub{font-size:12px;color:#9ca3af}\r\n      .edbb-flow-step{display:flex;align-items:center;gap:8px;margin-bottom:8px}\r\n      .edbb-flow-badge{width:22px;height:22px;border-radius:999px;background:#111827;color:#fbbf24;font-size:11px;display:flex;align-items:center;justify-content:center}\r\n      .edbb-flow-label{flex:1;font-size:13px}\r\n      .edbb-map-link{display:inline-flex;align-items:center;gap:6px;padding:6px 10px;border-radius:999px;font-size:12px;border:1px solid rgba(148,163,184,.6);color:#e5e7eb;text-decoration:none}\r\n      .edbb-map-link:hover{background:#111827}\r\n      .edbb-news-card{border-radius:12px;background:#020617;border:1px solid rgba(148,163,184,.35);padding:10px 12px;font-size:13px;margin-bottom:8px}\r\n      .edbb-news-title{font-weight:600;margin-bottom:4px}\r\n      .edbb-news-date{font-size:11px;color:#9ca3af;margin-bottom:6px}\r\n      .edbb-mode-note{font-size:12px;color:#9ca3af;margin-top:4px}\r\n        .edbb-voice-wrap{\r\n        padding:16px 4px;\r\n        display:flex;\r\n        flex-direction:column;\r\n        align-items:center;\r\n        gap:16px;\r\n        text-align:center;\r\n      }\r\n      .edbb-voice-status{\r\n        font-size:14px;\r\n        color:#e5e7eb;\r\n      }\r\n      .edbb-voice-btn{\r\n        margin-top:4px;\r\n        width:140px;\r\n        height:140px;\r\n        border-radius:999px;\r\n        border:0;\r\n        background:#fbbf24;\r\n        color:#111827;\r\n        display:flex;\r\n        flex-direction:column;\r\n        align-items:center;\r\n        justify-content:center;\r\n        gap:6px;\r\n        font-weight:600;\r\n        font-size:14px;\r\n        cursor:pointer;\r\n        box-shadow:0 10px 30px rgba(251,191,36,.45);\r\n        transition:all .15s ease-out;\r\n      }\r\n      .edbb-voice-btn .edbb-voice-icon{\r\n        font-size:32px;\r\n      }\r\n      .edbb-voice-btn-active{\r\n        background:#f97316;\r\n        box-shadow:0 0 0 6px rgba(248,250,252,.12);\r\n      }\r\n      .edbb-voice-log{\r\n        max-width:600px;\r\n        width:100%;\r\n        text-align:left;\r\n        border-radius:12px;\r\n        border:1px solid rgba(148,163,184,.35);\r\n        background:#020617;\r\n        padding:10px 12px;\r\n        font-size:13px;\r\n        min-height:60px;\r\n      }\r\n\r\n      @media (max-width: 900px){\r\n        .edbb-wrap{margin:1rem}\r\n        .edbb-body{height:80vh}\r\n        .edbb-main{flex-direction:column}\r\n        .edbb-side-col{flex-direction:column}\r\n      }\r\n    <\/style>\r\n\r\n    <div class=\"edbb-wrap\">\r\n      <div class=\"edbb-card\">\r\n        <div class=\"edbb-header\">\r\n          <div class=\"edbb-header-left\">\r\n            <div class=\"edbb-logo\">\r\n              <span style=\"color:#fbbf24;font-size:18px\">\ud83c\udfdb\ufe0f<\/span>\r\n            <\/div>\r\n            <div>\r\n              <div class=\"edbb-title\">Tr\u1ee3 l\u00fd H\u00e0nh ch\u00ednh \u1ea2o 24\/7<\/div>\r\n              <div class=\"edbb-sub\">UBND Ph\u01b0\u1eddng \u0110i\u1ec7n B\u00e0n B\u1eafc<\/div>\r\n            <\/div>\r\n          <\/div>\r\n        <\/div>\r\n\r\n        <div class=\"edbb-body\">\r\n          <div class=\"edbb-tabs\">\r\n            <button class=\"edbb-tab edbb-tab-active\" data-mode=\"chat\">\ud83d\udcac H\u1ecfi \u0111\u00e1p h\u00e0nh ch\u00ednh<\/button>\r\n            <button class=\"edbb-tab\" data-mode=\"voice\">\ud83c\udf99H\u1ecfi \u0111\u00e1p gi\u1ecdng n\u00f3i<\/button>\r\n            <button class=\"edbb-tab\" data-mode=\"news\">\ud83d\udcf0 Tin t\u1ee9c ph\u01b0\u1eddng<\/button>\r\n          <\/div>\r\n\r\n          <div class=\"edbb-main\" id=\"edbb-main\">\r\n              <!-- Layout ch\u00ednh cho tab H\u1ecfi \u0111\u00e1p -->\r\n              <div class=\"edbb-chat-layout\" id=\"edbb-chat-layout\">\r\n                <!-- C\u1ed9t chat -->\r\n                <div class=\"edbb-chat-col\" id=\"edbb-chat-col\">\r\n                  <div class=\"edbb-log\" id=\"edbb-log\" aria-live=\"polite\"><\/div>\r\n                  <div class=\"edbb-input-wrap\">\r\n                    <div class=\"edbb-input-row\">\r\n                      <textarea id=\"edbb-input\" class=\"edbb-input\" placeholder=\"Nh\u1eadp c\u00e2u h\u1ecfi v\u1ec1 th\u1ee7 t\u1ee5c h\u00e0nh ch\u00ednh, h\u1ed3 s\u01a1, gi\u1ea5y t\u1edd... (Shift+Enter \u0111\u1ec3 xu\u1ed1ng d\u00f2ng)\"><\/textarea>\r\n                      <button id=\"edbb-send\" class=\"edbb-btn\">G\u1eedi<\/button>\r\n                    <\/div>\r\n                    <div class=\"edbb-suggestions\" id=\"edbb-suggestions\"><\/div>\r\n                    <div class=\"edbb-mode-note\">Tr\u1ee3 l\u00fd ch\u1ec9 tr\u1ea3 l\u1eddi c\u00e1c v\u1ea5n \u0111\u1ec1 li\u00ean quan \u0111\u1ebfn th\u1ee7 t\u1ee5c h\u00e0nh ch\u00ednh v\u00e0 th\u00f4ng tin tr\u00ean \u0111\u1ecba b\u00e0n ph\u01b0\u1eddng \u0110i\u1ec7n B\u00e0n B\u1eafc.<\/div>\r\n                  <\/div>\r\n                <\/div>\r\n            \r\n                <!-- C\u1ed9t b\u00ean: Flowchart + B\u1ea3n \u0111\u1ed3 + Th\u00f4ng tin nhanh -->\r\n                <div class=\"edbb-side-col\" id=\"edbb-side-col\">\r\n                  <div class=\"edbb-panel\" id=\"edbb-flow-panel\" style=\"display:none;\">\r\n                    <div class=\"edbb-panel-title\">L\u01b0u \u0111\u1ed3 th\u1ee7 t\u1ee5c<\/div>\r\n                    <div class=\"edbb-panel-sub\">C\u00e1c b\u01b0\u1edbc x\u1eed l\u00fd h\u1ed3 s\u01a1 \u0111\u01b0\u1ee3c t\u00f3m t\u1eaft \u0111\u1ec3 b\u1ea1n d\u1ec5 theo d\u00f5i.<\/div>\r\n                    <div id=\"edbb-flow-steps\"><\/div>\r\n                  <\/div>\r\n            \r\n                  <div class=\"edbb-panel\" id=\"edbb-map-panel\" style=\"display:none;\">\r\n                    <div class=\"edbb-panel-title\" id=\"edbb-map-title\">B\u1ea3n \u0111\u1ed3<\/div>\r\n                    <div class=\"edbb-panel-sub\" id=\"edbb-map-desc\">Li\u00ean k\u1ebft b\u1ea3n \u0111\u1ed3 Google Maps.<\/div>\r\n                    <a id=\"edbb-map-link\" class=\"edbb-map-link\" href=\"#\" target=\"_blank\" rel=\"noopener\">\r\n                      \ud83d\udccd M\u1edf Google Maps\r\n                    <\/a>\r\n                  <\/div>\r\n            \r\n                  <div class=\"edbb-panel\" id=\"edbb-news-side\">\r\n                    <div class=\"edbb-panel-title\">Th\u00f4ng tin nhanh<\/div>\r\n                    <ul style=\"padding-left:18px;margin:4px 0;font-size:13px;\">\r\n                      <li>Gi\u1edd l\u00e0m vi\u1ec7c: T2\u2013T6 (7h30\u201311h30, 13h30\u201317h00)<\/li>\r\n                      <li>Khi \u0111i l\u00e0m th\u1ee7 t\u1ee5c nh\u1edb mang theo CCCD v\u00e0 gi\u1ea5y t\u1edd li\u00ean quan.<\/li>\r\n                      <li>Vui l\u00f2ng \u0111\u1ebfn s\u1edbm tr\u01b0\u1edbc gi\u1edd \u0111\u00f3ng c\u1eeda 30 ph\u00fat \u0111\u1ec3 \u0111\u01b0\u1ee3c h\u1ed7 tr\u1ee3 t\u1ed1t nh\u1ea5t.<\/li>\r\n                    <\/ul>\r\n                  <\/div>\r\n                <\/div>\r\n              <\/div><!-- \/edbb-chat-layout -->\r\n              \r\n            <!-- Layout ri\u00eang cho tab Gi\u1ecdng n\u00f3i -->\r\n              <div id=\"edbb-voice-main\" style=\"display:none;\">\r\n                <div class=\"edbb-voice-wrap\">\r\n                  <div class=\"edbb-voice-status\" id=\"edbb-voice-status\">\r\n                    Nh\u1ea5n n\u00fat &#127908; \u0111\u1ec3 b\u1eaft \u0111\u1ea7u n\u00f3i (khuy\u00ean d\u00f9ng Chrome\/Edge v\u00e0 cho ph\u00e9p quy\u1ec1n micro).\r\n                  <\/div>\r\n                  <button class=\"edbb-voice-btn\" id=\"edbb-voice-btn\">\r\n                    <span class=\"edbb-voice-icon\">\ud83c\udf99\ufe0f<\/span>\r\n                    <span id=\"edbb-voice-btn-label\">B\u1eaft \u0111\u1ea7u ghi \u00e2m<\/span>\r\n                  <\/button>\r\n                  <div class=\"edbb-voice-log\">\r\n                    <div><strong>B\u1ea1n:&nbsp;<\/strong><span id=\"edbb-voice-user\"><\/span><\/div>\r\n                    <div style=\"margin-top:8px;\"><strong>Tr\u1ee3 l\u00fd:&nbsp;<\/strong><span id=\"edbb-voice-bot\"><\/span><\/div>\r\n                  <\/div>\r\n                  <p class=\"edbb-mode-note\">\r\n                    Tr\u1ee3 l\u00fd s\u1ebd chuy\u1ec3n gi\u1ecdng n\u00f3i c\u1ee7a b\u1ea1n th\u00e0nh v\u0103n b\u1ea3n, g\u1eedi l\u00ean h\u1ec7 th\u1ed1ng r\u1ed3i tr\u1ea3 l\u1eddi l\u1ea1i (c\u00f3 th\u1ec3 \u0111\u1ecdc to n\u1ebfu tr\u00ecnh duy\u1ec7t h\u1ed7 tr\u1ee3).\r\n                  <\/p>\r\n                <\/div>\r\n              <\/div>\r\n\r\n            \r\n              <!-- Layout ri\u00eang cho tab Tin t\u1ee9c ph\u01b0\u1eddng -->\r\n              <div id=\"edbb-news-main\" style=\"display:none;\">\r\n                <div style=\"padding:14px\">\r\n                  <div class=\"edbb-news-card\">\r\n                    <div class=\"edbb-news-title\">Th\u00f4ng b\u00e1o c\u1ea5p C\u0103n c\u01b0\u1edbc c\u00f4ng d\u00e2n g\u1eafn chip<\/div>\r\n                    <div class=\"edbb-news-date\">25\/07\/2024<\/div>\r\n                    <div>UBND Ph\u01b0\u1eddng \u0110i\u1ec7n B\u00e0n B\u1eafc tr\u00e2n tr\u1ecdng th\u00f4ng b\u00e1o v\u1ec1 k\u1ebf ho\u1ea1ch c\u1ea5p CCCD g\u1eafn chip cho c\u00f4ng d\u00e2n c\u01b0 tr\u00fa tr\u00ean \u0111\u1ecba b\u00e0n. Vui l\u00f2ng mang theo gi\u1ea5y t\u1edd t\u00f9y th\u00e2n khi \u0111\u1ebfn l\u00e0m th\u1ee7 t\u1ee5c.<\/div>\r\n                  <\/div>\r\n                  <div class=\"edbb-news-card\">\r\n                    <div class=\"edbb-news-title\">Khai m\u1ea1c h\u00e8 v\u00e0 ho\u1ea1t \u0111\u1ed9ng cho thi\u1ebfu nhi<\/div>\r\n                    <div class=\"edbb-news-date\">15\/07\/2024<\/div>\r\n                    <div>Ch\u01b0\u01a1ng tr\u00ecnh h\u00e8 2024 v\u1edbi nhi\u1ec1u ho\u1ea1t \u0111\u1ed9ng sinh ho\u1ea1t h\u00e8, k\u1ef9 n\u0103ng s\u1ed1ng v\u00e0 s\u00e2n ch\u01a1i cho thi\u1ebfu nhi tr\u00ean \u0111\u1ecba b\u00e0n ph\u01b0\u1eddng.<\/div>\r\n                  <\/div>\r\n                  <div class=\"edbb-news-card\">\r\n                    <div class=\"edbb-news-title\">H\u01b0\u1edbng d\u1eabn n\u1ed9p h\u1ed3 s\u01a1 tr\u1ef1c tuy\u1ebfn<\/div>\r\n                    <div class=\"edbb-news-date\">05\/07\/2024<\/div>\r\n                    <div>Ng\u01b0\u1eddi d\u00e2n c\u00f3 th\u1ec3 n\u1ed9p m\u1ed9t s\u1ed1 th\u1ee7 t\u1ee5c h\u00e0nh ch\u00ednh qua d\u1ecbch v\u1ee5 c\u00f4ng tr\u1ef1c tuy\u1ebfn. Tr\u1ee3 l\u00fd \u1ea3o c\u00f3 th\u1ec3 h\u01b0\u1edbng d\u1eabn c\u1ee5 th\u1ec3 khi b\u1ea1n \u0111\u1eb7t c\u00e2u h\u1ecfi trong tab H\u1ecfi \u0111\u00e1p.<\/div>\r\n                  <\/div>\r\n                <\/div>\r\n              <\/div>\r\n            <\/div><!-- \/edbb-main -->\r\n        <\/div><!-- \/edbb-body -->\r\n      <\/div><!-- \/edbb-card -->\r\n    <\/div><!-- \/edbb-wrap -->\r\n\r\n    <script>\r\n    (function(){\r\n      const REST_URL = 'https:\/\/aiggroup.com.vn\/en\/wp-json\/edbb\/v1\/chat';\r\n      const NONCE    = '5ebdbdf49f';\r\n      const TTS_URL  = 'https:\/\/aiggroup.com.vn\/en\/wp-json\/edbb\/v1\/tts';\r\n\r\n    \r\n      const logEl       = document.getElementById('edbb-log');\r\n      const inputEl     = document.getElementById('edbb-input');\r\n      const sendBtn     = document.getElementById('edbb-send');\r\n      const suggEl      = document.getElementById('edbb-suggestions');\r\n      const flowPanel   = document.getElementById('edbb-flow-panel');\r\n      const flowStepsEl = document.getElementById('edbb-flow-steps');\r\n      const mapPanel    = document.getElementById('edbb-map-panel');\r\n      const mapTitleEl  = document.getElementById('edbb-map-title');\r\n      const mapLinkEl   = document.getElementById('edbb-map-link');\r\n\r\n      const tabs      = document.querySelectorAll('.edbb-tab');\r\n      const chatLayout= document.getElementById('edbb-chat-layout');\r\n      const newsMain  = document.getElementById('edbb-news-main');\r\n      \r\n        const voiceMain    = document.getElementById('edbb-voice-main');\r\n      const voiceBtn     = document.getElementById('edbb-voice-btn');\r\n      const voiceBtnLbl  = document.getElementById('edbb-voice-btn-label');\r\n      const voiceStatus  = document.getElementById('edbb-voice-status');\r\n      const voiceUserEl  = document.getElementById('edbb-voice-user');\r\n      const voiceBotEl   = document.getElementById('edbb-voice-bot');\r\n      \r\n    \/\/ ====== Ch\u1ecdn voice ti\u1ebfng Vi\u1ec7t cho Text-to-Speech ======\r\n      let vietnameseVoice = null;\r\n\r\n      function pickVietnameseVoice(){\r\n        if (!('speechSynthesis' in window)) return;\r\n        const voices = window.speechSynthesis.getVoices();\r\n        if (!voices || !voices.length) return;\r\n\r\n        \/\/ \u01afu ti\u00ean c\u00e1c voice c\u00f3 lang b\u1eaft \u0111\u1ea7u b\u1eb1ng 'vi'\r\n        vietnameseVoice = voices.find(v => v.lang && v.lang.toLowerCase().startsWith('vi'));\r\n        \/\/ N\u1ebfu kh\u00f4ng c\u00f3, th\u1eed t\u00ecm theo t\u00ean\r\n        if (!vietnameseVoice) {\r\n          vietnameseVoice = voices.find(v =>\r\n            (v.name || '').toLowerCase().includes('vietnam')\r\n          );\r\n        }\r\n      }\r\n\r\n      if ('speechSynthesis' in window) {\r\n        \/\/ Chrome load voices async, n\u00ean l\u1eafng nghe events\r\n        window.speechSynthesis.onvoiceschanged = pickVietnameseVoice;\r\n        \/\/ g\u1ecdi th\u1eed 1 l\u1ea7n\r\n        pickVietnameseVoice();\r\n      }\r\n\r\n\r\n\r\n      \/\/ ====== helper: t\u00e1ch JSON theo tag [FLOWCHART] \/ [MAP] \/ [SUGGESTIONS] ======\r\n      function extractJsonBlock(text, label, isArray){\r\n        const tag = '[' + label + ']';\r\n        const idx = text.indexOf(tag);\r\n        if (idx === -1) return { text, value: null };\r\n\r\n        \/\/ ph\u1ea7n sau tag\r\n        let rest = text.slice(idx + tag.length);\r\n        const colonIdx = rest.indexOf(':');\r\n        if (colonIdx !== -1) rest = rest.slice(colonIdx + 1);\r\n\r\n        const openChar  = isArray ? '[' : '{';\r\n        const closeChar = isArray ? ']' : '}';\r\n\r\n        const start = rest.indexOf(openChar);\r\n        if (start === -1) return { text, value: null };\r\n\r\n        let depth = 0;\r\n        let endIndex = -1;\r\n        for (let i = start; i < rest.length; i++) {\r\n          const ch = rest[i];\r\n          if (ch === openChar) depth++;\r\n          else if (ch === closeChar) {\r\n            depth--;\r\n            if (depth === 0) { endIndex = i; break; }\r\n          }\r\n        }\r\n        if (endIndex === -1) return { text, value: null };\r\n\r\n        const jsonStr = rest.slice(start, endIndex + 1);\r\n        let value = null;\r\n        try { value = JSON.parse(jsonStr); }\r\n        catch(e){ console.error('JSON parse error', label, e, jsonStr); }\r\n\r\n        const removeStr = text.slice(idx, idx + tag.length) + rest.slice(0, endIndex + 1);\r\n        const newText = text.replace(removeStr, '').trim();\r\n\r\n        return { text: newText, value };\r\n      }\r\n\r\n      function switchMode(mode){\r\n        tabs.forEach(btn=>{\r\n          if(btn.getAttribute('data-mode') === mode){\r\n            btn.classList.add('edbb-tab-active');\r\n          }else{\r\n            btn.classList.remove('edbb-tab-active');\r\n          }\r\n        });\r\n\r\n        if(mode === 'chat'){\r\n          chatLayout.style.display = 'flex';\r\n          voiceMain.style.display = 'none';\r\n          newsMain.style.display = 'none';\r\n        } else if (mode === 'voice') {\r\n          chatLayout.style.display = 'none';\r\n          voiceMain.style.display = 'block';\r\n          newsMain.style.display = 'none';\r\n        } else { \/\/ news\r\n          chatLayout.style.display = 'none';\r\n          voiceMain.style.display = 'none';\r\n          newsMain.style.display = 'block';\r\n        }\r\n      }\r\n\r\n\r\n      tabs.forEach(btn=>{\r\n        btn.addEventListener('click', ()=>switchMode(btn.getAttribute('data-mode')));\r\n      });\r\n      switchMode('chat');\r\n\r\n      function addMessage(text, role){\r\n        const row = document.createElement('div');\r\n        row.className = 'edbb-msg-row ' + (role === 'user' ? 'edbb-msg-user' : '');\r\n        const avatar = document.createElement('div');\r\n        avatar.className = 'edbb-avatar ' + (role === 'bot' ? 'edbb-avatar-bot' : '');\r\n        avatar.textContent = role === 'user' ? '\ud83d\udc64' : '\ud83c\udfdb\ufe0f';\r\n        const bubble = document.createElement('div');\r\n        bubble.className = 'edbb-bubble ' + (role === 'user' ? 'edbb-bubble-user' : 'edbb-bubble-bot');\r\n        bubble.textContent = text;\r\n        if (role === 'user') {\r\n          row.appendChild(bubble);\r\n          row.appendChild(avatar);\r\n        } else {\r\n          row.appendChild(avatar);\r\n          row.appendChild(bubble);\r\n        }\r\n        logEl.appendChild(row);\r\n        logEl.scrollTop = logEl.scrollHeight;\r\n      }\r\n\r\n      function renderFlowchart(flow){\r\n        if (!flow || !Array.isArray(flow.nodes) || flow.nodes.length === 0) {\r\n          flowPanel.style.display = 'none';\r\n          flowStepsEl.innerHTML = '';\r\n          return;\r\n        }\r\n        flowStepsEl.innerHTML = '';\r\n        const nodesById = {};\r\n        flow.nodes.forEach(n => nodesById[n.id] = n);\r\n        const hasIncoming = {};\r\n        (flow.edges || []).forEach(e => { hasIncoming[e.to] = true; });\r\n\r\n        let start = flow.nodes[0];\r\n        flow.nodes.forEach(n => { if(!hasIncoming[n.id]) start = n; });\r\n\r\n        const used = {};\r\n        const ordered = [];\r\n        let current = start;\r\n        while (current && !used[current.id]) {\r\n          ordered.push(current);\r\n          used[current.id] = true;\r\n          const edge = (flow.edges || []).find(e => e.from === current.id && !used[e.to]);\r\n          current = edge ? nodesById[edge.to] : null;\r\n        }\r\n        if (ordered.length === 0) ordered.push(...flow.nodes);\r\n\r\n        ordered.forEach((node, idx)=>{\r\n          const row = document.createElement('div');\r\n          row.className = 'edbb-flow-step';\r\n          const badge = document.createElement('div');\r\n          badge.className = 'edbb-flow-badge';\r\n          badge.textContent = (idx+1);\r\n          const label = document.createElement('div');\r\n          label.className = 'edbb-flow-label';\r\n          label.textContent = node.label || ('B\u01b0\u1edbc ' + (idx+1));\r\n          row.appendChild(badge);\r\n          row.appendChild(label);\r\n          flowStepsEl.appendChild(row);\r\n        });\r\n\r\n        flowPanel.style.display = 'block';\r\n      }\r\n\r\n      function renderMap(mapInfo){\r\n        if(!mapInfo || !mapInfo.title || !mapInfo.uri){\r\n          mapPanel.style.display = 'none';\r\n          return;\r\n        }\r\n        mapTitleEl.textContent = mapInfo.title;\r\n        mapLinkEl.href = mapInfo.uri;\r\n        mapPanel.style.display = 'block';\r\n      }\r\n\r\n      function clearSuggestions(){\r\n        suggEl.innerHTML = '';\r\n      }\r\n\r\n      function setSuggestions(list){\r\n        clearSuggestions();\r\n        if(!Array.isArray(list) || list.length === 0) return;\r\n        list.slice(0,3).forEach(text=>{\r\n          const btn = document.createElement('button');\r\n          btn.className = 'edbb-suggestion-btn';\r\n          btn.textContent = text;\r\n          btn.addEventListener('click', ()=>{\r\n            inputEl.value = text;\r\n            onSend();\r\n          });\r\n          suggEl.appendChild(btn);\r\n        });\r\n      }\r\n\r\n      async function callGemini(prompt){\r\n        const url = REST_URL + '?_wpnonce=' + encodeURIComponent(NONCE);\r\n        const res = await fetch(url, {\r\n          method:'POST',\r\n          headers:{'Content-Type':'application\/json'},\r\n          body: JSON.stringify({ prompt })\r\n        });\r\n        if(!res.ok){\r\n          const t = await res.text();\r\n          throw new Error('HTTP ' + res.status + ': ' + t);\r\n        }\r\n        return res.json();\r\n      }\r\n    \/\/ G\u1ecdi ElevenLabs TTS qua REST API WP, ph\u00e1t audio tr\u1ea3 v\u1ec1\r\n      async function speakWithElevenLabs(text){\r\n        if (!text) return;\r\n        try{\r\n          const res = await fetch(TTS_URL + '?_wpnonce=' + encodeURIComponent(NONCE), {\r\n            method: 'POST',\r\n            headers: { 'Content-Type': 'application\/json' },\r\n            body: JSON.stringify({ text })\r\n          });\r\n          if (!res.ok) {\r\n            console.error('TTS HTTP error', res.status);\r\n            return;\r\n          }\r\n          const arrayBuffer = await res.arrayBuffer();\r\n          const blob = new Blob([arrayBuffer], { type: 'audio\/mpeg' });\r\n          const url = URL.createObjectURL(blob);\r\n          const audio = new Audio(url);\r\n          audio.play();\r\n        } catch (err) {\r\n          console.error('TTS error', err);\r\n        }\r\n      }\r\n\r\n      \r\n      \r\n     \/\/ ====== Gi\u1ecdng n\u00f3i: d\u00f9ng Web Speech API (speech recognition) ======\r\n      let recognition = null;\r\n      let recognizing = false;\r\n      let lastTranscript = '';\r\n\r\n      const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition || null;\r\n\r\n      if (!SpeechRecognition) {\r\n        \/\/ Tr\u00ecnh duy\u1ec7t kh\u00f4ng h\u1ed7 tr\u1ee3\r\n        if (voiceStatus) {\r\n          voiceStatus.textContent = 'Tr\u00ecnh duy\u1ec7t c\u1ee7a b\u1ea1n ch\u01b0a h\u1ed7 tr\u1ee3 nh\u1eadn d\u1ea1ng gi\u1ecdng n\u00f3i. Vui l\u00f2ng d\u00f9ng Chrome\/Edge tr\u00ean m\u00e1y t\u00ednh ho\u1eb7c Android.';\r\n        }\r\n        if (voiceBtn) {\r\n          voiceBtn.disabled = true;\r\n        }\r\n      } else {\r\n        recognition = new SpeechRecognition();\r\n        recognition.lang = 'vi-VN';\r\n        recognition.interimResults = false;\r\n        recognition.maxAlternatives = 1;\r\n\r\n        recognition.onstart = function(){\r\n          recognizing = true;\r\n          lastTranscript = '';\r\n          if (voiceStatus) voiceStatus.textContent = '\u0110ang nghe, b\u1ea1n h\u00e3y n\u00f3i...';\r\n          if (voiceBtn) {\r\n            voiceBtn.classList.add('edbb-voice-btn-active');\r\n            voiceBtnLbl.textContent = '\u0110ang nghe... Nh\u1ea5n \u0111\u1ec3 d\u1eebng';\r\n          }\r\n        };\r\n\r\n        recognition.onerror = function(event){\r\n          recognizing = false;\r\n          if (voiceStatus) voiceStatus.textContent = 'L\u1ed7i micro: ' + event.error;\r\n          if (voiceBtn) {\r\n            voiceBtn.classList.remove('edbb-voice-btn-active');\r\n            voiceBtnLbl.textContent = 'B\u1eaft \u0111\u1ea7u ghi \u00e2m';\r\n          }\r\n        };\r\n\r\n        recognition.onend = function(){\r\n          recognizing = false;\r\n          if (!lastTranscript && voiceStatus) {\r\n            voiceStatus.textContent = 'Nh\u1ea5n n\u00fat micro \u0111\u1ec3 b\u1eaft \u0111\u1ea7u n\u00f3i';\r\n          }\r\n          if (voiceBtn) {\r\n            voiceBtn.classList.remove('edbb-voice-btn-active');\r\n            voiceBtnLbl.textContent = 'B\u1eaft \u0111\u1ea7u ghi \u00e2m';\r\n          }\r\n        };\r\n\r\n        recognition.onresult = async function(event){\r\n          lastTranscript = event.results[0][0].transcript;\r\n          if (voiceUserEl) voiceUserEl.textContent = lastTranscript;\r\n          if (voiceStatus) voiceStatus.textContent = '\u0110ang g\u1eedi c\u00e2u h\u1ecfi...';\r\n        \r\n          try{\r\n            const data = await callGemini(lastTranscript);\r\n            let text = '';\r\n            const cand = data && data.candidates && data.candidates[0];\r\n            if (cand && cand.content && Array.isArray(cand.content.parts)) {\r\n              text = cand.content.parts.map(p => p.text || '').join('\\n');\r\n            }\r\n            if (!text) text = '[Kh\u00f4ng c\u00f3 ph\u1ea3n h\u1ed3i]';\r\n        \r\n            \/\/ lo\u1ea1i b\u1ecf FLOWCHART \/ MAP \/ SUGGESTIONS n\u1ebfu c\u00f3\r\n            let flowRes = extractJsonBlock(text, 'FLOWCHART', false);\r\n            text = flowRes.text;\r\n            flowRes = extractJsonBlock(text, 'MAP', false);\r\n            text = flowRes.text;\r\n            flowRes = extractJsonBlock(text, 'SUGGESTIONS', true);\r\n            text = flowRes.text\r\n              .replace(\/\\[FLOWCHART\\]:[\\s\\S]*\/g, '')\r\n              .replace(\/\\[SUGGESTIONS\\]:[\\s\\S]*\/g, '')\r\n              .trim();\r\n        \r\n            if (voiceBotEl) voiceBotEl.textContent = text;\r\n            if (voiceStatus) voiceStatus.textContent = '\u0110\u00e3 nh\u1eadn ph\u1ea3n h\u1ed3i. \u0110ang ph\u00e1t gi\u1ecdng n\u00f3i...';\r\n        \r\n            \/\/ \ud83d\udc49 \u0110\u1ecdc b\u1eb1ng ElevenLabs (thay cho speechSynthesis)\r\n            speakWithElevenLabs(text);\r\n        \r\n          }catch(err){\r\n            if (voiceBotEl) voiceBotEl.textContent = 'L\u1ed7i: ' + err.message;\r\n            if (voiceStatus) voiceStatus.textContent = 'C\u00f3 l\u1ed7i x\u1ea3y ra khi g\u1eedi c\u00e2u h\u1ecfi.';\r\n          }\r\n        };\r\n\r\n\r\n        if (voiceBtn) {\r\n          voiceBtn.addEventListener('click', function(){\r\n            if (!recognition) return;\r\n            if (recognizing) {\r\n              recognition.stop();\r\n            } else {\r\n              voiceBotEl.textContent = '';\r\n              voiceUserEl.textContent = '';\r\n              recognition.start();\r\n            }\r\n          });\r\n        }\r\n      }\r\n\r\n\r\n      async function onSend(){\r\n        const q = inputEl.value.trim();\r\n        if(!q) return;\r\n        addMessage(q, 'user');\r\n        inputEl.value = '';\r\n        sendBtn.disabled = true;\r\n        try{\r\n          const data = await callGemini(q);\r\n          let text = '';\r\n          const cand = data && data.candidates && data.candidates[0];\r\n          if (cand && cand.content && Array.isArray(cand.content.parts)) {\r\n            text = cand.content.parts.map(p => p.text || '').join('\\n');\r\n          }\r\n          if (!text) text = '[Kh\u00f4ng c\u00f3 ph\u1ea3n h\u1ed3i]';\r\n\r\n          \/\/ --- t\u00e1ch FLOWCHART \/ MAP \/ SUGGESTIONS kh\u1ecfi text ---\r\n          let flow = null, mapInfo = null, suggestions = [];\r\n\r\n          let result = extractJsonBlock(text, 'FLOWCHART', false);\r\n          text = result.text;\r\n          flow = result.value;\r\n\r\n          result = extractJsonBlock(text, 'MAP', false);\r\n          text = result.text;\r\n          mapInfo = result.value;\r\n\r\n          result = extractJsonBlock(text, 'SUGGESTIONS', true);\r\n          text = result.text;\r\n          suggestions = result.value || [];\r\n\r\n          \/\/ fallback: n\u1ebfu v\u1eabn c\u00f2n tag th\u00ec x\u00f3a n\u1ed1t ph\u1ea7n c\u00f2n l\u1ea1i cho ch\u1eafc\r\n          text = text.replace(\/\\[FLOWCHART\\]:[\\s\\S]*\/g, '').replace(\/\\[SUGGESTIONS\\]:[\\s\\S]*\/g, '').trim();\r\n\r\n          addMessage(text, 'bot');\r\n          renderFlowchart(flow);\r\n          renderMap(mapInfo);\r\n          setSuggestions(suggestions);\r\n        }catch(err){\r\n          addMessage('L\u1ed7i: ' + err.message, 'bot');\r\n        }finally{\r\n          sendBtn.disabled = false;\r\n        }\r\n      }\r\n\r\n      sendBtn.addEventListener('click', onSend);\r\n      inputEl.addEventListener('keydown', e=>{\r\n        if(e.key === 'Enter' && !e.shiftKey){\r\n          e.preventDefault();\r\n          onSend();\r\n        }\r\n      });\r\n\r\n      \/\/ L\u1eddi ch\u00e0o \u0111\u1ea7u\r\n      addMessage('Xin ch\u00e0o! T\u00f4i l\u00e0 tr\u1ee3 l\u00fd h\u00e0nh ch\u00ednh \u1ea3o e-\u0110i\u1ec7n B\u00e0n B\u1eafc. B\u1ea1n c\u1ea7n h\u1ed7 tr\u1ee3 th\u1ee7 t\u1ee5c g\u00ec h\u00f4m nay?', 'bot');\r\n    })();\r\n    <\/script>\r\n\r\n    <\/div><\/div><\/div><\/div><\/div><\/div><\/div><!-- cell_inner --><\/div><!-- cell --><\/div><!-- port --><\/section>","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-7887","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/aiggroup.com.vn\/en\/wp-json\/wp\/v2\/pages\/7887","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/aiggroup.com.vn\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/aiggroup.com.vn\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/aiggroup.com.vn\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/aiggroup.com.vn\/en\/wp-json\/wp\/v2\/comments?post=7887"}],"version-history":[{"count":2,"href":"https:\/\/aiggroup.com.vn\/en\/wp-json\/wp\/v2\/pages\/7887\/revisions"}],"predecessor-version":[{"id":7889,"href":"https:\/\/aiggroup.com.vn\/en\/wp-json\/wp\/v2\/pages\/7887\/revisions\/7889"}],"wp:attachment":[{"href":"https:\/\/aiggroup.com.vn\/en\/wp-json\/wp\/v2\/media?parent=7887"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}