V.AISTUDIO / index.html
bep40's picture
Ép tải bản sửa An Cường, custom shorts và swipe mới
b982741 verified
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>V.AI STUDIO | Niềm tin khách hàng là tài sản của chúng tôi</title>
<meta name="description" content="V.AI STUDIO — 8000+ sản phẩm thiết bị nhà bếp & điện máy. Malloca, Eurogold, Grob, Canzy, Demax & Điện Máy Xanh chính hãng.">
<meta property="og:type" content="website">
<meta property="og:site_name" content="V.AI STUDIO">
<meta property="og:title" id="ogTitle" content="V.AI STUDIO | Niềm tin khách hàng là tài sản của chúng tôi">
<meta property="og:description" id="ogDesc" content="8000+ sản phẩm thiết bị nhà bếp & điện máy chính hãng. Malloca, Eurogold, Grob, Canzy, Demax & Điện Máy Xanh.">
<meta property="og:image" id="ogImage" content="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/logo_600.png">
<meta property="og:url" id="ogUrl" content="https://bep40-v-aistudio.static.hf.space/">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" id="twTitle" content="V.AI STUDIO">
<meta name="twitter:description" id="twDesc" content="8000+ sản phẩm thiết bị nhà bếp & điện máy chính hãng">
<meta name="twitter:image" id="twImage" content="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/logo_600.png">
<link rel="icon" href="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/favicon.png">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<style>
*{margin:0;padding:0;box-sizing:border-box}
:root{--p:#003f62;--pl:#005a8c;--a:#db9815;--al:#f0b840;--d:#0f172a;--l:#f8fafc;--g:#64748b;--gl:#e2e8f0;--w:#fff;--r:14px;--t:all .25s ease}
body{font-family:'Inter',system-ui,sans-serif;color:var(--d);background:var(--l);overflow-x:hidden}
::-webkit-scrollbar{width:6px}::-webkit-scrollbar-thumb{background:var(--p);border-radius:3px}
img{max-width:100%;display:block}a{text-decoration:none;color:inherit}
.container{max-width:1360px;margin:0 auto;padding:0 20px}
/* TOPBAR */
.topbar{background:var(--p);color:#fff;padding:6px 0;font-size:.72rem}
.topbar .container{display:flex;justify-content:center;gap:28px;flex-wrap:wrap}
.topbar-item{display:flex;align-items:center;gap:6px}
.topbar-item i{color:var(--a);font-size:.65rem}
/* HEADER */
.header{background:#fff;position:sticky;top:0;z-index:100;box-shadow:0 2px 12px rgba(0,0,0,.06)}
.header .container{display:flex;align-items:center;justify-content:space-between;padding:10px 20px;gap:14px}
.logo{display:flex;align-items:center;gap:10px;flex-shrink:0;cursor:pointer}
.logo-box{width:40px;height:40px;background:var(--p);border-radius:10px;display:flex;align-items:center;justify-content:center;color:#fff;font-weight:900;font-size:1rem}
.logo-name{font-size:1.3rem;font-weight:900;color:var(--p);letter-spacing:1.5px}
.logo-name span{color:var(--a)}
.logo-sub{font-size:.55rem;color:var(--g);letter-spacing:2px;text-transform:uppercase}
/* SEARCH */
.search-box{flex:1;max-width:480px;position:relative}
.search-box input{width:100%;padding:9px 14px 9px 38px;border:2px solid var(--gl);border-radius:10px;font-size:.85rem;font-family:inherit;background:#fff;transition:var(--t);outline:none}
.search-box input:focus{border-color:var(--p);box-shadow:0 0 0 3px rgba(0,63,98,.08)}
.search-box>i{position:absolute;left:12px;top:50%;transform:translateY(-50%);color:var(--g);font-size:.85rem}
.search-count{position:absolute;right:10px;top:50%;transform:translateY(-50%);font-size:.68rem;color:var(--g);background:var(--l);padding:2px 7px;border-radius:6px}
.nav-icons{display:flex;gap:6px;flex-shrink:0}
.nav-icons a{width:36px;height:36px;border-radius:9px;display:flex;align-items:center;justify-content:center;color:var(--p);background:rgba(0,63,98,.05);transition:var(--t);font-size:.85rem}
.nav-icons a:hover{background:var(--p);color:#fff}
.hamburger{display:none;background:none;border:none;cursor:pointer;padding:6px}
.hamburger span{display:block;width:20px;height:2px;background:var(--p);margin:4px 0}
/* HERO */
.hero{background:linear-gradient(135deg,var(--p),var(--pl),#0077b6);padding:40px 0;text-align:center;position:relative;overflow:hidden}
.hero::after{content:'';position:absolute;top:-40%;right:-15%;width:500px;height:500px;background:radial-gradient(circle,rgba(219,152,21,.1),transparent 70%);border-radius:50%}
.hero .container{position:relative;z-index:2}
.hero h1{font-size:2.2rem;color:#fff;font-weight:900;margin-bottom:10px}.hero h1 span{color:var(--al)}
.hero p{color:rgba(255,255,255,.75);font-size:.9rem;max-width:550px;margin:0 auto 20px;line-height:1.5}
.hero-stats{display:flex;justify-content:center;gap:36px;flex-wrap:wrap}
.hero-stat{color:#fff;text-align:center}.hero-stat strong{display:block;font-size:1.6rem;font-weight:900}.hero-stat span{font-size:.7rem;opacity:.7}
/* LAYOUT */
.main{display:flex;gap:22px;padding:28px 0}
.sidebar{width:260px;flex-shrink:0;position:sticky;top:76px;height:fit-content}
.content{flex:1;min-width:0}
/* SIDEBAR */
.sb-card{background:#fff;border-radius:var(--r);padding:18px;margin-bottom:14px;box-shadow:0 1px 4px rgba(0,0,0,.06)}
.sb-card h3{font-size:.8rem;font-weight:700;color:var(--d);margin-bottom:12px;display:flex;align-items:center;gap:7px}
.sb-card h3 i{color:var(--a);font-size:.75rem}
.cat-list{list-style:none}
.cat-item{padding:8px 10px;border-radius:9px;cursor:pointer;transition:var(--t);font-size:.78rem;font-weight:500;display:flex;align-items:center;gap:9px;color:var(--d);margin-bottom:1px}
.cat-item:hover{background:rgba(0,63,98,.04);color:var(--p)}
.cat-item.active{background:var(--p);color:#fff}
.cat-count{margin-left:auto;font-size:.65rem;opacity:.6;background:rgba(0,0,0,.05);padding:2px 7px;border-radius:20px}
.cat-item.active .cat-count{background:rgba(255,255,255,.2)}
.cat-item i{width:18px;text-align:center;font-size:.8rem}
.cat-sub{font-size:.72rem;padding:5px 10px 5px 18px;opacity:.85}
.cat-sub:hover{opacity:1}
.pr{display:flex;flex-direction:column;gap:3px}
.pr-item{padding:7px 10px;border-radius:8px;cursor:pointer;font-size:.76rem;transition:var(--t);display:flex;align-items:center;gap:7px}
.pr-item:hover{background:rgba(0,63,98,.04)}
.pr-item.active{background:var(--p);color:#fff}
.pr-dot{width:7px;height:7px;border-radius:50%;border:2px solid var(--gl)}
.pr-item.active .pr-dot{border-color:#fff;background:#fff}
/* TOOLBAR */
.toolbar{display:flex;align-items:center;justify-content:space-between;margin-bottom:18px;flex-wrap:wrap;gap:10px}
.result-count{font-size:.82rem;color:var(--g)}.result-count strong{color:var(--d)}
.sort-select{padding:7px 12px;border:2px solid var(--gl);border-radius:9px;font-size:.78rem;font-family:inherit;background:#fff;cursor:pointer;outline:none;font-weight:500}
.view-btns{display:flex;gap:3px}
.view-btn{width:32px;height:32px;border:2px solid var(--gl);border-radius:7px;display:flex;align-items:center;justify-content:center;cursor:pointer;background:#fff;color:var(--g);transition:var(--t);font-size:.75rem}
.view-btn.active{border-color:var(--p);color:var(--p);background:rgba(0,63,98,.04)}
/* GRID */
.pg{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:16px}
.pg.lv{grid-template-columns:1fr}
.pg.lv .pc{display:flex}.pg.lv .pc .pi{width:180px;height:140px;flex-shrink:0}.pg.lv .pc .pb{flex:1}
/* CARD */
.pc{background:#fff;border-radius:var(--r);overflow:hidden;box-shadow:0 1px 3px rgba(0,0,0,.06);transition:var(--t);cursor:pointer;border:2px solid transparent}
.pc:hover{transform:translateY(-3px);box-shadow:0 8px 24px rgba(0,0,0,.1);border-color:var(--p)}
.pi{height:200px;overflow:hidden;position:relative;background:#f1f5f9;display:flex;align-items:center;justify-content:center}
.pi img{width:100%;height:100%;object-fit:contain;transition:transform .35s ease;padding:6px}
.pc:hover .pi img{transform:scale(1.05)}
.pi-badge{position:absolute;top:8px;left:8px;background:var(--p);color:#fff;padding:3px 8px;border-radius:5px;font-size:.6rem;font-weight:700}
.pb{padding:14px}
.pn{font-size:.82rem;font-weight:600;color:var(--d);line-height:1.35;margin-bottom:8px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;min-height:2.2em}
.pp{font-size:1rem;font-weight:800;color:var(--p)}
.pf{display:flex;align-items:center;justify-content:space-between;margin-top:8px}
.pf i{color:var(--a);font-size:.8rem}
/* PAGINATION */
.pagination{display:flex;justify-content:center;gap:5px;margin-top:26px;flex-wrap:wrap}
.pgb{width:36px;height:36px;border:2px solid var(--gl);border-radius:9px;display:flex;align-items:center;justify-content:center;cursor:pointer;background:#fff;font-size:.82rem;font-weight:600;transition:var(--t)}
.pgb:hover{border-color:var(--p);color:var(--p)}
.pgb.active{background:var(--p);color:#fff;border-color:var(--p)}
.pgb:disabled{opacity:.3;cursor:default}
#catTabs{display:flex;gap:6px;padding:4px 0 12px;margin-bottom:16px;overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none}
#catTabs::-webkit-scrollbar{display:none}
#catTabs .ctab{display:inline-flex;align-items:center;gap:6px;padding:9px 16px;border:2px solid var(--gl);border-radius:50px;cursor:pointer;background:#fff;font-size:.78rem;font-weight:600;transition:var(--t);white-space:nowrap;flex-shrink:0;color:var(--d);line-height:1}
#catTabs .ctab:hover{border-color:var(--p);color:var(--p);background:rgba(0,63,98,.03)}
#catTabs .ctab.active{background:var(--p);color:#fff;border-color:var(--p)}
#catTabs .ctab i{font-size:.7rem}
#catTabs .ctab .cnt{font-size:.6rem;opacity:.65;margin-left:2px}
#catTabs .ctab.active .cnt{opacity:.8}
@media(max-width:768px){#catTabs .ctab{padding:7px 12px;font-size:.7rem}}
.pg-dots{display:flex;align-items:center;font-size:.8rem;color:var(--g);padding:0 4px}
/* ========== DETAIL VIEW ========== */
#detailView{display:none;min-height:80vh}
#detailView.show{display:block}
#listView.hide{display:none}
.detail-back{display:inline-flex;align-items:center;gap:8px;color:var(--p);font-weight:600;font-size:.85rem;cursor:pointer;padding:8px 0;margin-bottom:20px;transition:var(--t)}
.detail-back:hover{color:var(--a)}
.detail-grid{display:grid;grid-template-columns:1fr 1fr;gap:36px;background:#fff;border-radius:20px;padding:32px;box-shadow:0 2px 16px rgba(0,0,0,.06)}
/* Gallery — Carousel */
.gallery{position:relative;width:100%}
.gallery-carousel{position:relative;width:100%;overflow:hidden;border-radius:16px;background:#f8fafc;margin-bottom:10px}
.gallery-track{display:flex;transition:transform .3s ease;will-change:transform}
.gallery-slide{min-width:100%;display:flex;align-items:center;justify-content:center;overflow:hidden;max-height:75vh}
.gallery-slide img{display:block;width:100%;height:auto;max-height:75vh;object-fit:contain}
.gallery-nav{position:absolute;top:50%;transform:translateY(-50%);width:36px;height:36px;border-radius:50%;background:rgba(0,0,0,.45);color:#fff;border:none;cursor:pointer;font-size:1rem;display:flex;align-items:center;justify-content:center;z-index:3;opacity:0;transition:opacity .2s}
.gallery-carousel:hover .gallery-nav{opacity:1}
.gallery-nav.gn-prev{left:8px}
.gallery-nav.gn-next{right:8px}
.gallery-dots{display:flex;justify-content:center;gap:6px;padding:6px 0}
.gallery-dot{width:8px;height:8px;border-radius:50%;background:var(--gl);cursor:pointer;transition:var(--t)}
.gallery-dot.active{background:var(--p);width:20px;border-radius:4px}
.gallery-thumbs{display:flex;flex-wrap:wrap;gap:6px;padding-bottom:6px}
.gallery-thumbs::-webkit-scrollbar{display:none}
.gallery-thumb{width:56px;height:56px;min-width:56px;border-radius:8px;overflow:hidden;border:2px solid var(--gl);cursor:pointer;flex-shrink:0;transition:var(--t);background:#f8fafc}
.gallery-thumb.active{border-color:var(--p)}
.gallery-thumb img{width:100%;height:100%;object-fit:contain;padding:2px}
@media(max-width:768px){
.gallery-nav{opacity:.7;width:32px;height:32px;font-size:.85rem}
.gallery-slide{max-height:60vh}
.gallery-slide img{max-height:60vh}
.gallery-thumbs{gap:5px}
.gallery-thumb{width:48px;height:48px;min-width:48px}
}
/* Info */
.detail-info{}
.detail-sku{font-size:.72rem;color:var(--g);margin-bottom:6px;font-weight:500}
.detail-cat{display:inline-flex;align-items:center;gap:6px;background:rgba(0,63,98,.06);color:var(--p);padding:5px 14px;border-radius:50px;font-size:.72rem;font-weight:700;margin-bottom:14px}
.detail-name{font-size:1.6rem;font-weight:800;color:var(--d);line-height:1.3;margin-bottom:16px}
.detail-price{font-size:1.8rem;font-weight:900;color:var(--p);margin-bottom:20px}
.detail-summary{font-size:.88rem;color:var(--g);line-height:1.7;margin-bottom:24px;white-space:pre-line}
.detail-badges{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:24px}
.detail-badge{display:flex;align-items:center;gap:6px;background:rgba(0,63,98,.04);padding:8px 14px;border-radius:10px;font-size:.78rem;font-weight:500;color:var(--d)}
.detail-badge i{color:var(--a);font-size:.75rem}
.detail-btn{display:inline-flex;align-items:center;gap:8px;padding:14px 28px;background:var(--p);color:#fff;border-radius:12px;font-weight:700;font-size:.88rem;transition:var(--t);border:none;cursor:pointer;font-family:inherit}
.detail-btn:hover{background:var(--pl);transform:translateY(-2px);box-shadow:0 8px 20px rgba(0,63,98,.25)}
.detail-btn-outline{background:transparent;border:2px solid var(--p);color:var(--p)}
.detail-btn-outline:hover{background:var(--p);color:#fff}
/* TABS */
.dtabs{margin-top:30px;background:#fff;border-radius:20px;padding:0;box-shadow:0 2px 16px rgba(0,0,0,.06);overflow:hidden}
.dtab-headers{display:flex;border-bottom:2px solid var(--gl);overflow-x:auto}
.dtab-h{padding:16px 28px;font-size:.85rem;font-weight:600;color:var(--g);cursor:pointer;transition:var(--t);border-bottom:3px solid transparent;white-space:nowrap;flex-shrink:0}
.dtab-h:hover{color:var(--p)}
.dtab-h.active{color:var(--p);border-color:var(--a);background:rgba(0,63,98,.02)}
.dtab-body{padding:28px}
.dtab-content{display:none}
.dtab-content.active{display:block}
/* Specs table */
.specs-table{width:100%;border-collapse:collapse}
.specs-table tr:nth-child(even){background:#f8fafc}
.specs-table td{padding:11px 16px;font-size:.84rem;border-bottom:1px solid var(--gl)}
.specs-table td:first-child{font-weight:600;color:var(--d);width:40%;background:rgba(0,63,98,.02)}
.specs-table td:last-child{color:var(--g)}
/* Features list */
.feat-list{list-style:none;columns:2;column-gap:24px}
.feat-item{padding:8px 0;font-size:.84rem;color:var(--d);display:flex;align-items:flex-start;gap:8px;break-inside:avoid}
.feat-item i{color:var(--a);margin-top:3px;font-size:.7rem;flex-shrink:0}
/* Description */
.desc-text{font-size:.88rem;color:var(--d);line-height:1.8;white-space:pre-line;overflow:hidden;word-break:break-word}
.desc-text img,.dtab-body img,.specs-table img{max-width:100%;height:auto}
/* Video */
.video-wrap{position:relative;padding-bottom:56.25%;height:0;border-radius:16px;overflow:hidden}
.video-wrap iframe{position:absolute;top:0;left:0;width:100%;height:100%;border:none}
/* Empty */
.empty{text-align:center;padding:50px 20px;color:var(--g)}
.empty i{font-size:2.5rem;opacity:.3;margin-bottom:14px;display:block}
.empty h3{font-size:1rem;font-weight:700;color:var(--d);margin-bottom:6px}
.reset-btn{margin-top:14px;padding:9px 22px;background:var(--p);color:#fff;border:none;border-radius:9px;cursor:pointer;font-weight:600;font-size:.82rem;font-family:inherit}
/* MOBILE SIDEBAR */
.msb{display:none;position:fixed;inset:0;background:#fff;z-index:150;overflow-y:auto;padding:18px;flex-direction:column}
.msb.open{display:flex}
.msb-h{display:flex;justify-content:space-between;align-items:center;margin-bottom:18px}
.msb-close{width:34px;height:34px;border:none;background:var(--gl);border-radius:9px;cursor:pointer;font-size:.95rem}
/* FOOTER */
.footer{background:var(--d);color:rgba(255,255,255,.65);padding:44px 0 20px;margin-top:36px}
.footer-grid{display:grid;grid-template-columns:1.5fr 1fr 1fr 1fr;gap:30px;margin-bottom:30px}
.footer h4{color:#fff;font-size:.78rem;font-weight:700;margin-bottom:14px;text-transform:uppercase;letter-spacing:1px}
.footer-links{list-style:none}.footer-links li{margin-bottom:7px}
.footer-links a{color:rgba(255,255,255,.5);font-size:.78rem;transition:var(--t)}.footer-links a:hover{color:var(--a)}
.footer-brand p{font-size:.78rem;line-height:1.6;margin:8px 0 14px}
.social-links{display:flex;gap:7px}
.social-links a{width:34px;height:34px;border-radius:8px;background:rgba(255,255,255,.07);display:flex;align-items:center;justify-content:center;color:rgba(255,255,255,.55);transition:var(--t);font-size:.8rem}
.social-links a:hover{background:var(--a);color:#fff}
.footer-bottom{border-top:1px solid rgba(255,255,255,.07);padding-top:16px;text-align:center;font-size:.7rem}
/* RESPONSIVE */
@media(max-width:1024px){.sidebar{width:230px}.pg{grid-template-columns:repeat(auto-fill,minmax(200px,1fr))}.detail-grid{grid-template-columns:1fr;gap:24px}.feat-list{columns:1}}
@media(max-width:768px){
.sidebar{display:none}.hamburger{display:block}
.hero h1{font-size:1.6rem}.hero-stats{gap:20px}.hero-stat strong{font-size:1.2rem}
.search-box{max-width:none;order:3;flex-basis:100%}.header .container{flex-wrap:wrap}
.pg{grid-template-columns:repeat(2,1fr);gap:10px}.pi{height:150px}.pb{padding:10px}.pn{font-size:.75rem}.pp{font-size:.88rem}
.detail-grid{grid-template-columns:1fr;padding:16px}.detail-name{font-size:1.2rem}.detail-price{font-size:1.4rem}
.detail-info{word-break:break-word}
.dtab-h{padding:12px 18px;font-size:.8rem}
.footer-grid{grid-template-columns:1fr 1fr}
}
.fade{opacity:0;transform:translateY(14px);transition:opacity .35s,transform .35s}.fade.vis{opacity:1;transform:none}
@keyframes fadeUp{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}
/* ===== NAV TABS ===== */
.nav-tabs{display:flex;gap:2px;margin-left:16px}
.nav-tab{padding:8px 14px;border-radius:9px;font-size:.78rem;font-weight:600;cursor:pointer;transition:var(--t);color:var(--g);position:relative}
.nav-tab:hover{color:var(--p);background:rgba(0,63,98,.04)}
.nav-tab.active{color:var(--p);background:rgba(0,63,98,.08)}
/* ===== CART BADGE ===== */
.cart-badge{position:relative}
.cart-num{position:absolute;top:-4px;right:-4px;background:var(--a);color:#fff;font-size:.55rem;font-weight:800;width:16px;height:16px;border-radius:50%;display:flex;align-items:center;justify-content:center;border:2px solid #fff}
/* ===== PAGE SECTIONS ===== */
.page-section{display:none;padding:28px 0;min-height:60vh}
.page-section.active{display:block}
/* ===== CATALOGUE ===== */
.cat-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:20px;margin-top:24px}
.cat-card{background:#fff;border-radius:var(--r);overflow:hidden;box-shadow:0 2px 10px rgba(0,0,0,.06);transition:var(--t);cursor:pointer;border:2px solid transparent}
.cat-card:hover{border-color:var(--p);transform:translateY(-4px);box-shadow:0 8px 24px rgba(0,0,0,.1)}
.cat-card img{width:100%;height:200px;object-fit:cover}
.cat-card-body{padding:16px;text-align:center}
.cat-card-body h3{font-size:.95rem;font-weight:700;color:var(--d);margin-bottom:6px}
.cat-card-body p{font-size:.78rem;color:var(--g)}
.cat-iframe-wrap{margin-top:20px;background:#fff;border-radius:16px;overflow:hidden;box-shadow:0 2px 16px rgba(0,0,0,.06)}
.cat-iframe-wrap iframe{width:100%;height:80vh;border:none}
.cat-back{display:inline-flex;align-items:center;gap:8px;color:var(--p);font-weight:600;font-size:.85rem;cursor:pointer;padding:8px 0;margin-bottom:16px}
/* ===== CONTACT ===== */
.contact-grid{display:grid;grid-template-columns:1fr 1fr;gap:30px;margin-top:24px}
.contact-card{background:#fff;border-radius:var(--r);padding:28px;box-shadow:0 2px 10px rgba(0,0,0,.06)}
.contact-card h3{font-size:1.1rem;font-weight:700;color:var(--d);margin-bottom:20px;display:flex;align-items:center;gap:10px}
.contact-card h3 i{color:var(--a)}
.contact-info{list-style:none}
.contact-info li{display:flex;align-items:flex-start;gap:12px;padding:12px 0;border-bottom:1px solid var(--gl);font-size:.88rem;color:var(--d)}
.contact-info li:last-child{border:none}
.contact-info li i{color:var(--p);width:20px;text-align:center;margin-top:3px}
.form-group{margin-bottom:16px}
.form-group label{display:block;font-size:.82rem;font-weight:600;color:var(--d);margin-bottom:6px}
.form-input{width:100%;padding:10px 14px;border:2px solid var(--gl);border-radius:10px;font-size:.85rem;font-family:inherit;outline:none;transition:var(--t)}
.form-input:focus{border-color:var(--p);box-shadow:0 0 0 3px rgba(0,63,98,.08)}
textarea.form-input{height:120px;resize:vertical}
.form-btn{padding:12px 28px;background:var(--p);color:#fff;border:none;border-radius:10px;font-weight:700;font-size:.88rem;cursor:pointer;font-family:inherit;transition:var(--t);width:100%}
.form-btn:hover{background:var(--pl)}
.map-wrap{margin-top:20px;border-radius:12px;overflow:hidden;height:280px}
.map-wrap iframe{width:100%;height:100%;border:none}
/* ===== CART DRAWER ===== */
.cart-overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,.4);z-index:200;backdrop-filter:blur(3px)}
.cart-overlay.open{display:block}
.cart-drawer{position:fixed;top:0;right:0;width:400px;max-width:90vw;height:100vh;height:100dvh;background:#fff;z-index:201;display:flex;flex-direction:column;box-shadow:-4px 0 30px rgba(0,0,0,.15);transform:translateX(100%);transition:transform .35s ease;visibility:hidden}
.cart-drawer.open{transform:translateX(0);visibility:visible}
.cart-header{padding:20px;border-bottom:2px solid var(--gl);display:flex;align-items:center;justify-content:space-between}
.cart-header h3{font-size:1rem;font-weight:700;display:flex;align-items:center;gap:8px}
.cart-header h3 i{color:var(--a)}
.cart-close{width:34px;height:34px;border:none;background:var(--gl);border-radius:9px;cursor:pointer;font-size:.9rem}
.cart-items{flex:1;overflow-y:auto;padding:16px;min-height:0}
.cart-item{display:flex;gap:12px;padding:12px;background:var(--l);border-radius:12px;margin-bottom:10px}
.cart-item img{width:70px;height:70px;object-fit:contain;border-radius:8px;background:#fff;flex-shrink:0}
.cart-item-info{flex:1;min-width:0}
.cart-item-name{font-size:.8rem;font-weight:600;color:var(--d);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.cart-item-price{font-size:.88rem;font-weight:800;color:var(--p);margin-top:4px}
.cart-item-qty{display:flex;align-items:center;gap:8px;margin-top:6px}
.qty-btn{width:26px;height:26px;border:1px solid var(--gl);border-radius:6px;background:#fff;cursor:pointer;font-size:.8rem;display:flex;align-items:center;justify-content:center}
.qty-val{font-size:.82rem;font-weight:700;min-width:20px;text-align:center}
.cart-item-del{background:none;border:none;color:#dc3545;cursor:pointer;font-size:.85rem;margin-left:auto;padding:4px}
.cart-empty{text-align:center;padding:40px;color:var(--g)}
.cart-empty i{font-size:2.5rem;opacity:.3;display:block;margin-bottom:12px}
.cart-footer{padding:10px 14px;border-top:2px solid var(--gl);background:#fff;flex-shrink:0;max-height:none}
.cart-footer-btns{display:flex;gap:6px;margin-top:6px}
.cart-footer-btns .cart-checkout-btn{flex:1;padding:10px 6px;font-size:.78rem;border-radius:10px}
.cart-total{display:flex;justify-content:space-between;font-size:.92rem;font-weight:800;color:var(--d);margin-bottom:0}
.cart-checkout-btn{width:100%;padding:14px;background:var(--a);color:#fff;border:none;border-radius:12px;font-weight:700;font-size:.92rem;cursor:pointer;font-family:inherit;transition:var(--t);display:flex;align-items:center;justify-content:center;gap:8px}
.cart-checkout-btn:hover{background:var(--al);transform:translateY(-1px)}
/* ===== CHECKOUT MODAL ===== */
.checkout-overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:300;align-items:center;justify-content:center;padding:20px;backdrop-filter:blur(4px)}
.checkout-overlay.open{display:flex}
.checkout-modal{background:#fff;border-radius:20px;max-width:550px;width:100%;max-height:92vh;overflow-y:auto;padding:32px;position:relative}
.checkout-close{position:absolute;top:16px;right:16px;width:34px;height:34px;border:none;background:var(--gl);border-radius:9px;cursor:pointer;font-size:.9rem}
.checkout-title{font-size:1.3rem;font-weight:800;color:var(--d);margin-bottom:4px}
.checkout-sub{font-size:.82rem;color:var(--g);margin-bottom:24px}
.checkout-summary{background:var(--l);border-radius:12px;padding:16px;margin-bottom:20px}
.checkout-summary-item{display:flex;justify-content:space-between;font-size:.82rem;padding:4px 0;color:var(--d)}
.checkout-summary-item.total{font-weight:800;font-size:1rem;color:var(--p);border-top:2px solid var(--gl);padding-top:10px;margin-top:8px}
.qr-section{text-align:center;background:var(--l);border-radius:16px;padding:24px;margin:20px 0}
.qr-section img{max-width:260px;margin:0 auto 16px;border-radius:12px;border:3px solid var(--gl)}
.qr-bank-info{font-size:.82rem;color:var(--d);line-height:1.8}
.qr-bank-info strong{color:var(--p)}
.copy-btn{display:inline-flex;align-items:center;gap:4px;background:var(--p);color:#fff;border:none;padding:4px 10px;border-radius:6px;font-size:.7rem;cursor:pointer;font-family:inherit;margin-left:6px}
.checkout-note{font-size:.78rem;color:var(--g);text-align:center;margin-top:16px;line-height:1.6}
.checkout-done-btn{width:100%;padding:14px;background:var(--p);color:#fff;border:none;border-radius:12px;font-weight:700;font-size:.92rem;cursor:pointer;font-family:inherit;margin-top:16px}
/* ADD TO CART BTN (in detail) */
.add-cart-btn{display:inline-flex;align-items:center;gap:8px;padding:14px 28px;background:var(--a);color:#fff;border-radius:12px;font-weight:700;font-size:.88rem;transition:var(--t);border:none;cursor:pointer;font-family:inherit}
.add-cart-btn:hover{background:var(--al);transform:translateY(-2px);box-shadow:0 8px 20px rgba(219,152,21,.3)}
@media(max-width:768px){
.nav-tabs{display:none}
.contact-grid{grid-template-columns:1fr}
.cart-drawer{width:100%;max-width:100%;height:100vh;height:100dvh}
.cart-footer{padding:8px 12px}
.cart-footer-btns .cart-checkout-btn{padding:10px 4px;font-size:.76rem;gap:4px}
.cart-total{font-size:.88rem}
}
/* ===== AI CHATBOX ===== */
.chat-fab{position:fixed;bottom:24px;right:24px;z-index:180;width:56px;height:56px;border-radius:50%;background:linear-gradient(135deg,var(--p),#0077b6);color:#fff;border:none;cursor:pointer;box-shadow:0 4px 20px rgba(0,63,98,.4);display:flex;align-items:center;justify-content:center;font-size:1.4rem;transition:var(--t)}
.chat-fab:hover{transform:scale(1.1);box-shadow:0 6px 28px rgba(0,63,98,.5)}
.chat-fab-label{position:fixed;bottom:32px;right:90px;z-index:180;background:var(--p);color:#fff;padding:6px 14px;border-radius:8px;font-size:.75rem;font-weight:600;white-space:nowrap;box-shadow:0 2px 8px rgba(0,0,0,.15);pointer-events:none;opacity:0;transition:opacity .3s}
.chat-fab:hover+.chat-fab-label{opacity:1}
.chat-overlay2{display:none;position:fixed;inset:0;background:rgba(0,0,0,.3);z-index:190;backdrop-filter:blur(2px)}
.chat-overlay2.open{display:block}
.chatbox{position:fixed;bottom:0;right:0;width:400px;max-width:100vw;height:560px;max-height:calc(100vh - 48px);background:#fff;border-radius:20px;box-shadow:0 8px 40px rgba(0,0,0,.18);z-index:200;display:none;flex-direction:column;overflow:hidden}
.chatbox.open{display:flex}
.chat-head{background:linear-gradient(135deg,var(--p),#0077b6);color:#fff;padding:14px 16px;padding-top:max(14px, env(safe-area-inset-top, 14px));display:flex;align-items:center;gap:12px;flex-shrink:0}
.chat-head-avatar{width:38px;height:38px;border-radius:12px;background:rgba(255,255,255,.15);display:flex;align-items:center;justify-content:center;font-size:1.1rem;flex-shrink:0}
.chat-head-info h4{font-size:.9rem;font-weight:700}.chat-head-info span{font-size:.68rem;opacity:.75}
.chat-close{margin-left:auto;width:40px;height:40px;min-width:40px;min-height:40px;border-radius:50%;background:rgba(255,255,255,.25);border:none;color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:1.1rem;transition:var(--t)}
.chat-close:hover{background:rgba(255,255,255,.4)}
.chat-body{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:10px;background:#f8fafc}
.chat-msg{max-width:88%;padding:11px 15px;border-radius:16px;font-size:.84rem;line-height:1.55;word-wrap:break-word}
.chat-msg.bot{background:#fff;color:var(--d);align-self:flex-start;border:1px solid var(--gl);border-bottom-left-radius:4px}
.chat-msg.user{background:var(--p);color:#fff;align-self:flex-end;border-bottom-right-radius:4px}
.chat-msg a{color:var(--a);font-weight:600;text-decoration:underline}
.chat-msg.bot a{color:var(--p)}
.chat-typing{display:flex;gap:4px;padding:11px 15px;align-self:flex-start;background:#fff;border-radius:16px;border:1px solid var(--gl);border-bottom-left-radius:4px}
.chat-typing span{width:7px;height:7px;background:var(--g);border-radius:50%;animation:blink 1.4s infinite}
.chat-typing span:nth-child(2){animation-delay:.2s}
.chat-typing span:nth-child(3){animation-delay:.4s}
@keyframes blink{0%,80%,100%{opacity:.3}40%{opacity:1}}
.chat-suggestions{display:flex;flex-wrap:wrap;gap:6px;padding:0 16px 10px}
.chat-sug{padding:6px 12px;background:#fff;border:1.5px solid var(--gl);border-radius:20px;font-size:.75rem;font-weight:500;color:var(--p);cursor:pointer;transition:var(--t);white-space:nowrap}
.chat-sug:hover{border-color:var(--p);background:rgba(0,63,98,.04)}
.chat-foot{padding:10px 14px;padding-bottom:max(10px, env(safe-area-inset-bottom, 10px));border-top:1.5px solid var(--gl);display:flex;gap:8px;background:#fff;flex-shrink:0}
.chat-input{flex:1;padding:10px 14px;border:1.5px solid var(--gl);border-radius:12px;font-size:.85rem;font-family:inherit;outline:none;resize:none;max-height:80px;transition:var(--t)}
.chat-input:focus{border-color:var(--p)}
.chat-send{width:40px;height:40px;border-radius:10px;background:var(--p);color:#fff;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:.9rem;transition:var(--t);flex-shrink:0}
.chat-send:hover{background:var(--pl)}
.chat-send:disabled{opacity:.4;cursor:default}
/* ===== SHARE POPOVER ===== */
.share-popover-overlay{display:none;position:fixed;inset:0;z-index:240;background:rgba(0,0,0,.35);backdrop-filter:blur(2px)}
.share-popover-overlay.open{display:flex;align-items:center;justify-content:center}
.share-popover{background:#fff;border-radius:18px;padding:24px;box-shadow:0 8px 40px rgba(0,0,0,.18);max-width:340px;width:calc(100vw - 40px)}
.share-popover h4{font-size:.92rem;font-weight:700;color:var(--d);margin-bottom:16px;display:flex;align-items:center;gap:8px}
.share-popover h4 i{color:var(--a)}
.share-close-btn{position:absolute;top:14px;right:14px;width:30px;height:30px;border:none;background:var(--gl);border-radius:8px;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:.8rem}
.share-apps{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-bottom:16px}
.share-app{display:flex;flex-direction:column;align-items:center;gap:6px;cursor:pointer;transition:var(--t);padding:8px;border-radius:12px}
.share-app:hover{background:var(--l)}
.share-app-icon{width:44px;height:44px;border-radius:12px;display:flex;align-items:center;justify-content:center;font-size:1.15rem;color:#fff}
.share-app span{font-size:.65rem;font-weight:600;color:var(--g)}
.share-link-box{display:flex;gap:6px}
.share-link-input{flex:1;padding:9px 10px;border:1.5px solid var(--gl);border-radius:8px;font-size:.76rem;font-family:inherit;outline:none;background:var(--l);color:var(--d)}
.share-link-copy{padding:9px 14px;background:var(--p);color:#fff;border:none;border-radius:8px;font-size:.76rem;font-weight:700;cursor:pointer;font-family:inherit;white-space:nowrap;transition:var(--t)}
.share-link-copy:hover{background:var(--pl)}
@media(max-width:768px){
.chatbox{bottom:0;right:0;width:100%;max-width:100%;height:100%;max-height:100vh;max-height:100dvh;border-radius:0;padding-top:0}
.chatbox .chat-head{padding-top:max(14px, env(safe-area-inset-top, 14px))}
.chat-fab{bottom:16px;right:16px;width:50px;height:50px;font-size:1.2rem}
.chat-fab-label{display:none}
}
/* ===== QUOTATION MODAL ===== */
.quote-overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:300;align-items:center;justify-content:center;padding:16px;backdrop-filter:blur(4px)}
.quote-overlay.open{display:flex}
.quote-modal{background:#fff;border-radius:20px;max-width:960px;width:100%;max-height:92vh;overflow-y:auto;padding:0;position:relative}
.quote-head{background:linear-gradient(135deg,var(--p),#0077b6);color:#fff;padding:24px 28px;border-radius:20px 20px 0 0}
.quote-head h2{font-size:1.2rem;font-weight:800;margin-bottom:4px}
.quote-head p{font-size:.8rem;opacity:.75}
.quote-close{position:absolute;top:16px;right:16px;width:34px;height:34px;border:none;background:rgba(255,255,255,.15);border-radius:9px;cursor:pointer;color:#fff;font-size:.9rem;display:flex;align-items:center;justify-content:center}
.quote-body{padding:24px 28px}
.quote-section{margin-bottom:20px}
.quote-section h3{font-size:.88rem;font-weight:700;color:var(--d);margin-bottom:12px;display:flex;align-items:center;gap:8px}
.quote-section h3 i{color:var(--a);font-size:.8rem}
.quote-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.quote-field label{display:block;font-size:.76rem;font-weight:600;color:var(--g);margin-bottom:4px}
.quote-field input{width:100%;padding:9px 12px;border:1.5px solid var(--gl);border-radius:8px;font-size:.84rem;font-family:inherit;outline:none}
.quote-field input:focus{border-color:var(--p)}
.quote-table{width:100%;border-collapse:collapse;font-size:.78rem;margin-top:8px}
.quote-table th{background:var(--p);color:#fff;padding:8px 6px;font-weight:600;text-align:center;white-space:nowrap}
.quote-table td{padding:8px 6px;border-bottom:1px solid var(--gl);vertical-align:middle}
.quote-table .qt-img{width:72px;height:72px;object-fit:contain;border-radius:6px;background:var(--l);border:1px solid var(--gl)}
.quote-table .qt-name{font-weight:600;color:var(--d)}
.quote-table .qt-price{text-align:right;white-space:nowrap}
.quote-table .qt-disc{width:80px;padding:5px 6px;border:1.5px solid var(--gl);border-radius:6px;font-size:.78rem;text-align:right;font-family:inherit;outline:none}
.quote-table .qt-disc:focus{border-color:var(--p)}
.quote-table .qt-note{width:100%;padding:5px 6px;border:1.5px solid var(--gl);border-radius:6px;font-size:.78rem;font-family:inherit;outline:none}
.quote-total-row td{font-weight:800;font-size:.88rem;color:var(--p);border-top:2px solid var(--p)}
.quote-actions{display:flex;gap:10px;margin-top:20px;flex-wrap:wrap;justify-content:center}
.quote-btn{padding:12px 24px;border:none;border-radius:10px;font-weight:700;font-size:.85rem;cursor:pointer;font-family:inherit;display:flex;align-items:center;gap:8px;transition:var(--t)}
.quote-btn-excel{background:#217346;color:#fff}.quote-btn-excel:hover{background:#1a5c38}
.quote-btn-pdf{background:#dc3545;color:#fff}.quote-btn-pdf:hover{background:#b02a37}
.quote-btn-print{background:var(--p);color:#fff}.quote-btn-print:hover{background:var(--pl)}
@media(max-width:768px){.quote-grid{grid-template-columns:1fr}.quote-modal{border-radius:12px}.quote-table{font-size:.7rem}}
.checkout-done-btn{display:none !important}.vas-unlocked .checkout-done-btn{display:flex !important}
.chat-msg.bot{max-height:58vh;overflow-y:auto;overflow-x:hidden;scrollbar-width:thin}.chat-msg.bot::-webkit-scrollbar{width:5px}.chat-msg.bot::-webkit-scrollbar-thumb{background:#94a3b8;border-radius:8px}
</style>
<!-- Libraries for quotation export -->
<script src="https://cdn.jsdelivr.net/npm/exceljs@4.4.0/dist/exceljs.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
</head>
<body>
<div class="topbar"><div class="container">
<div class="topbar-item"><i class="fas fa-truck-fast"></i> Miễn phí vận chuyển</div>
<div class="topbar-item"><i class="fas fa-shield-halved"></i> Bảo hành chính hãng</div>
<div class="topbar-item"><i class="fas fa-headset"></i> Hỗ trợ 24/7</div>
</div></div>
<header class="header"><div class="container">
<div class="logo" onclick="goHome()"><img src="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/logo_200.png" alt="V.AI STUDIO" style="height:44px;object-fit:contain"><div><div class="logo-name">V.AI <span>STUDIO</span></div><div class="logo-sub">Niềm tin khách hàng là tài sản của chúng tôi</div></div></div>
<div class="search-box"><i class="fas fa-search"></i><input type="text" id="q" placeholder="Tìm sản phẩm Malloca, Eurogold, Grob, Canzy & Demax..." oninput="doSearch()"><span class="search-count" id="sc"></span></div>
<div class="nav-tabs">
<div class="nav-tab active" data-page="products" onclick="showPage('products')"><i class="fas fa-th-large"></i> Sản Phẩm</div>
<div class="nav-tab" data-page="catalogue" onclick="showPage('catalogue')"><i class="fas fa-book-open"></i> Catalogue</div>
<div class="nav-tab" data-page="contact" onclick="showPage('contact')"><i class="fas fa-envelope"></i> Liên Hệ</div>
</div>
<div class="nav-icons">
<a href="#" onclick="toggleChat();return false" title="Tư Vấn AI" style="background:linear-gradient(135deg,var(--p),#0077b6);color:#fff"><i class="fas fa-robot"></i></a>
<a href="#" onclick="openCart();return false" title="Giỏ hàng" class="cart-badge"><i class="fas fa-shopping-cart"></i><span class="cart-num" id="cartNum" style="display:none">0</span></a>
</div>
<button class="hamburger" onclick="document.getElementById('msb').classList.toggle('open')"><span></span><span></span><span></span></button>
</div></header>
<section class="hero"><div class="container">
<img src="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/logo_600.png" alt="V.AI STUDIO" style="height:80px;object-fit:contain;margin-bottom:12px">
<h1>V.AI <span>STUDIO</span></h1>
<p style="font-size:1rem;font-style:italic;color:rgba(255,255,255,.9);margin-bottom:6px">❝ Niềm tin khách hàng là tài sản của chúng tôi ❞</p>
<p>Thiết bị nhà bếp & khóa thông minh — Malloca, Eurogold, Grob, Canzy & Demax</p>
<div class="hero-stats" id="heroStats">
<div class="hero-stat"><strong id="statTotal">1979</strong><span>Sản phẩm</span></div>
<div class="hero-stat"><strong id="statCats">17</strong><span>Danh mục</span></div>
<div class="hero-stat"><strong id="statBrands">8</strong><span>Đối tác</span></div>
<div class="hero-stat"><strong>100%</strong><span>Dữ liệu thật</span></div>
</div>
</div></section>
<!-- AI SEARCH + QUOTE -->
<!-- AI SEARCH -->
<div style="background:#fff;padding:16px 0;border-bottom:1px solid #e2e8f0">
<div class="container" style="display:flex;gap:12px;flex-wrap:wrap;justify-content:center;max-width:900px">
<div style="flex:1;min-width:280px;position:relative">
<i class="fas fa-robot" style="position:absolute;left:12px;top:50%;transform:translateY(-50%);color:#003f62;font-size:.85rem"></i>
<input type="text" id="aiSearch" placeholder="🔍 AI tìm kiếm: bếp từ đôi dưới 15 triệu, máy hút mùi tốt nhất..." style="width:100%;padding:11px 14px 11px 36px;border:2px solid #003f62;border-radius:10px;font-size:.85rem;font-family:inherit;outline:none" onkeypress="if(event.key==='Enter')doAISearch()">
</div>
<button onclick="doAISearch()" style="padding:11px 20px;background:#003f62;color:#fff;border:none;border-radius:10px;font-weight:700;font-size:.85rem;cursor:pointer;white-space:nowrap"><i class="fas fa-search"></i> Tìm AI</button>
</div>
<div id="aiResults" style="max-width:900px;margin:12px auto 0;display:none;background:#f8fafc;border-radius:10px;padding:14px;font-size:.82rem"></div>
</div>
<!-- Partner Logos -->
<div style="background:#fff;padding:14px 0;border-bottom:1px solid var(--gl)">
<div class="container" style="display:flex;align-items:center;justify-content:center;gap:20px;flex-wrap:wrap">
<span style="font-size:.68rem;font-weight:700;color:var(--g);text-transform:uppercase;letter-spacing:1px;white-space:nowrap">Đối tác:</span>
<a href="javascript:void(0)" onclick="setBrand('Malloca')" style="display:flex;align-items:center"><img src="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/partners/malloca.png" alt="Malloca" style="height:28px;object-fit:contain;opacity:.75;transition:.3s" onmouseover="this.style.opacity='1'" onmouseout="this.style.opacity='.75'"></a>
<a href="javascript:void(0)" onclick="setBrand('Eurogold')" style="display:flex;align-items:center"><img src="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/partners/eurogold.png" alt="Eurogold" style="height:26px;object-fit:contain;opacity:.75;transition:.3s" onmouseover="this.style.opacity='1'" onmouseout="this.style.opacity='.75'"></a>
<a href="javascript:void(0)" onclick="setBrand('Grob')" style="display:flex;align-items:center"><img src="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/partners/grob.webp" alt="Grob" style="height:24px;object-fit:contain;opacity:.75;transition:.3s" onmouseover="this.style.opacity='1'" onmouseout="this.style.opacity='.75'"></a>
<a href="javascript:void(0)" onclick="setBrand('Canzy')" style="display:flex;align-items:center"><img src="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/partners/canzy.png" alt="Canzy" style="height:26px;object-fit:contain;opacity:.75;transition:.3s" onmouseover="this.style.opacity='1'" onmouseout="this.style.opacity='.75'"></a>
<a href="javascript:void(0)" onclick="setBrand('Demax')" style="display:flex;align-items:center"><img src="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/partners/demax.png" alt="Demax" style="height:22px;object-fit:contain;opacity:.75;transition:.3s" onmouseover="this.style.opacity='1'" onmouseout="this.style.opacity='.75'"></a>
<a href="javascript:void(0)" onclick="setBrand('Hafele')" style="display:flex;align-items:center"><img src="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/partners/hafele.png" alt="Hafele" style="height:22px;object-fit:contain;opacity:.75;transition:.3s" onmouseover="this.style.opacity='1'" onmouseout="this.style.opacity='.75'"></a>
<a href="javascript:void(0)" onclick="setBrand('Garis')" style="display:flex;align-items:center"><img src="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/partners/garis.png" alt="Garis" style="height:24px;object-fit:contain;opacity:.75;transition:.3s" onmouseover="this.style.opacity='1'" onmouseout="this.style.opacity='.75'"></a>
<a href="javascript:void(0)" onclick="setBrand('Boss')" style="display:flex;align-items:center"><span style="font-size:.72rem;color:#333;font-weight:700;background:#f0f0f0;padding:3px 8px;border-radius:4px;opacity:.75;transition:.3s" onmouseover="this.style.opacity='1'" onmouseout="this.style.opacity='.75'">BOSS</span></a>
<a href="javascript:void(0)" onclick="setBrand('_dmx')" style="display:flex;align-items:center;gap:6px">
<img src="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/partners/dmx.png" alt="Điện Máy Xanh" style="height:24px;object-fit:contain;opacity:.75;transition:.3s" onmouseover="this.style.opacity='1'" onmouseout="this.style.opacity='.75'">
<span style="font-size:.56rem;color:#e53935;font-weight:700;background:#fff3f3;padding:2px 6px;border-radius:4px;white-space:nowrap">Cam kết giá rẻ hơn ĐMX</span>
</a>
</div>
</div>
<div class="container">
<!-- LIST VIEW -->
<div id="page-products" class="page-section active"><div id="listView">
<div class="main">
<aside class="sidebar" id="sidebar">
<div class="sb-card"><h3><i class="fas fa-building"></i> Thương Hiệu</h3><div class="pr" id="br"></div></div>
<div class="sb-card"><h3><i class="fas fa-layer-group"></i> Danh Mục</h3><ul class="cat-list" id="cl"></ul></div>
<div class="sb-card"><h3><i class="fas fa-tag"></i> Khoảng Giá</h3><div class="pr" id="pr"></div></div>
</aside>
<main class="content">
<div class="toolbar">
<span class="result-count" id="rc"></span>
<div style="display:flex;gap:7px;align-items:center">
<select class="sort-select" id="ss" onchange="doSort()"><option value="default">Mặc định</option><option value="pa">Giá: Thấp → Cao</option><option value="pd">Giá: Cao → Thấp</option><option value="na">Tên: A → Z</option></select>
<div class="view-btns"><button class="view-btn active" id="gb" onclick="setV('g')"><i class="fas fa-th-large"></i></button><button class="view-btn" id="lb" onclick="setV('l')"><i class="fas fa-list"></i></button></div>
</div>
</div>
<div class="pg" id="grid"></div>
<div class="pagination" id="pag"></div>
<div id="catSearchResults"></div>
</main>
</div>
</div>
<div id="detailView"></div>
</div><!-- /page-products -->
<!-- CATALOGUE PAGE -->
<div class="page-section" id="page-catalogue">
<div class="section-header" style="text-align:center;margin-bottom:10px">
<h2 style="font-size:1.8rem;font-weight:800;color:var(--d)"><i class="fas fa-book-open" style="color:var(--a);margin-right:10px"></i>Catalogue <span style="color:var(--a)">Sản phẩm</span></h2>
<p style="color:var(--g);font-size:.9rem;max-width:550px;margin:10px auto 0">10 bộ catalogue — tìm kiếm theo văn bản trong ảnh</p>
<div style="max-width:480px;margin:16px auto 0;position:relative">
<i class="fas fa-search" style="position:absolute;left:14px;top:50%;transform:translateY(-50%);color:var(--g);font-size:.85rem"></i>
<input type="text" id="catSearch" placeholder="Tìm trong catalogue... (tên SP, mã SP, thông số...)" oninput="searchCatalogue()" style="width:100%;padding:10px 14px 10px 40px;border:2px solid var(--gl);border-radius:12px;font-size:.85rem;font-family:inherit;outline:none">
<span id="catSearchCount" style="position:absolute;right:12px;top:50%;transform:translateY(-50%);font-size:.68rem;color:var(--g);background:var(--l);padding:2px 7px;border-radius:6px"></span>
</div>
</div>
<!-- Tab selector -->
<div id="catTabs"></div>
<!-- Catalogue content renders here -->
<div id="catContent"></div>
</div>
<!-- CONTACT PAGE -->
<div class="page-section" id="page-contact">
<div class="section-header" style="text-align:center;margin-bottom:10px">
<h2 style="font-size:1.8rem;font-weight:800;color:var(--d)"><i class="fas fa-envelope" style="color:var(--a);margin-right:10px"></i>Liên Hệ <span style="color:var(--a)">Malloca</span></h2>
<p style="color:var(--g);font-size:.9rem;max-width:500px;margin:10px auto 0">Liên hệ trực tiếp hoặc gửi tin nhắn — chúng tôi sẵn sàng hỗ trợ bạn</p>
</div>
<div class="contact-grid">
<div class="contact-card">
<h3><i class="fas fa-building"></i> Thông Tin Liên Hệ</h3>
<ul class="contact-info">
<li><i class="fas fa-map-marker-alt"></i><div><strong>Showroom Hà Nội</strong><br>10 Chương Dương Độ, Hoàn Kiếm, Hà Nội</div></li>
<li><i class="fas fa-comment-dots"></i><div><strong>Zalo tư vấn</strong><br><a href="https://zalo.me/0981873395" target="_blank" style="color:var(--p);font-weight:700">0981 873 395</a> · <a href="https://zalo.me/0918258385" target="_blank" style="color:var(--p);font-weight:700">0918 258 385</a></div></li>
<li><i class="fas fa-qrcode"></i><div><strong>QR Kết bạn Zalo</strong><br><img src="https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=https://zalo.me/0981873395" alt="QR Zalo 0981873395" style="width:120px;border-radius:8px;margin-top:6px;border:2px solid var(--gl)"><br><span style="font-size:.72rem;color:var(--g)">Quét mã để kết bạn Zalo</span></div></li>
<li><i class="fas fa-envelope"></i><div><strong>Email</strong><br><a href="mailto:info@malloca.com" style="color:var(--p)">info@malloca.com</a></div></li>
<li><i class="fas fa-clock"></i><div><strong>Giờ làm việc</strong><br>Thứ 2 - Thứ 7: 8:00 - 17:30<br>Chủ nhật: 9:00 - 12:00</div></li>
<li><i class="fas fa-robot"></i><div><strong>Tư Vấn AI</strong><br><a href="javascript:void(0)" onclick="toggleChat()" style="color:var(--p);font-weight:700">Chat ngay →</a></div></li>
</ul>
<div style="display:flex;gap:8px;margin-top:16px">
<a href="https://zalo.me/0981873395" target="_blank" style="padding:10px 16px;background:#0068FF;color:#fff;border-radius:10px;font-size:.8rem;font-weight:600;display:flex;align-items:center;gap:6px"><i class="fas fa-comment-dots"></i> Zalo 1</a>
<a href="https://zalo.me/0918258385" target="_blank" style="padding:10px 16px;background:#0068FF;color:#fff;border-radius:10px;font-size:.8rem;font-weight:600;display:flex;align-items:center;gap:6px"><i class="fas fa-comment-dots"></i> Zalo 2</a>
</div>
</div>
<div class="contact-card">
<h3><i class="fas fa-paper-plane"></i> Gửi Tin Nhắn</h3>
<form onsubmit="submitContact(event)">
<div class="form-group"><label>Họ tên *</label><input class="form-input" id="ctName" placeholder="Nhập họ tên" required></div>
<div class="form-group"><label>Email *</label><input class="form-input" id="ctEmail" type="email" placeholder="email@example.com" required></div>
<div class="form-group"><label>Số điện thoại</label><input class="form-input" id="ctPhone" placeholder="Nhập SĐT"></div>
<div class="form-group"><label>Nội dung *</label><textarea class="form-input" id="ctMsg" placeholder="Nhập nội dung liên hệ..." required></textarea></div>
<button type="submit" class="form-btn"><i class="fas fa-paper-plane"></i> Gửi Tin Nhắn</button>
</form>
</div>
</div>
</div>
</div>
<!-- MOBILE SIDEBAR -->
<div class="msb" id="msb">
<div class="msb-h"><img src="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/logo_200.png" alt="V.AI STUDIO" style="height:36px;object-fit:contain"><button class="msb-close" onclick="this.closest('.msb').classList.remove('open')"><i class="fas fa-times"></i></button></div>
<div style="display:flex;flex-direction:column;gap:4px;margin-bottom:16px;padding-bottom:16px;border-bottom:2px solid var(--gl)">
<div style="padding:12px;border-radius:10px;font-weight:600;cursor:pointer;font-size:.9rem" onclick="closeMsb();showPage('products')"><i class="fas fa-th-large" style="margin-right:8px;color:var(--a)"></i> Sản Phẩm</div>
<div style="padding:12px;border-radius:10px;font-weight:600;cursor:pointer;font-size:.9rem" onclick="closeMsb();showPage('catalogue')"><i class="fas fa-book-open" style="margin-right:8px;color:var(--a)"></i> Catalogue</div>
<div style="padding:12px;border-radius:10px;font-weight:600;cursor:pointer;font-size:.9rem" onclick="closeMsb();showPage('contact')"><i class="fas fa-envelope" style="margin-right:8px;color:var(--a)"></i> Liên Hệ</div>
<div style="padding:12px;border-radius:10px;font-weight:600;cursor:pointer;font-size:.9rem" onclick="closeMsb();openCart()"><i class="fas fa-shopping-cart" style="margin-right:8px;color:var(--a)"></i> Giỏ Hàng</div>
</div>
<div class="sb-card"><h3><i class="fas fa-building"></i> Thương Hiệu</h3><div class="pr" id="brm"></div></div>
<div class="sb-card"><h3><i class="fas fa-layer-group"></i> Danh Mục</h3><ul class="cat-list" id="clm"></ul></div>
<div class="sb-card"><h3><i class="fas fa-tag"></i> Khoảng Giá</h3><div class="pr" id="prm"></div></div>
</div>
<!-- CART DRAWER -->
<div class="cart-overlay" id="cartOverlay" onclick="closeCart()"></div>
<div class="cart-drawer" id="cartDrawer">
<div class="cart-header"><h3><i class="fas fa-shopping-cart"></i> Giỏ Hàng</h3><button class="cart-close" onclick="closeCart()"><i class="fas fa-times"></i></button></div>
<div class="cart-items" id="cartItems"></div>
<div class="cart-footer" id="cartFooter" style="display:none">
<div class="cart-total"><span>Tổng cộng:</span><span id="cartTotal"></span></div>
<div class="cart-footer-btns">
<button class="cart-checkout-btn" onclick="openCheckout()"><i class="fas fa-qrcode"></i> Thanh toán</button>
<button class="cart-checkout-btn" onclick="openQuotation()" style="background:var(--p)"><i class="fas fa-file-invoice"></i> Báo giá</button>
</div>
</div>
</div>
<!-- CHECKOUT MODAL -->
<div class="checkout-overlay" id="checkoutOverlay" onclick="if(event.target===this)closeCheckout()">
<div class="checkout-modal">
<button class="checkout-close" onclick="closeCheckout()"><i class="fas fa-times"></i></button>
<div id="checkoutContent"></div>
</div>
</div>
<!-- QUOTATION MODAL -->
<div class="quote-overlay" id="quoteOverlay" onclick="if(event.target===this)closeQuotation()">
<div class="quote-modal">
<button class="quote-close" onclick="closeQuotation()"><i class="fas fa-times"></i></button>
<div class="quote-head">
<h2><i class="fas fa-file-invoice" style="margin-right:8px"></i>Lập Báo Giá</h2>
<p>Điền thông tin khách hàng và chiết khấu để xuất báo giá</p>
</div>
<div class="quote-body">
<div class="quote-section">
<h3><i class="fas fa-user"></i> Thông tin khách hàng</h3>
<div class="quote-grid">
<div class="quote-field"><label>Họ tên khách hàng</label><input id="qcName" placeholder="Nhập họ tên"></div>
<div class="quote-field"><label>Số điện thoại</label><input id="qcPhone" placeholder="Nhập SĐT"></div>
<div class="quote-field"><label>Email</label><input id="qcEmail" placeholder="Nhập email"></div>
<div class="quote-field"><label>Địa chỉ</label><input id="qcAddr" placeholder="Nhập địa chỉ"></div>
<div class="quote-field"><label>Mã đơn hàng</label><input id="qcCompany" placeholder="Tự động tạo" readonly style="background:var(--l);font-weight:700;color:var(--p)"></div>
<div class="quote-field"><label>Ngày báo giá</label><input id="qcDate" type="date"></div>
</div>
<div style="margin-top:12px;padding:12px 14px;background:var(--l);border-radius:10px">
<div style="display:flex;align-items:center;gap:10px;margin-bottom:8px">
<i class="fas fa-lock" id="qcLockIcon" style="color:var(--g);font-size:.85rem"></i>
<input id="qcAccessCode" type="password" placeholder="Nhập mã truy cập..." style="width:160px;padding:7px 10px;border:1.5px solid var(--gl);border-radius:8px;font-size:.8rem;font-family:inherit;outline:none;background:#fff" oninput="checkQuoteAccess()">
<span id="qcAccessStatus" style="font-size:.72rem;font-weight:600;color:var(--g)">🔒</span>
</div>
<div id="aiPromptBox" style="display:none">
<div style="display:flex;gap:8px">
<textarea id="qcAiPrompt" placeholder="VD: Malloca ck 35%, giao hàng 200k, lắp đặt 500k, cọc 5tr, ghi chú: giao thứ 7" style="flex:1;padding:8px 10px;border:1.5px solid var(--p);border-radius:8px;font-size:.82rem;font-family:inherit;outline:none;resize:none;height:48px;background:#fff"></textarea>
<button onclick="applyAiDiscount()" style="padding:8px 16px;background:var(--p);color:#fff;border:none;border-radius:8px;font-size:.8rem;font-weight:700;cursor:pointer;font-family:inherit;white-space:nowrap;display:flex;align-items:center;gap:6px" id="aiDiscBtn"><i class="fas fa-robot"></i> Áp dụng</button>
</div>
<div id="aiDiscStatus" style="font-size:.72rem;color:var(--g);margin-top:4px"></div>
</div>
</div>
</div>
<div class="quote-section">
<h3><i class="fas fa-list-check"></i> Chi tiết sản phẩm</h3>
<div style="overflow-x:auto">
<table class="quote-table" id="quoteTable">
<thead><tr>
<th>STT</th><th>Hình ảnh</th><th style="text-align:left">Tên sản phẩm</th><th>Mã SP</th><th>Thông tin</th><th>SL</th><th>Đơn giá</th><th>Đơn giá CK</th><th>Thành tiền</th><th>Ghi chú</th>
</tr></thead>
<tbody id="quoteTableBody"></tbody>
<tfoot><tr class="quote-total-row"><td colspan="8" style="text-align:right;padding-right:12px">TỔNG CỘNG:</td><td class="qt-price" id="quoteTotalCell"></td><td></td></tr></tfoot>
</table>
</div>
</div>
<div class="quote-actions">
<button class="quote-btn" onclick="shareQuoteImage()" style="background:#25D366"><i class="fas fa-share-alt"></i> Chia sẻ ảnh</button>
<button class="quote-btn" onclick="shareQuoteLink()" style="background:#0068FF"><i class="fas fa-share-alt"></i> Chia sẻ báo giá</button>
<button class="quote-btn quote-btn-excel" onclick="exportExcel()"><i class="fas fa-file-excel"></i> Xuất Excel</button>
<button class="quote-btn quote-btn-pdf" onclick="exportPDF()"><i class="fas fa-file-pdf"></i> Xuất PDF</button>
<button class="quote-btn quote-btn-print" onclick="printQuotation()"><i class="fas fa-print"></i> In báo giá</button>
</div>
</div>
</div>
</div>
<footer class="footer"><div class="container">
<div class="footer-grid">
<div class="footer-brand"><img src="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/logo_200.png" alt="V.AI STUDIO" style="height:50px;object-fit:contain;margin-bottom:8px;filter:brightness(10)"><div class="logo-name" style="color:#fff;font-size:1.1rem">V.AI <span style="color:var(--a)">STUDIO</span></div><p style="font-style:italic;opacity:.85;margin-bottom:4px">❝ Niềm tin khách hàng là tài sản của chúng tôi ❞</p><p>Thiết bị nhà bếp & khóa thông minh — Malloca, Eurogold, Grob, Canzy & Demax.</p>
<div class="social-links"><a href="https://zalo.me/0981873395" target="_blank" title="Zalo"><i class="fas fa-comment-dots"></i></a><a href="https://zalo.me/0918258385" target="_blank" title="Zalo 2"><i class="fas fa-comments"></i></a></div></div>
<div><h4>Thiết Bị Nhà Bếp</h4><ul class="footer-links"><li><a href="javascript:void(0)" onclick="setCat('may-hut-khoi-khu-mui')">Máy hút khói khử mùi</a></li><li><a href="javascript:void(0)" onclick="setCat('bep-dien-tu-bep-gas')">Bếp điện từ, bếp gas</a></li><li><a href="javascript:void(0)" onclick="setCat('lo-nuong')">Lò nướng, lò hấp</a></li><li><a href="javascript:void(0)" onclick="setCat('chau-rua-chen')">Chậu rửa chén</a></li><li><a href="javascript:void(0)" onclick="setCat('voi-rua-chen')">Vòi rửa chén</a></li></ul></div>
<div><h4>Thêm</h4><ul class="footer-links"><li><a href="javascript:void(0)" onclick="setCat('tu-lanh-tu-ruou')">Tủ lạnh, tủ rượu</a></li><li><a href="javascript:void(0)" onclick="setCat('may-rua-chen-may-say-chen')">Máy rửa chén</a></li><li><a href="javascript:void(0)" onclick="setCat('thiet-bi-gia-dung-nho')">Gia dụng nhỏ</a></li><li><a href="javascript:void(0)" onclick="showPage('catalogue')">Catalogue</a></li><li><a href="javascript:void(0)" onclick="toggleChat()">🤖 Tư Vấn AI</a></li></ul></div>
<div><h4>Hỗ Trợ</h4><ul class="footer-links"><li><a href="javascript:void(0)" onclick="toggleChat()">Tư vấn sản phẩm</a></li><li><a href="javascript:void(0)" onclick="showPage('contact')">Liên hệ</a></li><li><a href="https://zalo.me/0981873395" target="_blank">Zalo: 0981 873 395</a></li><li><a href="javascript:void(0)" onclick="showPage('catalogue')">Xem catalogue</a></li><li><a href="javascript:void(0)" onclick="goHome()">Trang chủ</a></li></ul></div>
</div>
<div class="footer-bottom"><p>&copy; 2026 V.AI STUDIO &bull; Thiết bị nhà bếp & khóa thông minh — Malloca, Eurogold, Grob, Canzy, Demax, Hafele, Garis & Điện Máy Xanh</p><p style="margin-top:6px;font-size:.65rem;color:rgba(255,255,255,.45)">Tất cả sản phẩm đã được cập nhật giá mới nhất nhanh nhất 2026 — Có sự hỗ trợ từ V.AI STUDIO</p></div>
</div></footer>
<script>
let D=[],S={cat:'all',q:'',pmin:0,pmax:1e15,sort:'default',pg:1,view:'g',brand:'all'};
const PP=24;
const PRICES=[
{l:'Tất cả',min:0,max:1e15},{l:'Dưới 5 triệu',min:0,max:5e6},{l:'5-15 triệu',min:5e6,max:15e6},
{l:'15-30 triệu',min:15e6,max:3e7},{l:'30-50 triệu',min:3e7,max:5e7},{l:'Trên 50 triệu',min:5e7,max:1e15}
];
Promise.all(['products_with_slugs.json','products_garis_slugs.json','products_dmx_slugs.json','products_boss_slugs.json'].map(f=>fetch(f).then(r=>r.ok?r.json():[]).catch(()=>[]))).then(all=>{let d=[].concat(...all);D=d.map(p=>{let o={name:p.n,link:p.l,image:p.i,price:p.p,priceNum:p.pn,cat:p.c,catSlug:p.cs,catIcon:p.ci,subCat:p.sub_c||'',subCatSlug:p.sub_cs||'',images:p.imgs||[],summary:p.sum||'',desc:p.desc||'',specs:p.specs||{},feats:p.feats||[],sku:p.sku||'',video:p.vid||'',model:p.mod||'',slug:p.slug||'',brand:p.brand||(p.n.includes('| Demax |')?'Demax':p.n.includes('| Canzy |')?'Canzy':p.n.includes('| Grob |')?'Grob':p.n.includes('| Eurogold |')||p.n.startsWith('Eurogold |')?'Eurogold':'Malloca'),_source:p._source||'',_sourceUrl:p._sourceUrl||''};o._idx=buildSearchIndex(o);return o}).filter(p=>{let isExt=(p.link&&(p.link.indexOf('dienmayxanh')!==-1||p.link.indexOf('hafele-vn')!==-1))||(p._source&&(p._source.toLowerCase().indexOf('hafele')!==-1||p._source.toLowerCase().indexOf('điện máy xanh')!==-1));if(!isExt)return true;let nm=((p.name||'')+(p.brand||'')+(p.sku||'')).toLowerCase();return nm.indexOf('malloca')===-1;});init()}).catch(e=>{document.getElementById('grid').innerHTML='<div class="empty" style="grid-column:1/-1"><i class="fas fa-exclamation-triangle"></i><h3>Lỗi tải dữ liệu</h3><p>'+e.message+'</p><button class="reset-btn" onclick="location.reload()">Tải lại</button></div>'});
function nVN(s){return s.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/đ/g,'d').replace(/Đ/g,'d')}
// Synonym map (normalized, no diacritics)
const SYN={'ke':['gia'],'gia':['ke'],'bat':['chen'],'chen':['bat'],'xoong':['noi'],'noi':['xoong'],'dao':['thot'],'thot':['dao'],'ray':['truot'],'truot':['ray'],'khoa':['lock'],'lock':['khoa'],'bon':['chau'],'chau':['bon'],'ro':['gia','ke']};
function smartSearch(query,text){
// Strip . - space from query for SKU matching
let cleanQ=query.replace(/[.\- ]/g,'');
let cleanT=text.replace(/[.\- ]/g,'');
// 1. Exact substring on cleaned versions
if(cleanT.includes(cleanQ))return true;
if(text.includes(query))return true;
// 2. Split into words, ALL must match (any order)
let words=query.split(/\s+/).filter(w=>w);
if(!words.length)return true;
let matched=0;
for(let w of words){
let cw=w.replace(/[.\-]/g,'');
if(text.includes(w)||cleanT.includes(cw)){matched++;continue;}
// Try synonym
let hit=false;
if(SYN[w]){for(let s of SYN[w]){if(text.includes(s)){hit=true;break;}}}
if(hit){matched++;continue;}
}
return matched===words.length;
}
function buildSearchIndex(p){
let parts=[p.name,p.sku,p.model,p.brand,p.price];
// Add cleaned SKU/model (no . - space) for search
if(p.sku)parts.push(p.sku.replace(/[.\- ]/g,''));
if(p.model)parts.push(p.model.replace(/[.\- ]/g,''));
if(p.feats&&p.feats.length)parts.push(p.feats.join(' '));
if(p.specs){Object.entries(p.specs).forEach(([k,v])=>{parts.push(k);parts.push(String(v))})}
// AI semantic keywords based on product name
let n=p.name.toLowerCase();
if(n.includes('giá bát')||n.includes('kệ bát'))parts.push('ke bat gia bat ro bat dia chen');
if(n.includes('gia vị')||n.includes('chai lọ'))parts.push('ke gia vi chai lo');
if(n.includes('xoong')||n.includes('nồi'))parts.push('xoong noi chao');
if(n.includes('dao thớt'))parts.push('ke dao thot');
if(n.includes('tẩy rửa'))parts.push('ke tay rua gia chai lo nuoc rua');
if(n.includes('nâng hạ'))parts.push('nang ha len xuong');
if(n.includes('cảm ứng'))parts.push('cam ung dien thong minh');
if(n.includes('mâm xoay'))parts.push('mam xoay goc lien hoan');
if(n.includes('liên hoàn'))parts.push('lien hoan goc mam xoay');
if(n.includes('tủ kho')||n.includes('đồ khô'))parts.push('tu kho ke do kho');
if(n.includes('khay chia'))parts.push('khay chia thia nia muong dia ngan keo');
if(n.includes('thùng gạo'))parts.push('thung gao hop dung gao');
if(n.includes('thùng rác'))parts.push('thung rac');
if(n.includes('hút mùi'))parts.push('hut mui hut khoi khu mui');
if(n.includes('bếp từ'))parts.push('bep tu bep dien tu');
if(n.includes('bếp gas'))parts.push('bep gas');
if(n.includes('khóa'))parts.push('khoa cua thong minh van tay face id');
if(n.includes('chậu rửa'))parts.push('chau rua bon rua');
if(n.includes('vòi rửa'))parts.push('voi rua nong lanh');
if(n.includes('lò nướng'))parts.push('lo nuong');
if(n.includes('lò vi'))parts.push('lo vi song');
if(n.includes('máy rửa'))parts.push('may rua bat chen');
if(n.includes('trang sức'))parts.push('trang suc ngan keo');
if(n.includes('giày'))parts.push('giay ke de giay');
if(n.includes('gương'))parts.push('guong xoay');
if(n.includes('ray')||n.includes('bản lề'))parts.push('ray truot ban le giam chan');
if(n.includes('lò nướng'))parts.push('lo nuong am tu nong');
if(n.includes('lò vi'))parts.push('lo vi song am tu');
if(n.includes('tủ áo')||n.includes('quần áo'))parts.push('tu ao phu kien tu quan ao');
if(n.includes('trang sức'))parts.push('trang suc ngan keo de do');
if(n.includes('giày'))parts.push('giay ke de giay tu ao');
if(n.includes('gương'))parts.push('guong xoay tu ao thong minh');
if(n.includes('máy rửa'))parts.push('may rua bat chen doc lap am tu');
return nVN(parts.join(' '));
}
// init() is defined at the end of the script with routing logic
function buildCats(){
let cats={};
let subs={};
D.forEach(p=>{
if(!cats[p.catSlug])cats[p.catSlug]={n:p.cat,i:p.catIcon,c:0,hasSub:false};
cats[p.catSlug].c++;
if(p.subCatSlug){
cats[p.catSlug].hasSub=true;
if(!subs[p.subCatSlug])subs[p.subCatSlug]={n:p.subCat,parent:p.catSlug,c:0};
subs[p.subCatSlug].c++;
}
});
let h=`<li class="cat-item active" data-s="all" onclick="setCat('all')"><i class="fas fa-border-all"></i>Tất cả<span class="cat-count">${D.length}</span></li>`;
Object.entries(cats).forEach(([s,c])=>{
h+=`<li class="cat-item" data-s="${s}" onclick="setCat('${s}')"><i class="fas ${c.i}"></i>${c.n}<span class="cat-count">${c.c}</span></li>`;
if(c.hasSub){
let subList=Object.entries(subs).filter(([sk,sv])=>sv.parent===s).sort((a,b)=>b[1].c-a[1].c);
subList.forEach(([sk,sv])=>{
h+=`<li class="cat-item cat-sub" data-s="${sk}" onclick="setCat('${sk}')"><span style="margin-left:12px;font-size:.7rem;opacity:.5"></span>${sv.n}<span class="cat-count">${sv.c}</span></li>`;
});
}
});
document.getElementById('cl').innerHTML=h;document.getElementById('clm').innerHTML=h;
}
function buildPrices(){
let h='';PRICES.forEach((p,i)=>{h+=`<div class="pr-item${i===0?' active':''}" data-i="${i}" onclick="setPrice(${p.min},${p.max},${i})"><span class="pr-dot"></span>${p.l}</div>`});
document.getElementById('pr').innerHTML=h;document.getElementById('prm').innerHTML=h;
}
function buildBrands(){
let brands={};D.forEach(p=>{brands[p.brand]=(brands[p.brand]||0)+1});
let h=`<div class="pr-item active" data-b="all" onclick="setBrand('all')"><span class="pr-dot"></span>Tất cả (${D.length})</div>`;
Object.entries(brands).sort((a,b)=>b[1]-a[1]).forEach(([b,c])=>{h+=`<div class="pr-item" data-b="${b}" onclick="setBrand('${b}')"><span class="pr-dot"></span>${b} (${c})</div>`});
document.getElementById('br').innerHTML=h;document.getElementById('brm').innerHTML=h;
document.getElementById('statTotal').textContent=D.length;
{let _c={};D.forEach(p=>{_c[p.catSlug]=1});document.getElementById('statCats').textContent=Object.keys(_c).length;}
{let _partners=['Malloca','Eurogold','Grob','Canzy','Demax','Hafele','Garis','Điện Máy Xanh'];let _found={};D.forEach(p=>{let b=p.brand||'';_partners.forEach(pp=>{if(b===pp||b.toLowerCase().includes(pp.toLowerCase()))_found[pp]=1})});let bEl=document.getElementById('statBrands');if(bEl)bEl.textContent=Object.keys(_found).length;}
}
function getF(){
let r=D;
if(S.brand==='_dmx')r=r.filter(p=>p.slug&&p.slug.startsWith('dmx-'));
else if(S.brand!=='all')r=r.filter(p=>p.brand===S.brand);
if(S.cat!=='all')r=r.filter(p=>p.catSlug===S.cat||p.subCatSlug===S.cat);
if(S.q){let q=nVN(S.q);r=r.filter(p=>smartSearch(q,p._idx))}
if(S.pmin>0||S.pmax<1e15)r=r.filter(p=>p.priceNum>=S.pmin&&p.priceNum<=S.pmax);
if(S.sort==='pa')r.sort((a,b)=>a.priceNum-b.priceNum);
else if(S.sort==='pd')r.sort((a,b)=>b.priceNum-a.priceNum);
else if(S.sort==='na')r.sort((a,b)=>a.name.localeCompare(b.name,'vi'));
return r;
}
function render(){
let f=getF(),tp=Math.ceil(f.length/PP);if(S.pg>tp)S.pg=tp||1;
let s=(S.pg-1)*PP,items=f.slice(s,s+PP);
document.getElementById('rc').innerHTML=`<strong>${f.length}</strong> sản phẩm`;
document.getElementById('sc').textContent=S.q?f.length+' kết quả':'';
let g=document.getElementById('grid');
g.className='pg'+(S.view==='l'?' lv':'');
if(!items.length){g.innerHTML=`<div class="empty" style="grid-column:1/-1"><i class="fas fa-search"></i><h3>Không tìm thấy</h3><p>Thử thay đổi bộ lọc hoặc từ khóa</p><button class="reset-btn" onclick="resetAll()">Xóa bộ lọc</button></div>`;
}else{
g.innerHTML=items.map((p,i)=>{let idx=D.indexOf(p);let brandColor=p.brand==='Eurogold'?'#c8102e':p.brand==='Grob'?'#2e7d32':'var(--p)';let href='/san-pham/'+p.slug+'/index.html';return`<a class="pc fade" href="${href}" onclick="event.preventDefault();showDetail(${idx})" style="transition-delay:${Math.min(i*25,400)}ms;display:block"><div class="pi"><img src="${p.image}" alt="${p.name}" loading="lazy" onerror="if(!this.dataset.retry){this.dataset.retry=1;this.referrerPolicy='no-referrer';this.src=this.src}else{this.style.display='none'}"><span class="pi-badge"><i class="fas ${p.catIcon}"></i> ${p.cat}</span><span style="position:absolute;top:8px;right:8px;background:${brandColor};color:#fff;padding:3px 8px;border-radius:5px;font-size:.58rem;font-weight:700">${p.brand}</span></div><div class="pb"><div style="font-size:.65rem;font-weight:700;color:${brandColor};letter-spacing:.5px;margin-bottom:4px">${p.model||p.brand}</div><div class="pn">${p.name}</div><div class="pf"><span class="pp">${p.price||'Liên hệ'}</span><i class="fas fa-chevron-right"></i></div></div></a>`}).join('');
}
requestAnimationFrame(()=>document.querySelectorAll('.fade').forEach(e=>e.classList.add('vis')));
renderPag(tp);
}
function renderPag(tp){
if(tp<=1){document.getElementById('pag').innerHTML='';return}
let h=`<button class="pgb" onclick="goPg(${S.pg-1})"${S.pg===1?' disabled':''}><i class="fas fa-chevron-left"></i></button>`;
let pages=[];
if(tp<=7){for(let i=1;i<=tp;i++)pages.push(i)}
else{pages.push(1);if(S.pg>3)pages.push('.');for(let i=Math.max(2,S.pg-1);i<=Math.min(tp-1,S.pg+1);i++)pages.push(i);if(S.pg<tp-2)pages.push('.');pages.push(tp)}
pages.forEach(p=>{if(p==='.')h+=`<span class="pg-dots">...</span>`;else h+=`<button class="pgb${p===S.pg?' active':''}" onclick="goPg(${p})">${p}</button>`});
h+=`<button class="pgb" onclick="goPg(${S.pg+1})"${S.pg===tp?' disabled':''}><i class="fas fa-chevron-right"></i></button>`;
document.getElementById('pag').innerHTML=h;
}
// ===== DETAIL VIEW =====
let _skipHistory=false;
function showDetail(idx){
let p=D[idx];
// Update URL with SEO-friendly slug using History API
if(!_skipHistory){
let slugUrl='/san-pham/'+p.slug+'/index.html';
history.pushState({type:'product',idx:idx},'',slugUrl);
}
_skipHistory=false;
document.title=p.name+' | '+p.brand+' - V.AI STUDIO';
// Update OG meta for sharing
var ogImg=p.image||(p.images&&p.images[0])||'';
if(ogImg.startsWith('//'))ogImg='https:'+ogImg;
var pUrl=location.origin+'/san-pham/'+p.slug+'/index.html';
var el;
if(el=document.getElementById('ogTitle'))el.content=p.name+' | V.AI STUDIO';
if(el=document.getElementById('ogDesc'))el.content=(p.summary||p.desc||p.name).substring(0,200);
if(el=document.getElementById('ogImage'))el.content=ogImg;
if(el=document.getElementById('ogUrl'))el.content=pUrl;
if(el=document.getElementById('twTitle'))el.content=p.name+' | V.AI STUDIO';
if(el=document.getElementById('twDesc'))el.content=(p.summary||p.desc||p.name).substring(0,200);
if(el=document.getElementById('twImage'))el.content=ogImg;
document.getElementById('listView').style.display='none';
document.querySelector('.hero').style.display='none';
let dv=document.getElementById('detailView');
dv.classList.add('show');
// Build gallery carousel
let allImgs=p.images.length?p.images:[p.image];
let slides=allImgs.map((img,i)=>`<div class="gallery-slide"><img src="${img}" alt="${p.name}" loading="${i===0?'eager':'lazy'}"></div>`).join('');
let dots=allImgs.length>1?`<div class="gallery-dots">${allImgs.map((_,i)=>`<div class="gallery-dot${i===0?' active':''}" onclick="goSlide(${i})"></div>`).join('')}</div>`:'';
let thumbs=allImgs.length>1?allImgs.map((img,i)=>`<div class="gallery-thumb${i===0?' active':''}" onclick="goSlide(${i})"><img src="${img}" alt="" loading="lazy"></div>`).join(''):'';
// Specs table
let specsHtml='';
if(Object.keys(p.specs).length){
specsHtml='<table class="specs-table">'+Object.entries(p.specs).map(([k,v])=>`<tr><td>${k}</td><td>${v}</td></tr>`).join('')+'</table>';
}else{specsHtml='<p style="color:var(--g);font-size:.88rem">Chưa có thông số kỹ thuật chi tiết.</p>'}
// Features
let featsHtml='';
if(p.feats.length){featsHtml='<ul class="feat-list">'+p.feats.map(f=>`<li class="feat-item"><i class="fas fa-check-circle"></i><span>${f}</span></li>`).join('')+'</ul>'}
else{featsHtml='<p style="color:var(--g);font-size:.88rem">Chưa có danh sách tính năng.</p>'}
// Description
let descHtml=p.desc?`<div class="desc-text">${p.desc.replace(/\n/g,'<br>')}</div>`:'<p style="color:var(--g);font-size:.88rem">Chưa có mô tả chi tiết.</p>';
// Video
let videoHtml=p.video?`<div class="video-wrap"><iframe src="${p.video}" allowfullscreen loading="lazy"></iframe></div>`:'<p style="color:var(--g);font-size:.88rem">Chưa có video.</p>';
// Tab count for conditional display
let tabs=[
{id:'specs',label:'Thông Số Kỹ Thuật',icon:'fa-list-check',content:specsHtml},
{id:'feats',label:'Tính Năng',icon:'fa-star',content:featsHtml},
{id:'desc',label:'Mô Tả Chi Tiết',icon:'fa-file-lines',content:descHtml},
];
if(p.video)tabs.push({id:'video',label:'Video',icon:'fa-play',content:videoHtml});
let tabHeaders=tabs.map((t,i)=>`<div class="dtab-h${i===0?' active':''}" onclick="switchTab('${t.id}',this)"><i class="fas ${t.icon}"></i> ${t.label}</div>`).join('');
let tabContents=tabs.map((t,i)=>`<div class="dtab-content${i===0?' active':''}" id="tab-${t.id}">${t.content}</div>`).join('');
// Related products (same category, max 4)
let related=D.filter(r=>r.catSlug===p.catSlug&&r!==p).slice(0,4);
let relatedHtml='';
if(related.length){
relatedHtml=`<div style="margin-top:30px"><h3 style="font-size:1.1rem;font-weight:700;margin-bottom:16px"><i class="fas fa-cubes" style="color:var(--a);margin-right:8px"></i>Sản phẩm liên quan</h3><div class="pg" style="grid-template-columns:repeat(auto-fill,minmax(200px,1fr))">`;
relatedHtml+=related.map(r=>{let ri=D.indexOf(r);return`<a class="pc" href="/san-pham/${r.slug}/index.html" onclick="event.preventDefault();showDetail(${ri});window.scrollTo({top:0,behavior:'smooth'})" style="display:block"><div class="pi" style="height:160px"><img src="${r.image}" alt="${r.name}" loading="lazy"></div><div class="pb"><div class="pn">${r.name}</div><div class="pp">${r.price||'Liên hệ'}</div></div></a>`}).join('');
relatedHtml+='</div></div>';
}
dv.innerHTML=`
<div style="padding:28px 0">
<div class="detail-back" onclick="goBack()"><i class="fas fa-arrow-left"></i> Quay lại danh sách</div>
<div class="detail-grid">
<div class="gallery">
<div class="gallery-carousel" id="galleryCarousel">
<div class="gallery-track" id="galleryTrack">${slides}</div>
${allImgs.length>1?`<button class="gallery-nav gn-prev" onclick="goSlide(_gSlide-1)"><i class="fas fa-chevron-left"></i></button><button class="gallery-nav gn-next" onclick="goSlide(_gSlide+1)"><i class="fas fa-chevron-right"></i></button>`:''}
</div>
${dots}
${thumbs?`<div class="gallery-thumbs">${thumbs}</div>`:''}
</div>
<div class="detail-info">
${p.sku?`<div class="detail-sku">SKU: ${p.sku} &bull; Model: ${p.model}</div>`:''}
<div class="detail-cat"><i class="fas ${p.catIcon}"></i> ${p.cat}</div>
<h1 class="detail-name">${p.name}</h1>
<div style="font-size:.95rem;font-weight:800;color:${p.brand==='Eurogold'?'#c8102e':p.brand==='Grob'?'#2e7d32':'var(--a)'};letter-spacing:1px;margin-bottom:12px">${p.model?p.model+' | ':''} ${p.brand}</div>
<div class="detail-price">${p.price||'Liên hệ'}</div>
${p.summary?`<div class="detail-summary">${p.summary}</div>`:''}
<div class="detail-badges">
<div class="detail-badge"><i class="fas fa-check-circle"></i> Chính hãng ${p.brand}</div>
<div class="detail-badge"><i class="fas fa-shield-halved"></i> Bảo hành điện tử</div>
<div class="detail-badge"><i class="fas fa-truck-fast"></i> Miễn phí giao hàng TPHCM</div>
</div>
<div style="display:flex;gap:10px;flex-wrap:wrap">
<button class="add-cart-btn" onclick="event.stopPropagation();addToCart(${idx})"><i class="fas fa-cart-plus"></i> Thêm vào giỏ</button>
<button class="detail-btn detail-btn-outline" onclick="event.stopPropagation();toggleChat()"><i class="fas fa-robot"></i> Tư Vấn AI</button>
<button class="detail-btn detail-btn-outline" onclick="event.stopPropagation();shareProduct('${p.slug}','${p.name.replace(/'/g,"\\'")}')" title="Chia sẻ sản phẩm"><i class="fas fa-share-alt"></i> Chia sẻ</button>
</div>
</div>
</div>
<div class="dtabs">
<div class="dtab-headers">${tabHeaders}</div>
<div class="dtab-body">${tabContents}</div>
</div>
${relatedHtml}
</div>`;
window.scrollTo({top:0,behavior:'smooth'});
}
function goBack(){
document.getElementById('detailView').classList.remove('show');
document.getElementById('detailView').innerHTML='';
document.getElementById('listView').style.display='';
document.querySelector('.hero').style.display='';
document.title='V.AI STUDIO | Niềm tin khách hàng là tài sản của chúng tôi';
// Reset OG meta
var defImg='https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/logo_600.png';
var el;
if(el=document.getElementById('ogTitle'))el.content='V.AI STUDIO | Niềm tin khách hàng là tài sản của chúng tôi';
if(el=document.getElementById('ogDesc'))el.content='8000+ sản phẩm thiết bị nhà bếp & điện máy chính hãng';
if(el=document.getElementById('ogImage'))el.content=defImg;
if(el=document.getElementById('ogUrl'))el.content=location.origin+'/';
if(el=document.getElementById('twTitle'))el.content='V.AI STUDIO';
if(el=document.getElementById('twDesc'))el.content='8000+ sản phẩm thiết bị nhà bếp & điện máy chính hãng';
if(el=document.getElementById('twImage'))el.content=defImg;
history.pushState({type:'list'},'','/');
}
function goHome(){
history.pushState({type:'list'},'','/');
goBack();resetAll();
}
let _gSlide=0,_gTotal=0,_touchStartX=0;
function goSlide(i){
let track=document.getElementById('galleryTrack');
if(!track)return;
_gTotal=track.children.length;
if(i<0)i=0;if(i>=_gTotal)i=_gTotal-1;
_gSlide=i;
track.style.transform='translateX(-'+(_gSlide*100)+'%)';
document.querySelectorAll('.gallery-dot').forEach((d,j)=>d.classList.toggle('active',j===_gSlide));
document.querySelectorAll('.gallery-thumb').forEach((t,j)=>{t.classList.toggle('active',j===_gSlide);if(j===_gSlide)t.scrollIntoView({behavior:'smooth',block:'nearest',inline:'center'})});
}
// Touch swipe support
document.addEventListener('touchstart',function(e){
let c=e.target.closest('.gallery-carousel');if(c)_touchStartX=e.touches[0].clientX;
},{passive:true});
document.addEventListener('touchend',function(e){
if(!_touchStartX)return;
let c=e.target.closest('.gallery-carousel');if(!c){_touchStartX=0;return}
let diff=_touchStartX-e.changedTouches[0].clientX;
if(Math.abs(diff)>40){diff>0?goSlide(_gSlide+1):goSlide(_gSlide-1)}
_touchStartX=0;
},{passive:true});
function switchTab(id,el){
document.querySelectorAll('.dtab-h').forEach(h=>h.classList.remove('active'));
document.querySelectorAll('.dtab-content').forEach(c=>c.classList.remove('active'));
el.classList.add('active');
document.getElementById('tab-'+id).classList.add('active');
}
// ===== ACTIONS =====
function setCat(s){S.cat=s;S.pg=1;updActive('.cat-item','data-s',s);goBackToList();updateHash();render();closeMsb()}
function setPrice(min,max,i){S.pmin=min;S.pmax=max;S.pg=1;document.querySelectorAll('.pr-item[data-i]').forEach((e,j)=>e.classList.toggle('active',j===i));goBackToList();updateHash();render();closeMsb()}
function setBrand(b){S.brand=b;S.pg=1;document.querySelectorAll('[data-b]').forEach(e=>e.classList.toggle('active',e.getAttribute('data-b')===b));goBackToList();updateHash();render();closeMsb()}
function goBackToList(){document.getElementById('listView').style.display='';document.getElementById('detailView').classList.remove('show');document.getElementById('detailView').innerHTML='';document.querySelector('.hero').style.display='';}
function doSearch(){S.q=document.getElementById('q').value.trim();S.pg=1;updateHash();render();renderMainCatSearch()}
function doSort(){S.sort=document.getElementById('ss').value;S.pg=1;updateHash();render()}
function setV(v){S.view=v;document.getElementById('gb').classList.toggle('active',v==='g');document.getElementById('lb').classList.toggle('active',v==='l');updateHash();render()}
function goPg(p){let f=getF(),tp=Math.ceil(f.length/PP);if(p<1||p>tp)return;S.pg=p;updateHash();render();document.querySelector('.toolbar').scrollIntoView({behavior:'smooth',block:'start'})}
function resetAll(){S={cat:'all',q:'',pmin:0,pmax:1e15,sort:'default',pg:1,view:S.view,brand:'all'};document.getElementById('q').value='';document.getElementById('ss').value='default';updActive('.cat-item','data-s','all');document.querySelectorAll('.pr-item[data-i]').forEach((e,i)=>e.classList.toggle('active',i===0));document.querySelectorAll('[data-b]').forEach(e=>e.classList.toggle('active',e.getAttribute('data-b')==='all'));history.replaceState({type:'list'},'','/');render();let csr=document.getElementById('catSearchResults');if(csr)csr.innerHTML='';}
function updActive(sel,attr,val){document.querySelectorAll(sel).forEach(e=>e.classList.toggle('active',e.getAttribute(attr)===val))}
function closeMsb(){document.getElementById('msb').classList.remove('open')}
document.addEventListener('keydown',e=>{if(e.key==='Escape')goBack();if(e.key==='/'&&document.activeElement.tagName!=='INPUT'){e.preventDefault();document.getElementById('q').focus()}});
// ===== CATALOGUE (all pages rendered as images, loaded from catalogues.json) =====
let CAT_LIST=[];
let catLoaded=false;
let activeCat=0;
const CAT_PER_PAGE=12;
let catPage=1;
function loadCatalogueData(){
return Promise.all([
fetch('catalogues.json').then(r=>r.json()),
fetch('catalogue_ocr.json').then(r=>r.json()).catch(()=>[])
]).then(([data,ocr])=>{
CAT_LIST=data;
window._catOcr=ocr;
initCatTabs();
loadCatalogue(0);
catLoaded=true;
});
}
function initCatTabs(){
let tabs=document.getElementById('catTabs');
if(!tabs||!CAT_LIST.length)return;
const icons={'bep':'fa-fire','may-hut':'fa-wind','lo':'fa-temperature-high','chau':'fa-sink','khac':'fa-box','gia':'fa-tags','master':'fa-layer-group','grob':'fa-cogs','canzy':'fa-kitchen-set','demax':'fa-lock'};
tabs.innerHTML=CAT_LIST.map((c,i)=>{
let slug=c.slug||'';
let icon='fa-book-open';
for(let k in icons){if(slug.includes(k)){icon=icons[k];break;}}
let pCount=c.pages?c.pages.length:0;
return `<div class="ctab${i===0?' active':''}" onclick="loadCatalogue(${i})"><i class="fas ${icon}"></i>${c.name||'Catalogue '+(i+1)}<span class="cnt">${pCount}</span></div>`;
}).join('');
}
function loadCatalogue(idx){
activeCat=idx;
let c=CAT_LIST[idx];
if(!c)return;
// Update active tab
let tabBtns=document.querySelectorAll('#catTabs .ctab');
tabBtns.forEach((b,i)=>b.classList.toggle('active',i===idx));
let content=document.getElementById('catContent');
if(!content)return;
let pages=c.pages||[];
let totalPages=pages.length;
let pageHtml=`<div style="text-align:center;margin-bottom:16px">
<div style="font-size:.85rem;color:var(--g)">${c.desc||c.name||''} — <strong>${totalPages}</strong> trang</div>
</div>`;
pageHtml+=`<div style="display:flex;flex-direction:column;gap:16px;align-items:center">`;
pageHtml+=pages.map((pg,i)=>{
// pg can be a URL string or an object with .file
let imgSrc=typeof pg==='string'?pg:(pg.file?'grob_img/'+pg.file:pg.image||pg.url||'');
let label='Trang '+(i+1);
return `<div style="width:100%;max-width:900px"><img src="${imgSrc}" alt="${label}" style="width:100%;border-radius:12px;box-shadow:0 2px 12px rgba(0,0,0,.1)" loading="lazy"><div style="text-align:center;font-size:.75rem;color:var(--g);margin-top:6px">${label}</div></div>`;
}).join('');
pageHtml+=`</div>`;
content.innerHTML=pageHtml;
window.scrollTo({top:0,behavior:'smooth'});
}
function searchCatalogue(){
let q=document.getElementById('catSearch').value.trim();
if(!q){loadCatalogue(activeCat);document.getElementById('catSearchCount').textContent='';return}
let nq=nVN(q);
let results=[];
CAT_LIST.forEach((c,ci)=>{
let ocrTexts=(window._catOcr&&window._catOcr[ci])?window._catOcr[ci].texts||[]:[];
(c.pages||[]).forEach((pg,pi)=>{
let ocrText=ocrTexts[pi]||'';
let text=nVN(ocrText+' '+(c.name||''));
if(smartSearch(nq,text)){
let imgSrc=typeof pg==='string'?pg:(pg.file?'grob_img/'+pg.file:'');
results.push({cat:c.name||'Catalogue',imgSrc:imgSrc,pageNum:pi+1});
}
});
});
document.getElementById('catSearchCount').textContent=results.length+' kết quả';
let content=document.getElementById('catContent');
if(!results.length){content.innerHTML='<div class="empty" style="padding:40px"><i class="fas fa-search"></i><h3>Không tìm thấy trong catalogue</h3></div>';return}
content.innerHTML=`<div style="display:flex;flex-direction:column;gap:16px;align-items:center">`+results.map(r=>`<div style="width:100%;max-width:900px"><div style="font-size:.8rem;color:var(--p);font-weight:700;margin-bottom:6px">${r.cat} — Trang ${r.pageNum}</div><img src="${r.imgSrc}" alt="Trang ${r.pageNum}" style="width:100%;border-radius:12px;box-shadow:0 2px 12px rgba(0,0,0,.1)" loading="lazy"></div>`).join('')+`</div>`;
}
function renderMainCatSearch(){
let q=document.getElementById('q').value.trim();
let csr=document.getElementById('catSearchResults');
if(!csr)return;
if(!q){csr.innerHTML='';return}
let nq=nVN(q);
let results=[];
CAT_LIST.forEach((c,ci)=>{
let ocrTexts=(window._catOcr&&window._catOcr[ci])?window._catOcr[ci].texts||[]:[];
(c.pages||[]).forEach((pg,pi)=>{
let ocrText=ocrTexts[pi]||'';
let text=nVN(ocrText+' '+(c.name||''));
if(smartSearch(nq,text)){
let imgSrc=typeof pg==='string'?pg:(pg.file?'grob_img/'+pg.file:'');
results.push({cat:c.name||'Catalogue',imgSrc:imgSrc,pageNum:pi+1});
}
});
});
if(!results.length){csr.innerHTML='';return}
let searchVal=document.getElementById('q').value.replace(/'/g,"\\'");
csr.innerHTML=`<div style="margin-top:24px;padding-top:24px;border-top:2px solid var(--gl)"><h3 style="font-size:1rem;font-weight:700;margin-bottom:12px"><i class="fas fa-book-open" style="color:var(--a);margin-right:8px"></i>Kết quả trong Catalogue (${results.length})</h3><div style="display:flex;flex-wrap:wrap;gap:12px">`+results.slice(0,6).map(r=>`<div style="width:200px;cursor:pointer" onclick="showPage('catalogue');document.getElementById('catSearch').value='${searchVal}';searchCatalogue();"><img src="${r.imgSrc}" style="width:100%;border-radius:8px;border:2px solid var(--gl)" loading="lazy"><div style="font-size:.7rem;color:var(--g);margin-top:4px">${r.cat} — Trang ${r.pageNum}</div></div>`).join('')+`</div></div>`;
}
function showPage(page){
document.querySelectorAll('.page-section').forEach(el=>el.classList.remove('active'));
document.querySelectorAll('.nav-tab').forEach(el=>el.classList.toggle('active',el.dataset.page===page));
let el=document.getElementById('page-'+page);
if(el){el.classList.add('active');}
if(page==='catalogue'&&!catLoaded){loadCatalogueData();}
if(page==='catalogue'){history.pushState({type:'page',page:'catalogue'},'','/catalogue/index.html');}
else if(page==='contact'){history.pushState({type:'page',page:'contact'},'','/lien-he/index.html');}
else if(page==='products'){history.pushState({type:'list'},'','/');}
window.scrollTo({top:0,behavior:'smooth'});
}
function initCatalogue(){
// catalogue loads on demand
}
// ===== CART =====
let cart=JSON.parse(localStorage.getItem('malloca_cart')||'[]');
function updateCartBadge(){
let n=cart.reduce((s,i)=>s+i.qty,0);
let badge=document.getElementById('cartNum');
if(badge){badge.textContent=n;badge.style.display=n>0?'flex':'none'}
}
function addToCart(idx){
let p=D[idx];
let exist=cart.find(c=>c.idx===idx);
if(exist){exist.qty++}else{cart.push({idx,name:p.name,price:p.price,priceNum:p.priceNum,image:p.image,qty:1})}
localStorage.setItem('malloca_cart',JSON.stringify(cart));
updateCartBadge();
// Show brief toast notification instead of opening cart
let toast=document.createElement('div');
toast.style.cssText='position:fixed;bottom:24px;right:24px;background:var(--p);color:#fff;padding:12px 20px;border-radius:12px;font-size:.85rem;font-weight:600;z-index:999;display:flex;align-items:center;gap:8px;box-shadow:0 8px 24px rgba(0,0,0,.2);animation:fadeUp .3s ease';
toast.innerHTML='<i class="fas fa-check-circle"></i> Đã thêm vào giỏ hàng';
document.body.appendChild(toast);
setTimeout(()=>{toast.style.opacity='0';toast.style.transition='opacity .3s';setTimeout(()=>toast.remove(),300)},2000);
}
function removeFromCart(i){
cart.splice(i,1);
localStorage.setItem('malloca_cart',JSON.stringify(cart));
updateCartBadge();renderCart();
}
function changeQty(i,delta){
cart[i].qty+=delta;
if(cart[i].qty<1)cart.splice(i,1);
localStorage.setItem('malloca_cart',JSON.stringify(cart));
updateCartBadge();renderCart();
}
function openCart(){
document.getElementById('cartOverlay').classList.add('open');
document.getElementById('cartDrawer').classList.add('open');
document.body.style.overflow='hidden';
renderCart();
}
function closeCart(){
document.getElementById('cartOverlay').classList.remove('open');
document.getElementById('cartDrawer').classList.remove('open');
document.body.style.overflow='';
}
function renderCart(){
let el=document.getElementById('cartItems');
let footer=document.getElementById('cartFooter');
if(!cart.length){
el.innerHTML=`<div class="cart-empty"><i class="fas fa-shopping-bag"></i><h3>Giỏ hàng trống</h3><p style="font-size:.82rem">Thêm sản phẩm vào giỏ để đặt hàng</p></div>`;
footer.style.display='none';
return;
}
footer.style.display='block';
el.innerHTML=cart.map((c,i)=>`<div class="cart-item"><img src="${c.image}" alt="${c.name}"><div class="cart-item-info"><div class="cart-item-name">${c.name}</div><div class="cart-item-price">${c.price}</div><div class="cart-item-qty"><button class="qty-btn" onclick="changeQty(${i},-1)"></button><span class="qty-val">${c.qty}</span><button class="qty-btn" onclick="changeQty(${i},1)">+</button></div></div><button class="cart-item-del" onclick="removeFromCart(${i})"><i class="fas fa-trash"></i></button></div>`).join('');
let total=cart.reduce((s,c)=>s+c.priceNum*c.qty,0);
document.getElementById('cartTotal').textContent=total.toLocaleString('vi-VN')+'đ';
}
// ===== CHECKOUT =====
function openCheckout(){
closeCart();
let overlay=document.getElementById('checkoutOverlay');
overlay.classList.add('open');
document.body.style.overflow='hidden';
let total=cart.reduce((s,c)=>s+c.priceNum*c.qty,0);
let totalStr=total.toLocaleString('vi-VN')+'đ';
// Build order summary
let itemsHtml=cart.map(c=>`<div class="checkout-summary-item"><span>${c.name} x${c.qty}</span><span style="font-weight:600">${(c.priceNum*c.qty).toLocaleString('vi-VN')}đ</span></div>`).join('');
// Build QR URL (VietQR standard)
let qrUrl=`https://img.vietqr.io/image/VIB-918258385-compact2.jpg?amount=${total}&addInfo=${encodeURIComponent('Thanh toan don hang Malloca')}&accountName=${encodeURIComponent('TRAN QUOC VUONG')}`;
document.getElementById('checkoutContent').innerHTML=`
<h2 class="checkout-title"><i class="fas fa-qrcode" style="color:var(--a)"></i> Thanh Toán Đơn Hàng</h2>
<p class="checkout-sub">Quét mã QR để thanh toán qua ứng dụng ngân hàng</p>
<div class="checkout-summary">${itemsHtml}<div class="checkout-summary-item total"><span>Tổng cộng</span><span>${totalStr}</span></div></div>
<div class="form-group"><label>Họ tên người nhận *</label><input class="form-input" id="coName" placeholder="Nhập họ tên"></div>
<div class="form-group"><label>Số điện thoại *</label><input class="form-input" id="coPhone" placeholder="Nhập SĐT"></div>
<div class="form-group"><label>Địa chỉ giao hàng *</label><input class="form-input" id="coAddr" placeholder="Nhập địa chỉ đầy đủ"></div>
<div class="form-group"><label>Ghi chú</label><textarea class="form-input" id="coNote" placeholder="Ghi chú đơn hàng (nếu có)" style="height:70px"></textarea></div>
<div class="qr-section">
<h3 style="font-size:1rem;font-weight:700;margin-bottom:14px"><i class="fas fa-university" style="color:var(--p);margin-right:6px"></i> Chuyển khoản ngân hàng</h3>
<img src="${qrUrl}" alt="QR Thanh toán" onerror="this.parentElement.innerHTML+='<p style=color:red>Không tải được QR. Vui lòng chuyển khoản thủ công.</p>'">
<div class="qr-bank-info">
<strong>Ngân hàng:</strong> VIB - Ngân hàng TMCP Quốc Tế Việt Nam<br>
<strong>Số TK:</strong> 918258385 <button class="copy-btn" onclick="copyText('918258385')"><i class="fas fa-copy"></i> Copy</button><br>
<strong>Chủ TK:</strong> TRẦN QUỐC VƯƠNG<br>
<strong>Số tiền:</strong> <span style="color:var(--p);font-weight:800">${totalStr}</span><br>
<strong>Nội dung CK:</strong> Thanh toan don hang Malloca <button class="copy-btn" onclick="copyText('Thanh toan don hang Malloca')"><i class="fas fa-copy"></i> Copy</button>
</div>
</div>
<button class="checkout-done-btn" onclick="confirmOrder()"><i class="fas fa-check-circle"></i> Xác nhận đã thanh toán</button>
<p class="checkout-note">Đơn hàng sẽ được xử lý sau khi xác nhận thanh toán thành công.<br>Zalo hỗ trợ: <a href="https://zalo.me/0981873395" target="_blank" style="color:var(--p)"><strong>0981 873 395</strong></a></p>`;
}
function closeCheckout(){
document.getElementById('checkoutOverlay').classList.remove('open');
document.body.style.overflow='';
}
function copyText(text){
navigator.clipboard.writeText(text).then(()=>{
let btns=document.querySelectorAll('.copy-btn');
btns.forEach(b=>{if(b.previousSibling&&b.previousSibling.textContent&&b.previousSibling.textContent.includes(text)){b.innerHTML='<i class="fas fa-check"></i> Đã copy';setTimeout(()=>{b.innerHTML='<i class="fas fa-copy"></i> Copy'},2000)}});
});
}
function confirmOrder(){
let name=document.getElementById('coName').value.trim();
let phone=document.getElementById('coPhone').value.trim();
let addr=document.getElementById('coAddr').value.trim();
if(!name||!phone||!addr){alert('Vui lòng điền đầy đủ Họ tên, SĐT và Địa chỉ.');return}
// Clear cart
cart=[];localStorage.setItem('malloca_cart','[]');updateCartBadge();
// Show success
document.getElementById('checkoutContent').innerHTML=`
<div style="text-align:center;padding:40px 0">
<div style="width:80px;height:80px;background:rgba(40,167,69,.1);border-radius:50%;display:flex;align-items:center;justify-content:center;margin:0 auto 20px"><i class="fas fa-check" style="font-size:2rem;color:#28a745"></i></div>
<h2 style="font-size:1.4rem;font-weight:800;color:var(--d);margin-bottom:8px">Đặt hàng thành công!</h2>
<p style="color:var(--g);font-size:.9rem;margin-bottom:24px">Cảm ơn bạn đã đặt hàng. Chúng tôi sẽ liên hệ xác nhận trong thời gian sớm nhất.</p>
<div style="background:var(--l);border-radius:12px;padding:16px;text-align:left;font-size:.85rem;color:var(--d);line-height:1.8">
<strong>Người nhận:</strong> ${name}<br>
<strong>SĐT:</strong> ${phone}<br>
<strong>Địa chỉ:</strong> ${addr}
</div>
<button class="checkout-done-btn" onclick="closeCheckout();showPage('products')" style="margin-top:20px"><i class="fas fa-home"></i> Về trang chủ</button>
</div>`;
}
// Contact form
function submitContact(e){
e.preventDefault();
let form=e.target;
let name=form.querySelector('#ctName').value;
let email=form.querySelector('#ctEmail').value;
let msg=form.querySelector('#ctMsg').value;
if(!name||!email||!msg){alert('Vui lòng điền đầy đủ thông tin.');return}
form.innerHTML=`<div style="text-align:center;padding:30px"><i class="fas fa-check-circle" style="font-size:2.5rem;color:#28a745;margin-bottom:12px;display:block"></i><h3 style="font-size:1.1rem;font-weight:700;margin-bottom:8px">Gửi thành công!</h3><p style="color:var(--g);font-size:.88rem">Cảm ơn bạn đã liên hệ. Chúng tôi sẽ phản hồi sớm nhất có thể.</p></div>`;
}
// Override goHome to also handle page nav
let _origGoHome=goHome;
goHome=function(){
if(document.getElementById('detailView').classList.contains('show')){
goBack();
}
showPage('products');resetAll();
}
// ===== SEO URL ROUTING =====
// Parse current path on load (for SPA navigation within iframe)
function parseCurrentPath(){
let path=location.pathname;
// Handle ?p= redirect from static OG pages
let qp=new URLSearchParams(location.search).get('p');
if(qp){
let idx=D.findIndex(p=>p.slug===qp);
if(idx>=0){showDetailNoHistory(idx);return true;}
}
// Product page: /san-pham/<slug>/ (slug may contain slashes like dmx-tu-lanh/samsung-xxx)
let prodMatch=path.match(/^\/san-pham\/(.+?)(?:\/index\.html)?$/);
if(prodMatch){
let slug=prodMatch[1].replace(/\/+$/,'');
if(slug==='index.html'||!slug)slug=null;
let idx=slug?D.findIndex(p=>p.slug===slug):-1;
if(idx>=0){showDetailNoHistory(idx);return true;}
}
// Category page: /danh-muc/<slug>/
let catMatch=path.match(/^\/danh-muc\/([^\/]+)/);
if(catMatch){
let catSlug=catMatch[1];
S.cat=catSlug;
updActive('.cat-item','data-s',catSlug);
document.querySelectorAll('[data-b]').forEach(e=>e.classList.toggle('active',e.getAttribute('data-b')==='all'));
render();
return true;
}
// Catalogue page
if(path.startsWith('/catalogue')){showPage('catalogue');return true;}
// Contact page
if(path.startsWith('/lien-he')){showPage('contact');return true;}
return false;
}
// Also parse hash params (backward compatibility)
function parseUrlParams(){
let hash=location.hash.replace(/^#/,'');
if(!hash)return;
let params=new URLSearchParams(hash);
// Legacy hash page navigation
let pageParam=params.get('page');
if(pageParam&&pageParam!=='products'){
showPage(pageParam);
return;
}
// Legacy product hash (supports both index and slug)
let productParam=params.get('product');
if(productParam!==null){
let idx=parseInt(productParam);
if(!isNaN(idx)&&idx>=0&&idx<D.length){showDetailNoHistory(idx);return;}
// Try slug match
let slugIdx=D.findIndex(p=>p.slug===productParam);
if(slugIdx>=0){showDetailNoHistory(slugIdx);return;}
}
let q=params.get('q')||params.get('search')||'';
if(q){document.getElementById('q').value=q;S.q=q;S.pg=1;}
let cat=params.get('cat');
if(cat){S.cat=cat;updActive('.cat-item','data-s',cat);}
let brand=params.get('brand');
if(brand){S.brand=brand;document.querySelectorAll('[data-b]').forEach(e=>e.classList.toggle('active',e.getAttribute('data-b')===brand));}
let pgNum=params.get('pg');
if(pgNum){S.pg=parseInt(pgNum)||1;}
let sort=params.get('sort');
if(sort){S.sort=sort;document.getElementById('ss').value=sort;}
let p=params.get('p');
if(p==='list'){S.view='l';document.getElementById('gb').classList.remove('active');document.getElementById('lb').classList.add('active');}
}
// Update hash based on current list state (for filters)
function updateHash(){
let parts=[];
if(S.q)parts.push('q='+encodeURIComponent(S.q));
if(S.cat!=='all')parts.push('cat='+encodeURIComponent(S.cat));
if(S.brand!=='all')parts.push('brand='+encodeURIComponent(S.brand));
if(S.pg>1)parts.push('pg='+S.pg);
if(S.sort!=='default')parts.push('sort='+encodeURIComponent(S.sort));
if(S.view==='l')parts.push('p=list');
let hash=parts.length?'#'+parts.join('&'):'';
history.replaceState({type:'list'},'','/'+hash);
}
// Handle browser back/forward buttons
window.addEventListener('popstate',function(e){
let state=e.state;
if(state&&state.type==='product'&&typeof state.idx==='number'){
if(state.idx>=0&&state.idx<D.length){
let dv=document.getElementById('detailView');
if(!dv.classList.contains('show')){showDetailNoHistory(state.idx);}
}
}else{
// Go back to list view
document.getElementById('detailView').classList.remove('show');
document.getElementById('detailView').innerHTML='';
document.getElementById('listView').style.display='';
document.querySelector('.hero').style.display='';
document.title='V.AI STUDIO | Niềm tin khách hàng là tài sản của chúng tôi';
// Check if we need to show a specific page
let path=location.pathname;
if(path.startsWith('/catalogue')){showPage('catalogue');}
else if(path.startsWith('/lien-he')){showPage('contact');}
else{
document.querySelectorAll('.page-section').forEach(el=>el.classList.remove('active'));
let prodPage=document.getElementById('page-products');
if(prodPage)prodPage.classList.add('active');
document.querySelectorAll('.nav-tab').forEach(el=>el.classList.toggle('active',el.dataset.page==='products'));
parseUrlParams();
render();
}
}
});
// showDetail without pushing history (for popstate)
function showDetailNoHistory(idx){
_skipHistory=true;
showDetail(idx);
}
// ===== SHARE (Web Share API + Popover fallback) =====
let _shareData={title:'',text:'',url:''};
async function shareProduct(slug,name){
let url=location.origin+'/san-pham/'+slug+'/index.html';
let title=name||document.title;
let text=title+' - V.AI STUDIO';
// Try native share first (includes image via OG tags already set by showDetail)
if(navigator.share){
try{await navigator.share({title,text,url});return}catch(e){if(e.name==='AbortError')return}
}
// Fallback: show share popover
openSharePopover(url,title,text);
}
function shareCurrentPage(){
let url=location.origin+location.pathname;
let title=document.title;
let text=title;
if(navigator.share){
navigator.share({title,text,url}).catch(()=>{});
return;
}
openSharePopover(url,title,text);
}
function openSharePopover(url,title,text){
_shareData={title:title||'',text:text||'',url:url||location.href};
document.getElementById('shareLinkInput').value=_shareData.url;
document.getElementById('shareOverlay').classList.add('open');
}
function closeSharePopover(){document.getElementById('shareOverlay').classList.remove('open')}
function shareVia(platform){
let u=encodeURIComponent(_shareData.url);
let t=encodeURIComponent(_shareData.title);
let txt=encodeURIComponent(_shareData.text+' '+_shareData.url);
let win=null;
switch(platform){
case'facebook':win=window.open('https://www.facebook.com/sharer/sharer.php?u='+u,'_blank','width=600,height=400');break;
case'messenger':win=window.open('https://www.facebook.com/dialog/send?link='+u+'&redirect_uri='+u,'_blank');break;
case'zalo':win=window.open('https://zalo.me/share?url='+u,'_blank');break;
case'twitter':win=window.open('https://twitter.com/intent/tweet?text='+t+'&url='+u,'_blank','width=600,height=400');break;
case'telegram':win=window.open('https://t.me/share/url?url='+u+'&text='+t,'_blank');break;
case'whatsapp':win=window.open('https://wa.me/?text='+txt,'_blank');break;
case'email':window.location.href='mailto:?subject='+t+'&body='+txt;break;
case'copy':
if(navigator.clipboard){navigator.clipboard.writeText(_shareData.url).then(()=>showToast('Đã sao chép link!'))}
else{let ta=document.createElement('textarea');ta.value=_shareData.url;ta.style.cssText='position:fixed;top:-9999px';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);showToast('Đã sao chép link!')}
break;
}
closeSharePopover();
}
function showToast(msg){
let old=document.querySelector('.share-toast');if(old)old.remove();
let t=document.createElement('div');t.className='share-toast';
t.style.cssText='position:fixed;bottom:24px;right:24px;background:#28a745;color:#fff;padding:12px 20px;border-radius:12px;font-size:.85rem;font-weight:600;z-index:999;box-shadow:0 4px 12px rgba(0,0,0,.2)';
t.innerHTML='<i class="fas fa-check-circle" style="margin-right:6px"></i>'+msg;
document.body.appendChild(t);
setTimeout(()=>{t.style.opacity='0';t.style.transition='opacity .3s';setTimeout(()=>t.remove(),300)},2500);
}
// ===== AI CHATBOX =====
let chatHistory=[];
let chatBusy=false;
function toggleChat(){
let box=document.getElementById('chatbox');
let ov=document.getElementById('chatOverlay2');
if(box.classList.contains('open')){closeChat()}
else{box.classList.add('open');ov.classList.add('open');document.getElementById('chatInput').focus()}
}
function closeChat(){
document.getElementById('chatbox').classList.remove('open');
document.getElementById('chatOverlay2').classList.remove('open');
}
function sendSuggestion(el){
document.getElementById('chatInput').value=el.textContent;
document.getElementById('chatSuggestions').style.display='none';
sendChat();
}
function appendMsg(role,html){
let body=document.getElementById('chatBody');
let div=document.createElement('div');
div.className='chat-msg '+role;
div.innerHTML=html;
// Add Zalo share button to bot messages
if(role==='bot'){
let shareBtn=document.createElement('div');
shareBtn.style.cssText='margin-top:8px;padding-top:6px;border-top:1px solid var(--gl);display:flex;gap:6px';
shareBtn.innerHTML='<button onclick="shareChatToZalo(this.closest(\'.chat-msg\'))" style="padding:5px 12px;background:#0068FF;color:#fff;border:none;border-radius:6px;font-size:.72rem;font-weight:600;cursor:pointer;font-family:inherit;display:inline-flex;align-items:center;gap:5px"><i class="fas fa-comment-dots"></i> Gửi qua Zalo</button>';
div.appendChild(shareBtn);
}
body.appendChild(div);
body.scrollTop=body.scrollHeight;
return div;
}
function shareChatToZalo(msgEl){
// Lấy toàn bộ nội dung hội thoại
let body=document.getElementById('chatBody');
let allMsgs=body.querySelectorAll('.chat-msg');
let lines=[];
allMsgs.forEach(m=>{
let clone=m.cloneNode(true);
clone.querySelectorAll('div[style*="border-top"]').forEach(b=>b.remove());
let txt=(clone.innerText||clone.textContent||'').trim();
if(!txt)return;
let role=m.classList.contains('user')?'🧑 Khách:':'🤖 AI:';
lines.push(role+' '+txt);
});
let text=lines.join('\n\n').substring(0,3000);
text='[V.AI STUDIO - Tư vấn thiết bị nhà bếp]\n\n'+text+'\n\n---\nGửi từ: '+location.origin;
// Copy vào clipboard
if(navigator.clipboard){
navigator.clipboard.writeText(text).catch(function(){
let ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;top:-9999px';
document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);
});
}else{
let ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;top:-9999px';
document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);
}
// Mở Zalo profile — user nhấn "Nhắn tin" rồi paste
showToast('✅ Đã sao chép nội dung! Nhấn "Nhắn tin" trên Zalo rồi DÁN để gửi.');
setTimeout(function(){window.open('https://zalo.me/0981873395','_blank')},800);
}
function showTyping(){
let body=document.getElementById('chatBody');
let div=document.createElement('div');
div.className='chat-typing';div.id='typingIndicator';
div.innerHTML='<span></span><span></span><span></span>';
body.appendChild(div);
body.scrollTop=body.scrollHeight;
}
function hideTyping(){let el=document.getElementById('typingIndicator');if(el)el.remove()}
// Search products locally
function searchProducts(query){
if(!D||!D.length)return[];
// Use advanced search-plus-boot.js context search first.
// This handles Grob/Eurogold/Garis cabinet accessory dimensions such as: "kệ gia vị 300mm".
try{
if(window._vaiSearchContext){
let advanced=window._vaiSearchContext(query,8).map(x=>x.p).filter(Boolean);
if(advanced.length)return advanced;
}
}catch(e){console.warn('advanced searchProducts failed',e)}
let q=nVN(query);
let words=q.split(/\s+/).filter(w=>w.length>1);
let scored=D.map((p,i)=>{
let idx=p._idx;
let score=0;
words.forEach(w=>{
if(idx&&idx.includes(w))score+=1;
if(p.name&&nVN(p.name).includes(w))score+=3;
if(p.cat&&nVN(p.cat).includes(w))score+=2;
});
return{p,score};
}).filter(x=>x.score>0).sort((a,b)=>b.score-a.score);
return scored.slice(0,8).map(x=>x.p);
}
// Format products for context
function productsToContext(products){
return products.map(p=>{
let specs='';
if(p.specs&&Object.keys(p.specs).length){specs=' | Thông số: '+Object.entries(p.specs).slice(0,5).map(([k,v])=>k+': '+v).join(', ')}
return '- '+p.name+' | Giá: '+(p.price||'Liên hệ')+' | Danh mục: '+p.cat+specs;
}).join('\n');
}
// Format bot response
function formatBotResponse(text,matchedProducts){
// Strip any raw URLs or markdown links — keep text only
text=text.replace(/\[([^\]]+)\]\([^)]+\)/g,'$1');
text=text.replace(/https?:\/\/[^\s<)]+/g,'');
text=text.replace(/\/san-pham\/[a-z0-9-]+\/index\.html/g,'');
// Bold
text=text.replace(/\*\*([^*]+)\*\*/g,'<strong>$1</strong>');
// Line breaks
text=text.replace(/\n/g,'<br>');
// Smart product cards: only show products MENTIONED in the AI response
if(matchedProducts&&matchedProducts.length>0){
let mentionedProducts=filterMentionedProducts(text,matchedProducts);
if(mentionedProducts.length>0){
let cards=mentionedProducts.slice(0,4).map(p=>{
let pidx=D.findIndex(d=>d.slug===p.slug);
let cartBtn=pidx>=0&&p.priceNum>0?'<button onclick="event.stopPropagation();addToCart('+pidx+');this.innerHTML=\'✅ Đã thêm\';this.disabled=true" style="padding:4px 10px;background:var(--a);color:#fff;border:none;border-radius:6px;font-size:.68rem;font-weight:600;cursor:pointer;display:inline-flex;align-items:center;gap:4px;font-family:inherit"><i class="fas fa-cart-plus"></i> Thêm giỏ</button>':'';
return '<div style="display:flex;gap:10px;background:var(--l);border-radius:10px;padding:10px;margin-top:6px;border:1px solid var(--gl)">'
+'<a href="/san-pham/'+p.slug+'/index.html" style="flex-shrink:0;text-decoration:none"><img src="'+p.image+'" style="width:50px;height:50px;object-fit:contain;border-radius:6px;background:#fff" onerror="this.style.display=\'none\'"></a>'
+'<div style="flex:1;min-width:0"><a href="/san-pham/'+p.slug+'/index.html" style="text-decoration:none;color:inherit"><div style="font-size:.76rem;font-weight:600;color:var(--d);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden">'+p.name+'</div>'
+'<div style="font-size:.8rem;font-weight:800;color:var(--p);margin-top:2px">'+(p.price||'Liên hệ')+'</div></a>'
+'<div style="display:flex;gap:6px;margin-top:6px;flex-wrap:wrap">'
+cartBtn
+'<a href="/san-pham/'+p.slug+'/index.html" style="padding:4px 10px;background:var(--p);color:#fff;border-radius:6px;font-size:.68rem;font-weight:600;text-decoration:none;display:inline-flex;align-items:center;gap:4px"><i class="fas fa-eye"></i> Xem</a>'
+'</div></div></div>';
}).join('');
text+='<div style="margin-top:10px;padding-top:10px;border-top:1px solid var(--gl)"><div style="font-size:.68rem;font-weight:700;color:var(--g);margin-bottom:4px;text-transform:uppercase;letter-spacing:.5px">👉 Sản phẩm được đề cập</div>'+cards+'</div>';
}
}
return text;
}
// Filter products that are actually mentioned in the AI response
function filterMentionedProducts(responseText,products){
if(!products||!products.length)return[];
let text=responseText.toLowerCase().replace(/<[^>]*>/g,' ');
let textClean=text.replace(/[^a-z0-9]/g,'');
let mentioned=[];
let scores=[];
for(let p of products){
let score=0;
// Check model code (strongest signal)
let model=(p.model||'').toLowerCase().replace(/[^a-z0-9]/g,'');
if(model.length>=4&&textClean.includes(model)){score+=10}
// Check significant name words (need >=3 matches for generic names)
let nameWords=p.name.toLowerCase().replace(/[|]/g,' ').split(/\s+/).filter(w=>w.length>3&&!['malloca','eurogold','grob','phụ','kiện'].includes(w));
let matchCount=nameWords.filter(w=>text.includes(w)).length;
if(matchCount>=3){score+=matchCount}
if(score>0)scores.push({p,score});
}
// Sort by score descending, take top matches
scores.sort((a,b)=>b.score-a.score);
return scores.slice(0,4).map(s=>s.p);
}
async function sendChat(){
if(chatBusy)return;
let input=document.getElementById('chatInput');
let msg=input.value.trim();
if(!msg)return;
input.value='';
input.style.height='auto';
document.getElementById('chatSuggestions').style.display='none';
// Show user message
appendMsg('user',msg.replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\n/g,'<br>'));
// Handle cart/quote/checkout commands
let msgLower=nVN(msg);
if(msgLower.match(/bao gia|lap bao gia|len bao gia|xuat bao gia|in bao gia/)){
// Extract SKU codes and add to cart
let skuPart=msg.replace(/^.*?(b[aá]o\s*gi[aá]|l[âaà]p|xu[aấ]t)/i,'').trim();
let codes=skuPart.split(/[,;\s]+/).map(s=>s.trim()).filter(s=>s.length>=3);
let addedNames=[];
if(codes.length>0&&typeof D!=='undefined'&&D.length){
codes.forEach(code=>{
let cNorm=code.toLowerCase().replace(/[\-\s_.\/]/g,'');
for(let i=0;i<D.length;i++){
let p=D[i];if(!p)continue;
let pSku=((p.sku||'')+(p.model||p.mod||'')).toLowerCase().replace(/[\-\s_.\/]/g,'');
let pName=(p.name||'').toLowerCase().replace(/[\-\s_.\/]/g,'');
if((pSku&&(pSku.indexOf(cNorm)!==-1||cNorm.indexOf(pSku)!==-1))||(cNorm.length>=5&&pName.indexOf(cNorm)!==-1)){
if(typeof addToCart==='function')addToCart(i);
addedNames.push((p.sku||p.model||p.mod||code)+' - '+(p.name||'').substring(0,30));
break;
}
}
});
}
// Now render LIVE quote from current cart
if(cart.length===0&&addedNames.length===0){
appendMsg('bot','Anh/chị cho em mã SP cần báo giá nhé! 😊<br>VD: <b>báo giá MH802</b> hoặc <b>báo giá MH802, MC9086HS</b>');
chatBusy=false;return;
}
// Build quote table from CURRENT CART (real-time)
let total=cart.reduce((s,c)=>s+c.priceNum*c.qty,0);
let rows=cart.map((c,i)=>'<tr><td style="padding:5px;text-align:center;font-size:.72rem">'+(i+1)+'</td><td style="padding:5px">'+(c.image?'<img src="'+c.image+'" style="width:32px;height:32px;object-fit:contain;border-radius:4px" referrerpolicy="no-referrer" onerror="this.style.display=\'none\'">':'')+'</td><td style="padding:5px;font-size:.7rem;font-weight:600">'+c.name.substring(0,30)+'<br><span style="font-size:.58rem;color:#94a3b8">'+(c.sku||c.model||'')+'</span></td><td style="padding:5px;text-align:center;font-size:.7rem">'+c.qty+'</td><td style="padding:5px;text-align:right;font-size:.7rem;font-weight:700;white-space:nowrap">'+(c.priceNum?c.priceNum.toLocaleString('vi-VN')+'đ':'LH')+'</td></tr>').join('');
let addedInfo=addedNames.length>0?'<div style="font-size:.7rem;color:#059669;margin-bottom:6px">✅ Đã thêm: '+addedNames.join(', ')+'</div>':'';
let qH='<div style="background:var(--l,#f8fafc);border-radius:10px;padding:10px;margin-top:6px;max-width:100%;overflow:hidden">'+addedInfo+'<div style="font-weight:800;font-size:.8rem;color:var(--p,#003f62);margin-bottom:6px">📋 BÁO GIÁ ('+cart.length+' SP)</div><div style="overflow-x:auto"><table style="width:100%;border-collapse:collapse;border:1px solid var(--gl,#e2e8f0);border-radius:6px;overflow:hidden"><thead><tr style="background:var(--p,#003f62);color:#fff"><th style="padding:4px;font-size:.6rem">#</th><th style="padding:4px;font-size:.6rem">Ảnh</th><th style="padding:4px;font-size:.6rem;text-align:left">SP</th><th style="padding:4px;font-size:.6rem">SL</th><th style="padding:4px;font-size:.6rem;text-align:right">Giá</th></tr></thead><tbody>'+rows+'</tbody><tfoot><tr style="background:rgba(0,63,98,.05)"><td colspan="4" style="padding:6px;font-weight:800;font-size:.72rem;text-align:right">TỔNG:</td><td style="padding:6px;font-weight:900;font-size:.8rem;color:var(--p,#003f62);text-align:right;white-space:nowrap">'+total.toLocaleString('vi-VN')+'đ</td></tr></tfoot></table></div><div style="display:flex;gap:4px;margin-top:8px;flex-wrap:wrap"><button onclick="if(typeof openQuotation===\'function\'){openQuotation();if(typeof closeChat===\'function\')closeChat();}" style="padding:5px 10px;background:var(--p,#003f62);color:#fff;border:none;border-radius:6px;font-size:.68rem;font-weight:700;cursor:pointer">📋 Lập phiếu báo giá</button><button onclick="if(window.VAI_ORDERS&&window.VAI_ORDERS.save)window.VAI_ORDERS.save()" style="padding:5px 10px;background:#7c3aed;color:#fff;border:none;border-radius:6px;font-size:.68rem;font-weight:700;cursor:pointer">💾 Lưu</button><button onclick="if(typeof shareQuoteImage===\'function\')shareQuoteImage()" style="padding:5px 10px;background:#25D366;color:#fff;border:none;border-radius:6px;font-size:.68rem;font-weight:700;cursor:pointer">📤 Chia sẻ</button></div><div style="font-size:.62rem;color:var(--g,#64748b);margin-top:6px;font-style:italic">💡 Nhắn thêm mã SP để bổ sung vào báo giá. VD: <b>báo giá K1603CL</b></div></div>';
appendMsg('bot','Dạ em báo giá ngay ạ! 📋'+qH);
chatBusy=false;return;
}
if(msgLower.match(/thanh toan|chuyen khoan|qr|quyet toan/)){
if(cart.length===0){
appendMsg('bot','Giỏ hàng đang trống ạ. Anh/chị chọn sản phẩm trước rồi em hỗ trợ thanh toán nhé! 😊');
chatBusy=false;return;
}
openCheckout();closeChat();
chatBusy=false;return;
}
if(msgLower.match(/^gio hang$|^xem gio$|^xem gio hang$/)){
if(cart.length===0){
appendMsg('bot','Giỏ hàng đang trống ạ. Anh/chị muốn tìm sản phẩm gì để em tư vấn? 😊');
}else{
let items=cart.map((c,i)=>`${i+1}. **${c.name}** x${c.qty} — ${c.priceNum?c.priceNum.toLocaleString('vi-VN')+'đ':'Liên hệ'}`).join('<br>');
let total=cart.reduce((s,c)=>s+c.priceNum*c.qty,0);
appendMsg('bot','🛒 Giỏ hàng hiện tại:<br>'+items+'<br><br><strong>Tổng: '+total.toLocaleString('vi-VN')+'đ</strong><br><br>Anh/chị có muốn em <strong>lên báo giá</strong> hoặc <strong>thanh toán</strong> không ạ?');
}
chatBusy=false;return;
}
// Search related products
let matchedProducts=searchProducts(msg);
let productContext=matchedProducts.length
? matchedProducts.map((p,i)=>{
let specs='';
if(p.specs&&Object.keys(p.specs).length){
specs=Object.entries(p.specs).slice(0,8).map(([k,v])=>k+': '+v).join('; ');
}
let features=(p.feats&&p.feats.length)?p.feats.slice(0,4).join('; '):'';
return `${i+1}. ${p.name}
Giá: ${p.price||'Liên hệ'} | ${p.cat} | ${p.brand}
${specs?'Thông số: '+specs:''}
${features?'Tính năng: '+features:''}
${p.summary?'Mô tả: '+p.summary.substring(0,200):''}`;
}).join('\n\n')
: '';
// Prepare messages for AI
chatHistory.push({role:'user',content:msg});
let systemPrompt=`Bạn là V.AI STUDIO — chuyên gia tư vấn thiết bị nhà bếp & khóa thông minh, với nhiều năm kinh nghiệm.
CÁCH TRẢ LỜI:
- Nói chuyện tự nhiên: xưng "em", gọi khách "anh/chị"
- PHÂN TÍCH nhu cầu cụ thể trước (diện tích bếp, số người, ngân sách)
- Giải thích TẠI SAO sản phẩm phù hợp — đừng chỉ liệt kê
- KHÔNG chèn link URL. KHÔNG dùng markdown link
- Dùng **in đậm** cho tên SP, emoji vừa phải
- Kết thúc bằng câu hỏi mở
KHẢ NĂNG ĐẶC BIỆT — hướng dẫn khách dùng:
- Nếu khách muốn mua, hướng dẫn: "Anh/chị gõ **'thêm giỏ'** hoặc bấm nút 🛒 bên cạnh sản phẩm"
- Nếu khách muốn báo giá, hướng dẫn: "Anh/chị gõ **'báo giá'** để em lên bảng báo giá chi tiết"
- Nếu khách muốn thanh toán, hướng dẫn: "Anh/chị gõ **'thanh toán'** để chuyển khoản qua QR"
- Nếu khách muốn xem giỏ hàng: "Anh/chị gõ **'giỏ hàng'** để xem"
${cart.length>0?'GIỎHÀNG HIỆN TẠI ('+cart.length+' SP):\\n'+cart.map(c=>c.name+' x'+c.qty+' — '+(c.priceNum?c.priceNum.toLocaleString('vi-VN')+'đ':'Liên hệ')).join('\\n')+'\\nTổng: '+cart.reduce((s,c)=>s+c.priceNum*c.qty,0).toLocaleString('vi-VN')+'đ':'Giỏ hàng đang trống.'}
${productContext ? 'DỮ LIỆU SẢN PHẨM:\\n'+productContext : 'Không có SP khớp — tư vấn bằng kiến thức chuyên môn.'}
THÔNG TIN:
- 2256 sản phẩm, 5 thương hiệu: Malloca, Eurogold, Grob, Canzy, Demax
- Malloca: thiết bị bếp cao cấp châu Âu
- Eurogold: phụ kiện tủ bếp inox 304
- Grob: phụ kiện nhập khẩu, ưu tiên tư vấn Grob cho phụ kiện tủ bếp; với kệ/giá gia vị 300mm dùng kết quả tìm kiếm nâng cao từ web
- Canzy: bếp từ, máy hút mùi
- Demax: khóa cửa thông minh Made in Malaysia
- Zalo tư vấn: 0981 873 395 (zalo.me/0981873395) hoặc 0918 258 385 (zalo.me/0918258385)`;
let messages=[{role:'system',content:systemPrompt}];
// Keep last 8 messages for context
let recent=chatHistory.slice(-8);
messages.push(...recent);
chatBusy=true;
document.getElementById('chatSend').disabled=true;
showTyping();
try{
// Auto-detect token from any variable name
let vars=window.huggingface?.variables||{};
let token=vars.HF_TOKEN||vars.VAISTUDIO||vars.AI_TOKEN||vars.TOKEN||'';
// Filter out non-token values
if(token&&!token.startsWith('hf_'))token='';
if(!token){
hideTyping();
// Smart local response without AI
let reply='';
if(matchedProducts.length>0){
reply='Tôi tìm thấy <strong>'+matchedProducts.length+' sản phẩm</strong> phù hợp với yêu cầu của bạn:';
}else{
reply='Tôi chưa tìm thấy sản phẩm cụ thể. Bạn có thể thử:<br>• Gõ tên sản phẩm (ví dụ: "bếp từ", "máy hút mùi")<br>• Gõ danh mục (ví dụ: "lò nướng", "chậu rửa")<br>• Gõ mã sản phẩm hoặc thương hiệu<br><br>💬 Hoặc nhắn Zalo <a href="https://zalo.me/0981873395" target="_blank" style="color:var(--p);font-weight:700">0981 873 395</a> để được tư vấn trực tiếp!';
}
appendMsg('bot',formatBotResponse(reply,matchedProducts));
chatHistory.push({role:'assistant',content:reply});
chatBusy=false;document.getElementById('chatSend').disabled=false;
return;
}
let resp=await fetch('https://router.huggingface.co/v1/chat/completions',{
method:'POST',
headers:{'Authorization':'Bearer '+token,'Content-Type':'application/json'},
body:JSON.stringify({
model:'meta-llama/Llama-3.3-70B-Instruct',
messages:messages,
max_tokens:500,
temperature:0.7,
top_p:0.9,
stream:true
})
});
hideTyping();
if(!resp.ok){
let err=await resp.text();
console.error('Chat API error:',resp.status,err);
appendMsg('bot',formatBotResponse('Xin lỗi, hệ thống AI đang bận. Đây là sản phẩm tôi tìm được cho anh/chị:',matchedProducts));
chatBusy=false;document.getElementById('chatSend').disabled=false;
return;
}
// Stream response — typewriter effect
let fullReply='';
let botDiv=appendMsg('bot','<span class="typing-cursor"></span>');
let reader=resp.body.getReader();
let decoder=new TextDecoder();
let buffer='';
while(true){
let {done,value}=await reader.read();
if(done)break;
buffer+=decoder.decode(value,{stream:true});
let lines=buffer.split('\n');
buffer=lines.pop();
for(let line of lines){
line=line.trim();
if(!line.startsWith('data: '))continue;
let data=line.slice(6);
if(data==='[DONE]')break;
try{
let j=JSON.parse(data);
let delta=j.choices?.[0]?.delta?.content;
if(delta){
fullReply+=delta;
botDiv.innerHTML=formatBotResponse(fullReply,null)+'<span class="typing-cursor" style="display:inline;animation:blink .8s step-end infinite;color:var(--p)"></span>';
document.getElementById('chatBody').scrollTop=document.getElementById('chatBody').scrollHeight;
}
}catch(e){}
}
}
// Remove cursor, add product cards
botDiv.innerHTML=formatBotResponse(fullReply,matchedProducts);
chatHistory.push({role:'assistant',content:fullReply});
document.getElementById('chatBody').scrollTop=document.getElementById('chatBody').scrollHeight;
}catch(e){
hideTyping();
console.error('Chat error:',e);
appendMsg('bot',formatBotResponse('Xin lỗi, đã có lỗi xảy ra. Đây là sản phẩm tôi tìm được cho anh/chị:',matchedProducts));
}
chatBusy=false;
document.getElementById('chatSend').disabled=false;
}
// Legacy compatibility
function copyProductLink(slug){shareProduct(slug)}
function copyCurrentLink(){shareCurrentPage()}
function fallbackCopy(text){
if(navigator.clipboard){navigator.clipboard.writeText(text).then(()=>showToast('Đã sao chép!'))}
else{let ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;top:-9999px';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);showToast('Đã sao chép!')}
}
// ===== INLINE QUOTE VIEW + EXCEL EXPORT =====
function viewQuoteInline(btn){
let container=btn.closest('div').parentElement;
let existing=container.querySelector('.quote-inline-detail');
if(existing){existing.remove();return;}
let today=new Date().toLocaleDateString('vi-VN');
let total=cart.reduce((s,c)=>s+c.priceNum*c.qty,0);
let rows=cart.map((c,i)=>{
let unit=c.priceNum?c.priceNum.toLocaleString('vi-VN')+'đ':'Liên hệ';
let sub=(c.priceNum*c.qty).toLocaleString('vi-VN')+'đ';
return `<tr style="border-bottom:1px solid var(--gl)">
<td style="padding:10px 8px;font-size:.8rem;text-align:center;color:var(--g)">${i+1}</td>
<td style="padding:10px 8px"><div style="font-size:.8rem;font-weight:600">${c.name}</div></td>
<td style="padding:10px 8px;text-align:center;font-size:.8rem">${c.qty}</td>
<td style="padding:10px 8px;text-align:right;font-size:.8rem;white-space:nowrap">${unit}</td>
<td style="padding:10px 8px;text-align:right;font-size:.85rem;font-weight:700;color:var(--p);white-space:nowrap">${sub}</td>
</tr>`;}).join('');
let html=`<div class="quote-inline-detail" style="margin-top:12px;background:#fff;border-radius:12px;border:2px solid var(--p);overflow:hidden">
<div style="background:linear-gradient(135deg,var(--p),#0077b6);color:#fff;padding:16px 18px">
<div style="display:flex;align-items:center;gap:10px;margin-bottom:6px">
<img src="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/logo_200.png" style="height:30px;filter:brightness(10)" onerror="this.style.display='none'">
<div style="font-weight:800;font-size:1rem">V.AI STUDIO</div>
</div>
<div style="font-size:.72rem;opacity:.8">Niềm tin khách hàng là tài sản của chúng tôi</div>
</div>
<div style="padding:16px 18px">
<div style="display:flex;justify-content:space-between;margin-bottom:12px">
<div><div style="font-size:.7rem;color:var(--g);text-transform:uppercase;letter-spacing:1px">Báo giá</div><div style="font-size:1.1rem;font-weight:800;color:var(--d)">BG-${Date.now().toString(36).toUpperCase()}</div></div>
<div style="text-align:right"><div style="font-size:.72rem;color:var(--g)">Ngày lập</div><div style="font-size:.85rem;font-weight:600">${today}</div></div>
</div>
<table style="width:100%;border-collapse:collapse">
<thead><tr style="background:var(--l)">
<th style="padding:8px;font-size:.7rem;text-align:center;color:var(--g);font-weight:700">STT</th>
<th style="padding:8px;font-size:.7rem;text-align:left;color:var(--g);font-weight:700">SẢN PHẨM</th>
<th style="padding:8px;font-size:.7rem;text-align:center;color:var(--g);font-weight:700">SL</th>
<th style="padding:8px;font-size:.7rem;text-align:right;color:var(--g);font-weight:700">ĐƠN GIÁ</th>
<th style="padding:8px;font-size:.7rem;text-align:right;color:var(--g);font-weight:700">THÀNH TIỀN</th>
</tr></thead>
<tbody>${rows}</tbody>
</table>
<div style="display:flex;justify-content:flex-end;padding:12px 8px;background:var(--l);border-radius:8px;margin-top:8px">
<div style="text-align:right"><div style="font-size:.72rem;color:var(--g)">TỔNG CỘNG</div><div style="font-size:1.2rem;font-weight:900;color:var(--p)">${total.toLocaleString('vi-VN')}đ</div></div>
</div>
<div style="margin-top:12px;padding-top:12px;border-top:1px dashed var(--gl);font-size:.72rem;color:var(--g);line-height:1.6">
💬 Zalo: <a href="https://zalo.me/0981873395" target="_blank" style="color:var(--p);font-weight:600">0981 873 395</a> · <a href="https://zalo.me/0918258385" target="_blank" style="color:var(--p);font-weight:600">0918 258 385</a><br>
💡 Giá đã bao gồm VAT · Miễn phí giao hàng trong TPHCM
</div>
</div>
</div>`;
container.insertAdjacentHTML('beforeend',html);
document.getElementById('chatBody').scrollTop=document.getElementById('chatBody').scrollHeight;
}
function exportQuoteExcel(){
if(!cart.length)return;
let today=new Date().toLocaleDateString('vi-VN');
let total=cart.reduce((s,c)=>s+c.priceNum*c.qty,0);
// Build CSV with BOM for Excel UTF-8
let csv='\uFEFF';
csv+='BÁO GIÁ V.AI STUDIO\n';
csv+='Ngày: '+today+'\n\n';
csv+='STT,Mã SP,Sản phẩm,SL,Đơn giá,Thành tiền\n';
cart.forEach((c,i)=>{
let name=c.name.replace(/,/g,' -');
let p=D[c.idx];
let sku=p?p.sku||p.model:'';
csv+=(i+1)+','+sku+','+name+','+c.qty+','+(c.priceNum||0)+','+(c.priceNum*c.qty)+'\n';
});
csv+=',,,,,\n';
csv+=',,,,TỔNG CỘNG,'+total+'\n';
csv+='\n';
csv+='Zalo: 0981 873 395 | 0918 258 385\n';
csv+='Niềm tin khách hàng là tài sản của chúng tôi\n';
let blob=new Blob([csv],{type:'text/csv;charset=utf-8'});
let url=URL.createObjectURL(blob);
let a=document.createElement('a');
a.href=url;a.download='BaoGia_VAI_STUDIO_'+today.replace(/\//g,'-')+'.csv';
a.click();URL.revokeObjectURL(url);
showToast('Đã tải báo giá Excel!');
}
// ===== QUOTATION SYSTEM =====
function openQuotation(){
closeCart();
let overlay=document.getElementById('quoteOverlay');
overlay.classList.add('open');
document.body.style.overflow='hidden';
// Set default date
document.getElementById('qcDate').value=new Date().toISOString().split('T')[0];
// Auto-generate order code
generateOrderCode();
// Listen for name/date changes to regenerate
document.getElementById('qcName').addEventListener('input',generateOrderCode);
document.getElementById('qcDate').addEventListener('change',generateOrderCode);
// Build product table
renderQuoteTable();
}
function generateOrderCode(){
let name=(document.getElementById('qcName').value||'').trim();
let date=document.getElementById('qcDate').value||'';
// Extract initials from name (first letter of each word, uppercase)
let initials=name.split(/\s+/).map(w=>w.charAt(0)).join('').toUpperCase()||'KH';
// Format date as DDMMYY
let dParts=date.split('-');// YYYY-MM-DD
let datePart=dParts.length===3?(dParts[2]+dParts[1]+dParts[0].slice(2)):(new Date().toLocaleDateString('en-GB').replace(/\//g,'').slice(0,6));
let code=initials+datePart;
document.getElementById('qcCompany').value=code;
}
function closeQuotation(){
document.getElementById('quoteOverlay').classList.remove('open');
document.body.style.overflow='';
}
let _quoteUnlocked=false;
function checkQuoteAccess(){
let code=document.getElementById('qcAccessCode').value;
let status=document.getElementById('qcAccessStatus');
let icon=document.getElementById('qcLockIcon');
let promptBox=document.getElementById('aiPromptBox');
if(code==='V.AISTUDIO'){
_quoteUnlocked=true;document.body.classList.add('vas-unlocked');localStorage.setItem('vas_quote_unlocked','1');
status.textContent='🔓 Đã mở';
status.style.color='#28a745';
icon.className='fas fa-lock-open';icon.style.color='#28a745';
promptBox.style.display='block';document.querySelectorAll('[data-order-btn]').forEach(function(b){b.style.display=''});
document.querySelectorAll('.qt-disc').forEach(el=>{el.disabled=false;el.style.background='#fff';el.style.cursor='text'});
}else{
_quoteUnlocked=false;
status.textContent='🔒';
status.style.color='var(--g)';
icon.className='fas fa-lock';icon.style.color='var(--g)';
promptBox.style.display='none';
document.querySelectorAll('.qt-disc').forEach(el=>{el.disabled=true;el.style.background='var(--l)';el.style.cursor='not-allowed'});
}
}
async function applyAiDiscount(){
let inp=document.getElementById('qcAiPrompt');
let txt=inp?inp.value.trim():'';
let btn=document.getElementById('aiDiscBtn');
let statusEl=document.getElementById('aiDiscStatus');
if(!txt){if(statusEl)statusEl.textContent='VD: Malloca ck 35%, giao hàng 200k, lắp đặt 500k, cọc 5tr, ghi chú: giao thứ 7';return}
let commands=txt.split(/[,;\n]+/).map(c=>c.trim()).filter(c=>c.length>1);
let results=[];
let applied=0;
let fees=[];
let deposit=0;
let depositPct=0;
let noteText='';
commands.forEach(cmd=>{
let cmdLow=cmd.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/[đĐ]/g,'d');
// GHI CHÚ: ghi chú: xxx hoặc note: xxx
if(cmdLow.match(/^(ghi chu|note|luu y)/)){
noteText=cmd.replace(/^(ghi chú|ghi chu|note|lưu ý|luu y)[:\s]*/i,'').trim();
results.push('📝 '+noteText);
return;
}
// CK theo brand: malloca ck 35%
let brandCkMatch=cmdLow.match(/^(\w+)\s+(?:ck|chiet khau|giam|discount)\s*(\d+)\s*%?/);
if(brandCkMatch){
let brandName=brandCkMatch[1];let pct=parseFloat(brandCkMatch[2]);
if(pct>0&&pct<=90){
let count=0;
cart.forEach((c,i)=>{
if(c.priceNum>0){
let pBrand=(c.brand||'').toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/[đĐ]/g,'d');
let pName=(c.name||'').toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/[đĐ]/g,'d');
if(pBrand.indexOf(brandName)!==-1||pName.indexOf(brandName)!==-1){
let dp=Math.round(c.priceNum*(1-pct/100));
let el=document.querySelector('.qt-disc[data-idx="'+i+'"]');
if(el){el.value=dp.toLocaleString('vi-VN');count++;}
}
}
});
if(count>0){results.push(brandCkMatch[1]+' CK '+pct+'% ('+count+' SP)');applied+=count;}
return;
}
}
// Giá theo mã SP: gov304190 giá 7tr
let skuPriceMatch=cmdLow.match(/^([a-z0-9_\-]+)\s+(?:gia|price)\s*([\d.,]+)\s*(tr|trieu|k|m|nghin)?/);
if(skuPriceMatch){
let skuQuery=skuPriceMatch[1].replace(/[.\-_ ]/g,'');
let priceVal=parseFloat(skuPriceMatch[2].replace(/\./g,'').replace(',','.'));
let unit=(skuPriceMatch[3]||'').toLowerCase();
if(unit==='tr'||unit==='trieu'||unit==='m')priceVal*=1000000;
else if(unit==='k'||unit==='nghin')priceVal*=1000;
else if(priceVal>0&&priceVal<500)priceVal*=1000000;
if(priceVal>0){
cart.forEach((c,i)=>{
let cSku=((c.sku||'')+(c.model||c.mod||'')).toLowerCase().replace(/[.\-_ ]/g,'');
if(cSku.indexOf(skuQuery)!==-1||skuQuery.indexOf(cSku)!==-1){
let el=document.querySelector('.qt-disc[data-idx="'+i+'"]');
if(el){el.value=priceVal.toLocaleString('vi-VN');applied++;results.push((c.sku||skuQuery)+' → '+priceVal.toLocaleString('vi-VN')+'đ');}
}
});
}
return;
}
// CK tất cả: ck 30%
let globalCkMatch=cmdLow.match(/^(?:ck|chiet khau|giam|discount)\s*(\d+)\s*%/);
if(globalCkMatch){
let pct=parseFloat(globalCkMatch[1]);
if(pct>0&&pct<=90){
let count=0;
cart.forEach((c,i)=>{
if(c.priceNum>0){
let dp=Math.round(c.priceNum*(1-pct/100));
let el=document.querySelector('.qt-disc[data-idx="'+i+'"]');
if(el){el.value=dp.toLocaleString('vi-VN');count++;}
}
});
results.push('CK '+pct+'% tất cả ('+count+' SP)');applied+=count;
}
return;
}
// CỌC: cọc 5tr, đặt cọc 30%
let cocMatch=cmdLow.match(/(?:coc|dat coc|deposit)\s*([\d.,]+)\s*(k|tr|trieu|%|m)?/);
if(cocMatch){
let cocVal=parseFloat(cocMatch[1].replace(/\./g,'').replace(',','.'));
let cu=(cocMatch[2]||'').toLowerCase();
if(cu==='%'){depositPct=cocVal;results.push('Cọc: '+cocVal+'%');}
else{
if(cu==='k')cocVal*=1000;else if(cu==='tr'||cu==='trieu'||cu==='m')cocVal*=1000000;
else if(cocVal>0&&cocVal<500)cocVal*=1000000;
deposit=cocVal;results.push('Cọc: '+cocVal.toLocaleString('vi-VN')+'đ');
}
return;
}
// PHỤ PHÍ: giao hàng 200k, lắp đặt 500k, phí khác 100k, vận chuyển 300k
let feeMatch=cmdLow.match(/^(giao hang|giao|lap dat|lap|van chuyen|ship|phi giao|phi lap|phi khac|phu phi|phi)\s*([\d.,]+)\s*(k|tr|trieu|m|nghin)?/);
if(feeMatch){
let feeAmt=parseFloat(feeMatch[2].replace(/\./g,'').replace(',','.'));
let u=(feeMatch[3]||'').toLowerCase();
if(u==='k'||u==='nghin')feeAmt*=1000;
else if(u==='tr'||u==='trieu'||u==='m')feeAmt*=1000000;
else if(feeAmt>0&&feeAmt<500)feeAmt*=1000;
// Label
let labelMap={'giao hang':'Phí giao hàng','giao':'Phí giao hàng','lap dat':'Phí lắp đặt','lap':'Phí lắp đặt','van chuyen':'Phí vận chuyển','ship':'Phí vận chuyển','phi giao':'Phí giao hàng','phi lap':'Phí lắp đặt','phi khac':'Phí khác','phu phi':'Phụ phí','phi':'Phụ phí'};
let feeLabel=labelMap[feeMatch[1]]||'Phụ phí';
fees.push({label:feeLabel,amount:feeAmt});
results.push(feeLabel+': +'+feeAmt.toLocaleString('vi-VN')+'đ');
return;
}
// Fallback: any line with number → treat as fee
let numMatch=cmdLow.match(/([\d.,]+)\s*(k|tr|trieu|m|nghin)?/);
if(numMatch){
let amt=parseFloat(numMatch[1].replace(/\./g,'').replace(',','.'));
let u2=(numMatch[2]||'').toLowerCase();
if(u2==='k'||u2==='nghin')amt*=1000;
else if(u2==='tr'||u2==='trieu'||u2==='m')amt*=1000000;
if(amt>0){
let lb=cmdLow.replace(numMatch[0],'').replace(/[^a-z\s]/g,'').trim();
if(!lb||lb.length<2)lb='Phụ phí';
else lb=lb.charAt(0).toUpperCase()+lb.slice(1);
fees.push({label:lb,amount:amt});
results.push(lb+': +'+amt.toLocaleString('vi-VN')+'đ');
}
return;
}
});
updateQuoteTotal();
// Render fees + cọc + ghi chú vào bảng
document.querySelectorAll('.qt-extra-row').forEach(r=>r.remove());
let tfoot=document.querySelector('.quote-table tfoot');
if(tfoot){
let totalCell=document.getElementById('quoteTotalCell');
let totalVal=parseInt((totalCell?totalCell.textContent:'0').replace(/\D/g,''))||0;
let insertBefore=tfoot.querySelector('.quote-total-row');
// Thêm dòng phụ phí
fees.forEach(f=>{
let row=document.createElement('tr');row.className='qt-extra-row';
row.innerHTML='<td colspan="8" style="text-align:right;padding:6px 12px;font-size:.82rem;color:#92400e;font-weight:600">'+f.label+':</td><td class="qt-price" style="font-size:.82rem;color:#e53935;font-weight:700">+'+f.amount.toLocaleString('vi-VN')+'đ</td><td></td>';
if(insertBefore)tfoot.insertBefore(row,insertBefore);else tfoot.appendChild(row);
totalVal+=f.amount;
});
// Cập nhật tổng
if(fees.length&&totalCell)totalCell.textContent=totalVal.toLocaleString('vi-VN')+'đ';
// Cọc
if(deposit>0||depositPct>0){
let cocAmt=deposit>0?deposit:Math.round(totalVal*depositPct/100);
let cocRow=document.createElement('tr');cocRow.className='qt-extra-row';
cocRow.innerHTML='<td colspan="8" style="text-align:right;padding:6px 12px;font-size:.82rem;color:#2563eb;font-weight:600">Đặt cọc'+(depositPct>0?' ('+depositPct+'%)':'')+':</td><td class="qt-price" style="font-size:.82rem;color:#2563eb;font-weight:700">'+cocAmt.toLocaleString('vi-VN')+'đ</td><td></td>';
tfoot.appendChild(cocRow);
let remainRow=document.createElement('tr');remainRow.className='qt-extra-row';
remainRow.innerHTML='<td colspan="8" style="text-align:right;padding:6px 12px;font-size:.82rem;color:#059669;font-weight:600">Còn lại:</td><td class="qt-price" style="font-size:.82rem;color:#059669;font-weight:700">'+(totalVal-cocAmt).toLocaleString('vi-VN')+'đ</td><td></td>';
tfoot.appendChild(remainRow);
}
}
// Điền ghi chú vào ô ghi chú (nếu có)
if(noteText){
let noteInputs=document.querySelectorAll('.qt-note,textarea[placeholder*="ghi chú"],input[placeholder*="ghi chú"]');
noteInputs.forEach(el=>{el.value=noteText;});
// Also try quote table note cells
document.querySelectorAll('input.qt-note,textarea.qt-note').forEach(el=>{if(!el.value)el.value=noteText;});
}
if(statusEl){
if(results.length)statusEl.innerHTML='✅ '+results.join(' | ');
else statusEl.textContent='⚠️ Không nhận diện. VD: Malloca ck 35%, giao hàng 200k, lắp 500k, cọc 5tr, ghi chú: giao thứ 7';
}
}
function renderQuoteTable(){
let tbody=document.getElementById('quoteTableBody');
let locked=!_quoteUnlocked;
tbody.innerHTML=cart.map((c,i)=>{
let product=D[c.idx]||{};
let model=product.model||product.sku||'';
let specs='';
if(product.specs&&Object.keys(product.specs).length){
specs=Object.entries(product.specs).slice(0,3).map(([k,v])=>k+': '+v).join(', ');
}
let lineTotal=c.priceNum*c.qty;
let discStyle='width:80px;padding:5px 6px;border:1.5px solid var(--gl);border-radius:6px;font-size:.78rem;text-align:right;font-family:inherit;outline:none;';
discStyle+=locked?'background:var(--l);cursor:not-allowed':'background:#fff';
return `<tr>
<td style="text-align:center">${i+1}</td>
<td><img class="qt-img" src="${c.image}" referrerpolicy="no-referrer" alt="${c.name}" onerror="this.style.display='none'" style="width:72px;height:72px;object-fit:contain"></td>
<td class="qt-name">${c.name}</td>
<td style="text-align:center">${model}</td>
<td style="font-size:.72rem;color:var(--g);max-width:160px">${specs}</td>
<td style="text-align:center">${c.qty}</td>
<td class="qt-price">${c.priceNum>0?c.priceNum.toLocaleString('vi-VN')+'đ':'Liên hệ'}</td>
<td><input class="qt-disc" data-idx="${i}" value="${c.priceNum>0?c.priceNum.toLocaleString('vi-VN'):''}" oninput="updateQuoteTotal()" ${locked?'disabled':''} style="${discStyle}"></td>
<td class="qt-price qt-linetotal" data-idx="${i}">${lineTotal>0?lineTotal.toLocaleString('vi-VN')+'đ':''}</td>
<td><input class="qt-note" data-idx="${i}" placeholder="Ghi chú"></td>
</tr>`;
}).join('');
updateQuoteTotal();
}
function updateQuoteTotal(){
let total=0;
cart.forEach((c,i)=>{
let discInput=document.querySelector(`.qt-disc[data-idx="${i}"]`);
let discVal=discInput?(parseInt(discInput.value.replace(/\D/g,''))||0)||0:c.priceNum;
let lineTotal=discVal*c.qty;
let cell=document.querySelector(`.qt-linetotal[data-idx="${i}"]`);
if(cell)cell.textContent=lineTotal>0?lineTotal.toLocaleString('vi-VN')+'đ':'';
total+=lineTotal;
});
document.getElementById('quoteTotalCell').textContent=total.toLocaleString('vi-VN')+'đ';
}
function getQuoteData(){
let customer={
name:document.getElementById('qcName').value||'',
phone:document.getElementById('qcPhone').value||'',
email:document.getElementById('qcEmail').value||'',
addr:document.getElementById('qcAddr').value||'',
orderCode:document.getElementById('qcCompany').value||'',
date:document.getElementById('qcDate').value||new Date().toISOString().split('T')[0]
};
let items=cart.map((c,i)=>{
let product=D[c.idx]||{};
let discInput=document.querySelector(`.qt-disc[data-idx="${i}"]`);
let noteInput=document.querySelector(`.qt-note[data-idx="${i}"]`);
let discPrice=discInput?parseInt(discInput.value.replace(/\D/g,''))||0:c.priceNum;
return{
stt:i+1, name:c.name, model:product.model||product.sku||'',
specs:product.specs?Object.entries(product.specs).slice(0,3).map(([k,v])=>k+': '+v).join(', '):'',
image:c.image, qty:c.qty, price:c.priceNum, discPrice:discPrice,
total:discPrice*c.qty, note:noteInput?noteInput.value:''
};
});
let grandTotal=items.reduce((s,it)=>s+it.total,0);
return{customer,items,grandTotal};
}
function fmtVND(n){return n>0?n.toLocaleString('vi-VN')+'đ':'Liên hệ'}
// ===== EXCEL EXPORT WITH FORMULAS =====
async function exportExcel(){
let qd=getQuoteData();
let wb=new ExcelJS.Workbook();
wb.creator='Malloca Vietnam';
let ws=wb.addWorksheet('Báo Giá');
ws.columns=[
{key:'stt',width:6},{key:'name',width:32},{key:'model',width:16},{key:'specs',width:28},
{key:'image',width:18},{key:'qty',width:8},{key:'price',width:16},{key:'disc',width:16},
{key:'total',width:18},{key:'note',width:18}
];
// Header
ws.mergeCells('A1','J1');
let h1=ws.getCell('A1');
h1.value='V.AI STUDIO - Niềm tin khách hàng là tài sản của chúng tôi';
h1.font={name:'Arial',bold:true,size:14,color:{argb:'FF003F62'}};
h1.alignment={horizontal:'center',vertical:'middle'};
h1.fill={type:'pattern',pattern:'solid',fgColor:{argb:'FFF0F7FF'}};
ws.getRow(1).height=30;
ws.mergeCells('A2','J2');
let h2=ws.getCell('A2');
h2.value='BẢNG BÁO GIÁ';
h2.font={name:'Arial',bold:true,size:16,color:{argb:'FFDB9815'}};
h2.alignment={horizontal:'center',vertical:'middle'};
ws.getRow(2).height=28;
// Customer info
let r=4;
let ci=[['Khách hàng:',qd.customer.name],['SĐT:',qd.customer.phone],['Email:',qd.customer.email],
['Địa chỉ:',qd.customer.addr],['Mã đơn hàng:',qd.customer.orderCode],['Ngày:',qd.customer.date]];
ci.forEach(pair=>{
ws.getCell('A'+r).value=pair[0];ws.getCell('A'+r).font={bold:true,size:10};
ws.getCell('B'+r).value=pair[1];ws.getCell('B'+r).font={size:10};
r++;
});
r++;
let headerR=r;
// Table header
let headers=['STT','Tên sản phẩm','Mã SP','Thông tin SP','Hình ảnh','SL','Đơn giá','Đơn giá CK','Thành tiền','Ghi chú'];
let headerRow=ws.getRow(r);
headers.forEach((h,i)=>{headerRow.getCell(i+1).value=h});
headerRow.height=26;
headerRow.eachCell(cell=>{
cell.font={bold:true,color:{argb:'FFFFFFFF'},name:'Arial',size:9};
cell.fill={type:'pattern',pattern:'solid',fgColor:{argb:'FF003F62'}};
cell.border={top:{style:'thin'},left:{style:'thin'},bottom:{style:'thin'},right:{style:'thin'}};
cell.alignment={horizontal:'center',vertical:'middle',wrapText:true};
});
r++;
let dataStartR=r;
// Data rows with FORMULAS
// Detect if AI applied a percentage discount
let discountPct=0;
if(qd.items.length>0&&qd.items[0].price>0){
let ratio=qd.items[0].discPrice/qd.items[0].price;
let allSameRatio=qd.items.every(it=>it.price===0||Math.abs(it.discPrice/it.price-ratio)<0.005);
if(allSameRatio&&ratio<1&&ratio>0)discountPct=Math.round(ratio*100);
}
qd.items.forEach((item,idx)=>{
let row=ws.getRow(r);
row.height=64;
// A: STT
let cA=row.getCell(1);cA.value=item.stt;
cA.alignment={horizontal:'center',vertical:'middle'};
cA.font={size:10};
// B: Tên SP
let cB=row.getCell(2);cB.value=item.name;
cB.alignment={vertical:'middle',wrapText:true};
cB.font={size:10,bold:true,color:{argb:'FF1A1A1A'}};
// C: Mã SP
let cC=row.getCell(3);cC.value=item.model;
cC.alignment={horizontal:'center',vertical:'middle'};
cC.font={size:9,color:{argb:'FF003F62'}};
// D: Thông tin
let cD=row.getCell(4);cD.value=item.specs;
cD.alignment={vertical:'middle',wrapText:true};
cD.font={size:8,color:{argb:'FF888888'}};
// E: Hình ảnh (embedded from DOM)
row.getCell(5).value='';
row.getCell(5).alignment={horizontal:'center',vertical:'middle'};
// Try to embed image from the quote table's <img> element
try{
let imgEl=document.querySelector('.qt-img[alt="'+item.name.substring(0,30)+'"]')||document.querySelectorAll('.qt-img')[idx];
if(imgEl&&imgEl.complete&&imgEl.naturalWidth>0){
let cvs=document.createElement('canvas');
cvs.width=Math.min(imgEl.naturalWidth,120);
cvs.height=Math.min(imgEl.naturalHeight,120);
let ctx=cvs.getContext('2d');
ctx.drawImage(imgEl,0,0,cvs.width,cvs.height);
try{
let b64=cvs.toDataURL('image/jpeg',0.7).split(',')[1];
let imgId=wb.addImage({base64:b64,extension:'jpeg'});
ws.addImage(imgId,{tl:{col:4,row:r-1},ext:{width:70,height:70}});
}catch(ce){}
}
}catch(ie){}
// F: SL
let cF=row.getCell(6);cF.value=item.qty;
cF.alignment={horizontal:'center',vertical:'middle'};
cF.font={size:10,bold:true};
// G: Đơn giá
let cG=row.getCell(7);cG.value=Number(item.price)||0;cG.numFmt='#,##0';
cG.numFmt='#,##0';
cG.alignment={horizontal:'right',vertical:'middle'};
cG.font={size:10};
// H: Đơn giá CK = FORMULA if %, else value
let cH=row.getCell(8);
if(discountPct>0&&discountPct<100){
cH.value={formula:`G${r}*${discountPct}%`};
}else{
cH.value=Number(item.discPrice)||Number(item.price)||0;
}
cH.numFmt='#,##0';
cH.alignment={horizontal:'right',vertical:'middle'};
if(item.discPrice<item.price){cH.font={size:10,color:{argb:'FFDC3545'},bold:true}}else{cH.font={size:10}}
// I: Thành tiền = H × F
let cI=row.getCell(9);
cI.value={formula:`H${r}*F${r}`};cI.numFmt='#,##0';
cI.numFmt='#,##0';
cI.alignment={horizontal:'right',vertical:'middle'};
cI.font={size:10,bold:true,color:{argb:'FF003F62'}};
// J: Ghi chú
let cJ=row.getCell(10);cJ.value=item.note;
cJ.alignment={vertical:'middle',wrapText:true};
cJ.font={size:9};
// Borders + alternate colors
for(let c=1;c<=10;c++){
row.getCell(c).border={top:{style:'thin',color:{argb:'FFD0D5DD'}},left:{style:'thin',color:{argb:'FFD0D5DD'}},bottom:{style:'thin',color:{argb:'FFD0D5DD'}},right:{style:'thin',color:{argb:'FFD0D5DD'}}};
}
if(item.stt%2===0){for(let c=1;c<=10;c++){row.getCell(c).fill={type:'pattern',pattern:'solid',fgColor:{argb:'FFF8FAFC'}}}}
r++;
});
let dataEndR=r-1;
// ═══ SUMMARY SECTION ═══
let sumStyle={border:{top:{style:'thin',color:{argb:'FFD0D5DD'}},bottom:{style:'thin',color:{argb:'FFD0D5DD'}}}};
// Number format for prices
ws.getColumn(7).numFmt='#,##0';ws.getColumn(8).numFmt='#,##0';ws.getColumn(9).numFmt='#,##0';
// TỔNG CỘNG
let tr=r;
ws.mergeCells(tr,1,tr,8);
ws.getCell('A'+tr).value='TỔNG CỘNG:';
ws.getCell('A'+tr).font={bold:true,size:11,color:{argb:'FF003F62'}};
ws.getCell('A'+tr).alignment={horizontal:'right',vertical:'middle'};
ws.getCell('I'+tr).value={formula:`SUM(I${dataStartR}:I${dataEndR})`};
ws.getCell('I'+tr).numFmt='#,##0';
ws.getCell('I'+tr).font={bold:true,size:12,color:{argb:'FF003F62'}};
ws.getCell('I'+tr).alignment={horizontal:'right',vertical:'middle'};
ws.getRow(tr).height=28;
for(let c=1;c<=10;c++){ws.getRow(tr).getCell(c).border={top:{style:'medium',color:{argb:'FF003F62'}},bottom:{style:'thin',color:{argb:'FFD0D5DD'}}};ws.getRow(tr).getCell(c).fill={type:'pattern',pattern:'solid',fgColor:{argb:'FFE8F0FE'}}}
r=tr+1;
// ĐẶT CỌC (30%)
ws.mergeCells(r,1,r,8);
ws.getCell('A'+r).value='Đặt cọc (30%):';
ws.getCell('A'+r).font={size:10,color:{argb:'FF555555'}};
ws.getCell('A'+r).alignment={horizontal:'right',vertical:'middle'};
ws.getCell('I'+r).value={formula:`ROUND(I${tr}*30%,0)`};
ws.getCell('I'+r).numFmt='#,##0';
ws.getCell('I'+r).font={bold:true,size:10,color:{argb:'FFDB9815'}};
ws.getCell('I'+r).alignment={horizontal:'right',vertical:'middle'};
ws.getRow(r).height=22;
r++;
// CÒN LẠI
ws.mergeCells(r,1,r,8);
ws.getCell('A'+r).value='Còn lại:';
ws.getCell('A'+r).font={size:10,color:{argb:'FF555555'}};
ws.getCell('A'+r).alignment={horizontal:'right',vertical:'middle'};
ws.getCell('I'+r).value={formula:`I${tr}-I${r-1}`};
ws.getCell('I'+r).numFmt='#,##0';
ws.getCell('I'+r).font={size:10};
ws.getCell('I'+r).alignment={horizontal:'right',vertical:'middle'};
ws.getRow(r).height=22;
r++;
// PHỤ PHÍ
ws.mergeCells(r,1,r,8);
ws.getCell('A'+r).value='Phụ phí (nếu có):';
ws.getCell('A'+r).font={size:10,color:{argb:'FF555555'}};
ws.getCell('A'+r).alignment={horizontal:'right',vertical:'middle'};
ws.getCell('I'+r).value=0;
ws.getCell('I'+r).numFmt='#,##0';
ws.getCell('I'+r).font={size:10,italic:true};
ws.getCell('I'+r).alignment={horizontal:'right',vertical:'middle'};
ws.getCell('I'+r).fill={type:'pattern',pattern:'solid',fgColor:{argb:'FFFFFFEE'}};
let surchargeR=r;
ws.getRow(r).height=22;
r++;
// TỔNG THANH TOÁN
ws.mergeCells(r,1,r,8);
ws.getCell('A'+r).value='TỔNG THANH TOÁN:';
ws.getCell('A'+r).font={bold:true,size:12,color:{argb:'FFFFFFFF'}};
ws.getCell('A'+r).alignment={horizontal:'right',vertical:'middle'};
ws.getCell('A'+r).fill={type:'pattern',pattern:'solid',fgColor:{argb:'FF003F62'}};
ws.getCell('I'+r).value={formula:`I${tr}+I${surchargeR}`};
ws.getCell('I'+r).numFmt='#,##0';
ws.getCell('I'+r).font={bold:true,size:13,color:{argb:'FFFFFFFF'}};
ws.getCell('I'+r).alignment={horizontal:'right',vertical:'middle'};
ws.getCell('I'+r).fill={type:'pattern',pattern:'solid',fgColor:{argb:'FF003F62'}};
for(let c=1;c<=10;c++){ws.getRow(r).getCell(c).fill={type:'pattern',pattern:'solid',fgColor:{argb:'FF003F62'}}}
ws.getRow(r).height=30;
r+=2;
// Footer notes
ws.mergeCells(r,1,r,10);
ws.getCell('A'+r).value='📌 Miễn phí giao hàng trong TPHCM. Giá đã bao gồm VAT (Hỗ trợ trị giá đơn hàng VAT xuất theo đúng số tiền khác mã).';
ws.getCell('A'+r).font={italic:true,size:9,color:{argb:'FF888888'}};
ws.getCell('A'+r).alignment={wrapText:true};
r++;
ws.mergeCells(r,1,r,10);
ws.getCell('A'+r).value='💬 Zalo: 0981 873 395 | 0918 258 385';
ws.getCell('A'+r).font={italic:true,size:9,color:{argb:'FF888888'}};
ws.getCell('A'+r).alignment={wrapText:true};
// Save
let buf=await wb.xlsx.writeBuffer();
let blob=new Blob([buf],{type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
let url=URL.createObjectURL(blob);
let a=document.createElement('a');a.href=url;a.download=(qd.customer.orderCode||'BG')+'.xlsx';
a.click();URL.revokeObjectURL(url);
showToast('Đã xuất file Excel!');
}
// ===== PDF EXPORT (html2canvas for Vietnamese) =====
async function exportPDF(){
let qd=getQuoteData();
// Build a hidden HTML table for rendering
let div=document.createElement('div');
div.id='pdfRender';
div.style.cssText='position:fixed;top:-9999px;left:0;width:1100px;background:#fff;padding:40px;font-family:Inter,Arial,sans-serif';
let itemsHtml=qd.items.map(it=>`<tr style="border-bottom:1px solid #e2e8f0">
<td style="padding:10px 6px;text-align:center;font-size:13px">${it.stt}</td>
<td style="padding:10px 6px"><img src="${it.image}" style="width:80px;height:80px;object-fit:contain;border-radius:6px;border:1px solid #e2e8f0;background:#f8fafc" onerror="this.style.display='none'"></td>
<td style="padding:10px 6px;font-size:12px;font-weight:600">${it.name}</td>
<td style="padding:10px 6px;text-align:center;font-size:12px">${it.model}</td>
<td style="padding:10px 6px;font-size:11px;color:#64748b;max-width:180px">${it.specs}</td>
<td style="padding:10px 6px;text-align:center;font-size:13px">${it.qty}</td>
<td style="padding:10px 6px;text-align:right;font-size:12px">${fmtVND(it.price)}</td>
<td style="padding:10px 6px;text-align:right;font-size:12px;${it.discPrice<it.price?'color:#dc3545;font-weight:700':''}">${fmtVND(it.discPrice)}</td>
<td style="padding:10px 6px;text-align:right;font-size:13px;font-weight:700">${fmtVND(it.total)}</td>
<td style="padding:10px 6px;font-size:11px">${it.note}</td>
</tr>`).join('');
div.innerHTML=`
<div style="text-align:center;margin-bottom:20px">
<img src="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/logo_200.png" alt="V.AI STUDIO" style="height:40px;object-fit:contain">
<div style="font-size:11px;color:#64748b;letter-spacing:2px;font-style:italic">❝ Niềm tin khách hàng là tài sản của chúng tôi ❞</div>
<div style="font-size:20px;font-weight:800;color:#db9815;margin-top:10px">BẢNG BÁO GIÁ</div>
</div>
<div style="display:flex;justify-content:space-between;margin-bottom:20px;font-size:12px;color:#0f172a">
<div><strong>Khách hàng:</strong> ${qd.customer.name}<br><strong>SĐT:</strong> ${qd.customer.phone}<br><strong>Email:</strong> ${qd.customer.email}<br><strong>Địa chỉ:</strong> ${qd.customer.addr}</div>
<div style="text-align:right"><strong>Mã đơn hàng:</strong> ${qd.customer.orderCode}<br><strong>Ngày:</strong> ${qd.customer.date}<br><strong>Zalo:</strong> <a href="https://zalo.me/0981873395" target="_blank">0981 873 395</a></div>
</div>
<table style="width:100%;border-collapse:collapse">
<thead><tr style="background:#003f62;color:#fff">
<th style="padding:10px 6px;font-size:11px">STT</th><th style="padding:10px 6px;font-size:11px">Hình ảnh</th><th style="padding:10px 6px;font-size:11px;text-align:left">Tên sản phẩm</th><th style="padding:10px 6px;font-size:11px">Mã SP</th><th style="padding:10px 6px;font-size:11px">Thông tin SP</th><th style="padding:10px 6px;font-size:11px">SL</th><th style="padding:10px 6px;font-size:11px">Đơn giá</th><th style="padding:10px 6px;font-size:11px">Đơn giá CK</th><th style="padding:10px 6px;font-size:11px">Thành tiền</th><th style="padding:10px 6px;font-size:11px">Ghi chú</th>
</tr></thead>
<tbody>${itemsHtml}</tbody>
<tfoot><tr style="border-top:3px solid #003f62">
<td colspan="8" style="padding:12px 6px;text-align:right;font-size:14px;font-weight:800;color:#003f62">TỔNG CỘNG:</td>
<td style="padding:12px 6px;text-align:right;font-size:15px;font-weight:900;color:#003f62">${fmtVND(qd.grandTotal)}</td><td></td>
</tr></tfoot>
</table>
<div style="margin-top:20px;font-size:11px;color:#64748b;font-style:italic">
Miễn phí giao hàng trong TPHCM. | Giá đã bao gồm VAT (Hỗ trợ trị giá đơn hàng VAT xuất theo đúng số tiền khác mã).
</div>`;
document.body.appendChild(div);
// Wait for images and fonts
await new Promise(r=>setTimeout(r,500));
try{
let canvas=await html2canvas(div,{scale:2,useCORS:true,allowTaint:true,logging:false});
let imgData=canvas.toDataURL('image/jpeg',0.95);
let {jsPDF}=window.jspdf;
let pdf=new jsPDF({orientation:'landscape',unit:'mm',format:'a4'});
let pdfW=pdf.internal.pageSize.getWidth();
let pdfH=pdf.internal.pageSize.getHeight();
let imgW=canvas.width;let imgH=canvas.height;
let ratio=pdfW/imgW*2;// scale:2 correction
let totalH=imgH*ratio/2;
let pageH=pdfH-10;let y=5;let srcY=0;
let sliceH=pageH/ratio*2;
// Multi-page support
while(srcY<imgH){
if(srcY>0)pdf.addPage();
let clipCanvas=document.createElement('canvas');
clipCanvas.width=imgW;clipCanvas.height=Math.min(sliceH,imgH-srcY);
clipCanvas.getContext('2d').drawImage(canvas,0,srcY,imgW,clipCanvas.height,0,0,imgW,clipCanvas.height);
let sliceData=clipCanvas.toDataURL('image/jpeg',0.95);
let sliceRatio=pdfW/imgW;
pdf.addImage(sliceData,'JPEG',0,y,pdfW,clipCanvas.height*sliceRatio);
srcY+=sliceH;
}
pdf.save((qd.customer.orderCode||'BG')+'.pdf');
showToast('Đã xuất file PDF!');
}catch(e){console.error('PDF error:',e);showToast('Lỗi xuất PDF. Vui lòng thử lại.')}
div.remove();
}
function printQuotation(){
exportPDF();// Reuse PDF then user can print from PDF viewer
}
// Init on load
function init(){buildCats();buildPrices();buildBrands();render();
// Try path-based routing first, then hash-based
if(!parseCurrentPath()){parseUrlParams();render();}
}
// Handle ?quote= parameter — auto-add products to cart + open quotation
(function(){
let params = new URLSearchParams(location.search);
let quoteParam = params.get('quote');
if(quoteParam){
let skus = quoteParam.split(',').map(s=>s.trim()).filter(Boolean);
// Wait for products to load
let waitForD = setInterval(function(){
if(typeof D !== 'undefined' && D.length > 0 && typeof addToCart === 'function'){
clearInterval(waitForD);
skus.forEach(function(sku){
let skuClean = sku.toLowerCase().replace(/[.\- ]/g,'');
let idx = D.findIndex(function(p){
return (p.sku||'').toLowerCase().replace(/[.\- ]/g,'') === skuClean ||
(p.model||'').toLowerCase().replace(/[.\- ]/g,'') === skuClean;
});
if(idx >= 0) addToCart(idx);
});
// Open quotation after adding
setTimeout(function(){
if(typeof openQuotation === 'function') openQuotation();
else if(typeof openCart === 'function') openCart();
}, 500);
}
}, 500);
setTimeout(function(){ clearInterval(waitForD); }, 15000);
}
})();
// === QUOTE LINK DECODER ===
// Handles ?q=Base64 links from Zalo bot
(function(){
var params=new URLSearchParams(location.search);
var q=params.get('q');
if(!q)return;
try{
// Decode Base64 (add padding back)
while(q.length%4)q+='=';
var decoded=atob(q.replace(/-/g,'+').replace(/_/g,'/'));
// Format: SKU:QTY:PRICE,SKU:QTY:PRICE
var items=decoded.split(',');
var waitD=setInterval(function(){
if(typeof D==='undefined'||!D.length||typeof addToCart!=='function')return;
clearInterval(waitD);
items.forEach(function(item){
var parts=item.split(':');
var sku=parts[0],qty=parseInt(parts[1])||1,price=parseInt(parts[2])||0;
var skuClean=sku.toLowerCase().replace(/[.\- ]/g,'');
var idx=D.findIndex(function(p){
return (p.sku||'').toLowerCase().replace(/[.\- ]/g,'')===skuClean||
(p.model||'').toLowerCase().replace(/[.\- ]/g,'')===skuClean;
});
if(idx>=0){
addToCart(idx);
// Override price if discounted
if(price>0&&price!==D[idx].priceNum){
var c=cart[cart.length-1];
if(c)c.priceNum=price;
}
}
});
setTimeout(function(){
if(typeof openQuotation==='function')openQuotation();
else if(typeof openCart==='function')openCart();
},600);
},500);
setTimeout(function(){clearInterval(waitD)},15000);
}catch(e){console.error('Quote decode error:',e)}
})();
// === ENCRYPTED QUOTE LINK DECODER v2 ===
(function(){
var KEY='vai2026studio';
function xorDecrypt(encoded){
while(encoded.length%4)encoded+='=';
var raw=atob(encoded.replace(/-/g,'+').replace(/_/g,'/'));
var result='';
for(var i=0;i<raw.length;i++)result+=String.fromCharCode(raw.charCodeAt(i)^KEY.charCodeAt(i%KEY.length));
return result;
}
var params=new URLSearchParams(location.search);
var qk=params.get('qk');
var qd=params.get('qd');
var encoded=qk||qd;
if(!encoded)return;
var isDiscounted=!!qd;
try{
var decoded=xorDecrypt(encoded);
var items=decoded.split(',');
var waitD=setInterval(function(){
if(typeof D==='undefined'||!D.length||typeof addToCart!=='function')return;
clearInterval(waitD);
// Clear cart first to avoid duplicates
if(typeof cart!=='undefined')cart.length=0;
items.forEach(function(item){
var parts=item.split(':');
var sku=parts[0],qty=parseInt(parts[1])||1,price=parseInt(parts[2])||0;
var skuClean=sku.toLowerCase().replace(/[.\- ]/g,'');
var idx=D.findIndex(function(p){
return (p.sku||'').toLowerCase().replace(/[.\- ]/g,'')===skuClean||(p.model||'').toLowerCase().replace(/[.\- ]/g,'')===skuClean||(p.slug||'')===sku;
});
if(idx>=0){
addToCart(idx);
// Set correct quantity
var c=cart[cart.length-1];
if(c)c.qty=qty;
}
});
// Open quotation
setTimeout(function(){
if(typeof openQuotation==='function')openQuotation();
// If discounted, apply prices after table renders
if(isDiscounted){
setTimeout(function(){
items.forEach(function(item,i){
var parts=item.split(':');
var price=parseInt(parts[2])||0;
if(price>0){
var discInput=document.querySelector('.qt-disc[data-idx="'+i+'"]');
if(discInput){
discInput.value=price.toLocaleString('vi-VN');
discInput.dispatchEvent(new Event('input'));
}
}
});
if(typeof updateQuoteTotal==='function')updateQuoteTotal();
},800);
}
},600);
},500);
setTimeout(function(){clearInterval(waitD)},15000);
}catch(e){console.error('Quote decode:',e)}
})();
document.addEventListener('DOMContentLoaded',()=>{
initCatalogue();
updateCartBadge();
// Auto-open chat if ?chat=1
if(location.search.includes('chat=1')){setTimeout(()=>toggleChat(),500)}
});
function shareQuoteLink(){
var KEY='vai2026studio';
var parts=cart.map(function(c,i){
var disc=document.querySelector('.qt-disc[data-idx="'+i+'"]');
var price=disc?parseInt(disc.value.replace(/\D/g,''))||c.priceNum:c.priceNum;
return (c.sku||c.slug)+':'+c.qty+':'+price;
});
var dataStr=parts.join(',');
var encrypted='';
for(var i=0;i<dataStr.length;i++)encrypted+=String.fromCharCode(dataStr.charCodeAt(i)^KEY.charCodeAt(i%KEY.length));
var encoded=btoa(encrypted).replace(/\+/g,'-').replace(/\//g,'_').replace(/=+$/,'');
var link=location.origin+'/?qd='+encoded;
if(navigator.clipboard)navigator.clipboard.writeText(link);
prompt('🔗 Link báo giá (đã áp CK) — chia sẻ qua Zalo:',link);
showToast('✅ Đã copy link báo giá!');
}
async function shareQuoteImage(){
let qd=getQuoteData();
// Build same HTML as exportPDF
let div=document.createElement('div');
div.style.cssText='position:fixed;top:-9999px;left:0;width:1100px;background:#fff;padding:40px;font-family:Inter,Arial,sans-serif';
let itemsHtml=qd.items.map(it=>`<tr style="border-bottom:1px solid #e2e8f0">
<td style="padding:10px 6px;text-align:center;font-size:13px">${it.stt}</td>
<td style="padding:10px 6px"><img src="${it.image}" style="width:80px;height:80px;object-fit:contain;border-radius:6px;border:1px solid #e2e8f0;background:#f8fafc" onerror="this.style.display='none'"></td>
<td style="padding:10px 6px;font-size:12px;font-weight:600">${it.name}</td>
<td style="padding:10px 6px;text-align:center;font-size:12px">${it.model}</td>
<td style="padding:10px 6px;font-size:11px;color:#64748b;max-width:180px">${it.specs}</td>
<td style="padding:10px 6px;text-align:center;font-size:13px">${it.qty}</td>
<td style="padding:10px 6px;text-align:right;font-size:12px">${fmtVND(it.price)}</td>
<td style="padding:10px 6px;text-align:right;font-size:12px;${it.discPrice<it.price?'color:#dc3545;font-weight:700':''}">${fmtVND(it.discPrice)}</td>
<td style="padding:10px 6px;text-align:right;font-size:13px;font-weight:700">${fmtVND(it.total)}</td>
<td style="padding:10px 6px;font-size:11px">${it.note}</td>
</tr>`).join('');
div.innerHTML=`
<div style="text-align:left;margin-bottom:20px">
<img src="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/logo_200.png" style="height:40px;object-fit:contain;margin:0 auto">
<div style="font-size:11px;color:#64748b;font-style:italic">❝ Niềm tin khách hàng là tài sản của chúng tôi ❞</div>
<div style="font-size:20px;font-weight:800;color:#db9815;margin-top:10px">BẢNG BÁO GIÁ</div>
</div>
<div style="display:flex;justify-content:space-between;margin-bottom:20px;font-size:12px">
<div><strong>Khách hàng:</strong> ${qd.customer.name}<br><strong>SĐT:</strong> ${qd.customer.phone}<br><strong>Email:</strong> ${qd.customer.email}<br><strong>Địa chỉ:</strong> ${qd.customer.addr}</div>
<div style="text-align:right"><strong>Mã đơn hàng:</strong> ${qd.customer.orderCode}<br><strong>Ngày:</strong> ${qd.customer.date}<br><strong>Zalo:</strong> 0981 873 395</div>
</div>
<table style="width:100%;border-collapse:collapse">
<thead><tr style="background:#003f62;color:#fff">
<th style="padding:10px 6px;font-size:11px">STT</th><th style="padding:10px 6px;font-size:11px">Hình</th><th style="padding:10px 6px;font-size:11px;text-align:left">Tên SP</th><th style="padding:10px 6px;font-size:11px"></th><th style="padding:10px 6px;font-size:11px">Thông tin</th><th style="padding:10px 6px;font-size:11px">SL</th><th style="padding:10px 6px;font-size:11px">Đơn giá</th><th style="padding:10px 6px;font-size:11px">Đơn giá CK</th><th style="padding:10px 6px;font-size:11px">Thành tiền</th><th style="padding:10px 6px;font-size:11px">Ghi chú</th>
</tr></thead>
<tbody>${itemsHtml}</tbody>
<tfoot><tr style="border-top:3px solid #003f62"><td colspan="8" style="padding:12px;text-align:right;font-size:14px;font-weight:800;color:#003f62">TỔNG CỘNG:</td><td style="padding:12px;text-align:right;font-size:15px;font-weight:900;color:#003f62">${fmtVND(qd.grandTotal)}</td><td></td></tr></tfoot>
</table>
<div style="margin-top:16px;font-size:11px;color:#64748b;font-style:italic">
Miễn phí giao hàng trong TPHCM. | Giá đã bao gồm VAT.
</div>`;
document.body.appendChild(div);
await new Promise(r=>setTimeout(r,500));
try{
let canvas=await html2canvas(div,{scale:2,useCORS:true,allowTaint:true,backgroundColor:'#fff'});
div.remove();
let blob=await new Promise(r=>canvas.toBlob(r,'image/png'));
let file=new File([blob],(qd.customer.orderCode||'BG')+'.png',{type:'image/png'});
// Try native share (mobile)
if(navigator.share&&navigator.canShare&&navigator.canShare({files:[file]})){
await navigator.share({title:'Báo giá V.AI STUDIO - '+(qd.customer.orderCode||''),files:[file]});
}else{
// Fallback: show preview + download
let url=URL.createObjectURL(blob);
let preview=document.createElement('div');
preview.style.cssText='position:fixed;inset:0;background:rgba(0,0,0,.7);z-index:999;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:16px;backdrop-filter:blur(4px)';
preview.innerHTML='<div style="background:#fff;border-radius:16px;max-width:90vw;max-height:85vh;overflow:auto;padding:16px"><img src="'+url+'" style="max-width:100%;border-radius:8px"><div style="display:flex;gap:8px;margin-top:12px;justify-content:center"><a href="'+url+'" download="'+(qd.customer.orderCode||'BG')+'.png" style="padding:10px 20px;background:#003f62;color:#fff;border-radius:8px;font-weight:700;font-size:.85rem;text-decoration:none"><i class="fas fa-download"></i> Tải ảnh</a><button onclick="this.closest(\'div[style*=fixed]\').remove()" style="padding:10px 20px;background:#e2e8f0;border:none;border-radius:8px;font-weight:700;font-size:.85rem;cursor:pointer">Đóng</button></div></div>';
document.body.appendChild(preview);
showToast('✅ Ảnh báo giá đã sẵn sàng! Tải về rồi gửi qua Zalo.');
}
}catch(e){
div.remove();
showToast('Lỗi: '+e.message);
}
}
</script>
<!-- ZALO + AI CHATBOX FAB -->
<a href="https://zalo.me/0981873395" target="_blank" title="Chat Zalo" style="position:fixed;bottom:90px;right:24px;z-index:180;width:50px;height:50px;border-radius:50%;background:#0068FF;color:#fff;display:flex;align-items:center;justify-content:center;font-size:1.3rem;box-shadow:0 4px 16px rgba(0,104,255,.4);text-decoration:none;transition:all .25s ease"><i class="fas fa-comment-dots"></i></a>
<button class="chat-fab" onclick="toggleChat()" title="Tư Vấn AI"><i class="fas fa-robot"></i></button>
<div class="chat-fab-label">Tư Vấn AI</div>
<div class="chat-overlay2" id="chatOverlay2" onclick="closeChat()"></div>
<div class="chatbox" id="chatbox">
<div class="chat-head">
<div class="chat-head-avatar"><i class="fas fa-robot"></i></div>
<div class="chat-head-info"><h4>Tư Vấn AI</h4><span>Trợ lý thiết bị nhà bếp thông minh</span></div>
<button class="chat-close" onclick="closeChat()" title="Đóng"><i class="fas fa-times"></i></button>
</div>
<div class="chat-body" id="chatBody">
<div class="chat-msg bot">Xin chào anh/chị! 👋 Em là <strong>V.AI STUDIO</strong> — trợ lý tư vấn thiết bị nhà bếp.<br><br>Em có thể giúp anh/chị:<br>🔍 Tìm & so sánh sản phẩm phù hợp<br>💡 Tư vấn theo nhu cầu & ngân sách<br>📋 Giải đáp thông số kỹ thuật<br><br>Anh/chị cần tư vấn gì ạ?<div style="margin-top:8px;padding-top:6px;border-top:1px solid #e2e8f0"><button onclick="shareChatToZalo(this.closest('.chat-msg'))" style="padding:5px 12px;background:#0068FF;color:#fff;border:none;border-radius:6px;font-size:.72rem;font-weight:600;cursor:pointer;display:inline-flex;align-items:center;gap:5px"><i class="fas fa-comment-dots"></i> Gửi qua Zalo</button></div></div>
</div>
<div class="chat-suggestions" id="chatSuggestions">
<div class="chat-sug" onclick="sendSuggestion(this)">Tư vấn bếp từ cho gia đình</div>
<div class="chat-sug" onclick="sendSuggestion(this)">Máy hút mùi nào tốt?</div>
<div class="chat-sug" onclick="sendSuggestion(this)">Khóa cửa thông minh</div>
<div class="chat-sug" onclick="sendSuggestion(this)">📋 Lên báo giá</div>
<div class="chat-sug" onclick="sendSuggestion(this)">🛒 Giỏ hàng</div>
</div>
<div class="chat-foot">
<textarea class="chat-input" id="chatInput" placeholder="Nhập câu hỏi..." rows="1" onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();sendChat()}"></textarea>
<button class="chat-send" id="chatSend" onclick="sendChat()"><i class="fas fa-paper-plane"></i></button>
</div>
</div>
<!-- SHARE POPOVER -->
<div class="share-popover-overlay" id="shareOverlay" onclick="if(event.target===this)closeSharePopover()">
<div class="share-popover" style="position:relative">
<button class="share-close-btn" onclick="closeSharePopover()"><i class="fas fa-times"></i></button>
<h4><i class="fas fa-share-alt"></i> Chia sẻ</h4>
<div class="share-apps">
<div class="share-app" onclick="shareVia('facebook')"><div class="share-app-icon" style="background:#1877F2"><i class="fab fa-facebook-f"></i></div><span>Facebook</span></div>
<div class="share-app" onclick="shareVia('messenger')"><div class="share-app-icon" style="background:#0099FF"><i class="fab fa-facebook-messenger"></i></div><span>Messenger</span></div>
<div class="share-app" onclick="shareVia('zalo')"><div class="share-app-icon" style="background:#0068FF"><i class="fas fa-comment-dots"></i></div><span>Zalo</span></div>
<div class="share-app" onclick="shareVia('twitter')"><div class="share-app-icon" style="background:#000"><i class="fab fa-x-twitter"></i></div><span>X</span></div>
<div class="share-app" onclick="shareVia('telegram')"><div class="share-app-icon" style="background:#2AABEE"><i class="fab fa-telegram-plane"></i></div><span>Telegram</span></div>
<div class="share-app" onclick="shareVia('whatsapp')"><div class="share-app-icon" style="background:#25D366"><i class="fab fa-whatsapp"></i></div><span>WhatsApp</span></div>
<div class="share-app" onclick="shareVia('email')"><div class="share-app-icon" style="background:var(--g)"><i class="fas fa-envelope"></i></div><span>Email</span></div>
<div class="share-app" onclick="shareVia('copy')"><div class="share-app-icon" style="background:var(--p)"><i class="fas fa-link"></i></div><span>Copy link</span></div>
</div>
<div class="share-link-box">
<input class="share-link-input" id="shareLinkInput" readonly>
<button class="share-link-copy" onclick="shareVia('copy')"><i class="fas fa-copy"></i> Sao chép</button>
</div>
</div>
</div>
<script src="search-plus-boot.js?v=20"></script>
<script src="shorts-subtitles.js?v=2"></script>
<script src="ancuong-shorts.js?v=8"></script>
<script src="custom-shorts.js?v=4"></script>
<script src="smart-kitchen-shorts.js?v=4"></script>
<script>function doAISearch(){
// Delegated to search-plus-boot.js v15 (ALL-IN-ONE)
if(window._vaiSearchContext && typeof D!=='undefined' && D.length>0){
var input=document.getElementById('aiSearch');var resultsDiv=document.getElementById('aiResults');
if(!input||!resultsDiv)return;var query=input.value.trim();
if(!query||query.length<2){resultsDiv.style.display='block';resultsDiv.innerHTML='💡 Nhập mã SP hoặc mô tả: "bếp từ Grob dưới 10tr", "máy hút mùi Malloca"';return;}
resultsDiv.style.display='block';resultsDiv.innerHTML='⏳ Đang tìm...';
// Multi-code search: nếu có dấu phẩy thì tìm theo mã
var queries=query.split(/[,;]+/).map(function(c){return c.trim()}).filter(function(c){return c.length>=2});
if(queries.length>1){
var allResults=[];
queries.forEach(function(q2){
var qLow=q2.toLowerCase().replace(/[.\-_ \/]/g,'');
for(var i=0;i<D.length&&allResults.length<20;i++){
var p=D[i];if(!p)continue;
var fields=[(p.sku||''),(p.mod||''),(p.slug||'')].map(function(f){return f.toLowerCase().replace(/[.\-_ \/]/g,'')});
if(fields.some(function(f){return f===qLow||(f&&qLow.length>=3&&(f.indexOf(qLow)!==-1||qLow.indexOf(f)!==-1))})){allResults.push(p);}
}
});
if(allResults.length){_renderAIResults(allResults,resultsDiv);return;}
}
// Context search (category + brand + price + keywords)
var results=window._vaiSearchContext(query,24);
if(!results.length){resultsDiv.innerHTML='❌ Không tìm thấy "'+query+'". Thử: "bếp từ Malloca", "máy hút mùi dưới 15tr"';return;}
var prods=results.map(function(r){return r.p;});
_renderAIResults(prods,resultsDiv);
return;
}
// Fallback: basic
var q2=document.getElementById('aiSearch').value.trim();
if(!q2){document.getElementById('aiResults').innerHTML='💡 Nhập mã hoặc tên SP';document.getElementById('aiResults').style.display='block';return;}
document.getElementById('aiResults').innerHTML='⏳ Đang tải dữ liệu...';document.getElementById('aiResults').style.display='block';
}
// Add to Order from AI search results (same as dropdown "+Đơn")
window._vaiAddOrder=function(slug,btn){
if(!slug||typeof D==='undefined')return;
for(var i=0;i<D.length;i++){
if(D[i]&&D[i].slug===slug){
if(window._showOrderPicker){
window._showOrderPicker(D[i],function(r){
if(r==='ok'){btn.textContent='✓';btn.style.background='#059669';}
else if(r==='dup'){btn.textContent='Có rồi';btn.style.background='#94a3b8';}
setTimeout(function(){btn.textContent='+ Đơn';btn.style.background='#003f62';},2500);
});
}
break;
}
}
};
function _renderAIResults(prods,res){
var fmt=function(n){if(!n||isNaN(n))return'LH';return Number(n).toLocaleString('vi-VN')+'đ'};
var cat=window._vaiDetectCategory?window._vaiDetectCategory(document.getElementById('aiSearch').value):'';
var brand=window._vaiDetectBrand?window._vaiDetectBrand(document.getElementById('aiSearch').value):'';
var parts=[];
if(cat)parts.push('📂 '+cat);if(brand)parts.push('🏷 '+brand);
parts.push('🔍 '+prods.length+' SP');
var h='<div style="margin-bottom:10px;display:flex;gap:6px;flex-wrap:wrap">'+parts.map(function(p){return'<span style="background:#eff6ff;padding:3px 8px;border-radius:20px;font-size:11px;font-weight:600;color:#1e40af;border:1px solid #bfdbfe">'+p+'</span>'}).join('')+'</div>';
h+='<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(170px,1fr));gap:10px">';
prods.slice(0,24).forEach(function(p){
var idx=D.indexOf(p);
var name=p.name||p.n||'';var price=p.price||p.p||'LH';var img=p.image||p.img||p.i||'';var sku=p.sku||p.model||p.mod||'';var brand2=p.brand||'';var slug=p.slug||'';
var feats=(p.feats||[]).slice(0,2).join(' • ');
var specs=p.specs||{};var sk=Object.entries(specs).slice(0,3);
h+='<div style="background:#fff;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden;transition:all .2s;cursor:pointer" onclick="showDetail('+idx+')" onmouseover="this.style.transform=\'translateY(-2px)\';this.style.boxShadow=\'0 4px 12px rgba(0,0,0,.1)\'" onmouseout="this.style.transform=\'\';this.style.boxShadow=\'\'">';
if(img)h+='<div style="height:90px;background:#f8fafc;display:flex;align-items:center;justify-content:center"><img src="'+img+'" style="max-width:100%;max-height:85px;object-fit:contain" onerror="this.parentElement.style.display=\'none\'"></div>';
h+='<div style="padding:8px"><div style="font-size:11px;font-weight:600;color:#003f62;line-height:1.3;height:30px;overflow:hidden">'+name.substring(0,55)+'</div>';
if(sku)h+='<div style="font-size:9px;color:#94a3b8;margin-top:2px">'+sku+'</div>';
if(brand2)h+='<div style="font-size:9px;color:#64748b;margin-top:1px">'+brand2+'</div>';
if(feats)h+='<div style="font-size:8.5px;color:#64748b;margin-top:2px;height:12px;overflow:hidden">'+feats+'</div>';
if(sk.length){h+='<div style="margin-top:3px;font-size:8px;color:#555;border-top:1px solid #f0f0f0;padding-top:3px">';sk.forEach(function(kv){h+=kv[0]+': <b>'+kv[1]+'</b><br>'});h+='</div>'}
h+='<div style="font-size:13px;font-weight:800;color:#059669;margin-top:4px">'+price+'</div>';
h+='</div>';
// Buttons: +Giỏ hàng + +Đơn
h+='<div style="display:flex;gap:4px;padding:0 6px 6px">';
h+='<button onclick="event.stopPropagation();if(typeof addToCart===\'function\')addToCart('+idx+')" style="flex:1;padding:4px;background:#db9815;color:#fff;border:none;border-radius:5px;font-size:9px;font-weight:700;cursor:pointer">+ Giỏ</button>';
h+='<button onclick="event.stopPropagation();window._vaiAddOrder(\''+slug+'\',this)" style="flex:1;padding:4px;background:#003f62;color:#fff;border:none;border-radius:5px;font-size:9px;font-weight:700;cursor:pointer">+ Đơn</button>';
h+='</div></div>';
});
h+='</div>';res.innerHTML=h;
}
async function _aiSelectProducts(q,res){
try{
if(typeof D==='undefined'||!D.length){res.innerHTML='⏳ Đang tải SP...';return}
// Parse budget
const bm=q.match(/(\d+)\s*(tr|triệu|m)/i);
const budget=bm?parseInt(bm[1])*1000000:50000000;
// Get product summary for AI (top products per category, Malloca/Grob priority)
const typeMap=[{k:['bep tu','bep dien','bep'],n:'Bếp từ'},{k:['may hut','hut mui'],n:'Máy hút mùi'},{k:['chau rua','bon rua'],n:'Chậu rửa'},{k:['voi rua'],n:'Vòi rửa'},{k:['lo nuong'],n:'Lò nướng'}];
const qn=q.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/đ/g,'d');
let wantTypes=typeMap.filter(t=>t.k.some(k=>qn.includes(k)));
if(!wantTypes.length)wantTypes=typeMap.slice(0,3);
const perBudget=Math.floor(budget/wantTypes.length);
// Build catalog summary for AI
let catalog=[];
wantTypes.forEach(typ=>{
let items=D.filter(p=>{
const pn=((p.name||'')+(p.cat||'')).toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/đ/g,'d');
return typ.k.some(k=>pn.includes(k))&&p.priceNum>0&&p.priceNum<=perBudget*1.5;
});
// Shuffle and take samples for AI to choose from
for(let i=items.length-1;i>0;i--){let j=Math.floor(Math.random()*(i+1));[items[i],items[j]]=[items[j],items[i]]}
// Priority: Malloca/Grob first in the list AI sees
let m=items.filter(p=>/malloca/i.test(p.brand));
let g=items.filter(p=>/grob/i.test(p.brand));
let o=items.filter(p=>!/malloca|grob/i.test(p.brand));
let pool=[...m.slice(0,5),...g.slice(0,4),...o.slice(0,3)];
pool.forEach(p=>catalog.push({sku:p.sku,name:p.name.substring(0,50),brand:p.brand,price:p.priceNum,type:typ.n}));
});
// Call AI to select best combo
const aiPrompt=`Khách yêu cầu: "${q}"\nNgân sách: ${budget.toLocaleString('vi')}đ\nDanh sách SP có sẵn:\n${catalog.map((p,i)=>i+1+'. ['+p.sku+'] '+p.name+' | '+p.brand+' | '+p.price.toLocaleString('vi')+'đ | Loại: '+p.type).join('\n')}\n\nChọn combo TỐT NHẤT (1 SP mỗi loại, ưu tiên Malloca/Grob, tổng ≤ ngân sách). Trả lời CHỈ JSON: {"picks":[{"sku":"...","reason":"lý do chọn ngắn"}]}`;
const aiRes=await fetch('https://router.huggingface.co/v1/chat/completions',{
method:'POST',
headers:{'Content-Type':'application/json','Authorization':'Bearer '+document.querySelector('meta[name=hf-token]')?.content||''},
body:JSON.stringify({model:'Qwen/Qwen2.5-72B-Instruct',messages:[{role:'user',content:aiPrompt}],max_tokens:300,temperature:0.7})
});
if(!aiRes.ok){
// Fallback to random selection if AI fails
_doQuoteFallback(wantTypes,perBudget,budget,res);return;
}
const aiData=await aiRes.json();
const content=aiData.choices[0].message.content;
const jsonMatch=content.match(/\{[\s\S]*\}/);
if(!jsonMatch){_doQuoteFallback(wantTypes,perBudget,budget,res);return}
const result=JSON.parse(jsonMatch[0]);
const selectedSkus=result.picks.map(p=>p.sku);
const reasons=Object.fromEntries(result.picks.map(p=>[p.sku,p.reason||'']));
// Map SKUs to actual products
let picks=[];let total=0;
selectedSkus.forEach(sku=>{
const found=D.find(p=>p.sku===sku);
if(found){
const typ=catalog.find(c=>c.sku===sku);
picks.push({...found,typeName:typ?typ.type:'SP',reason:reasons[sku]||''});
total+=found.priceNum;
}
});
if(!picks.length){_doQuoteFallback(wantTypes,perBudget,budget,res);return}
_renderQuote(picks,total,budget,res,true);
}catch(e){
console.error(e);
// Fallback
const bm=q.match(/(\d+)\s*(tr|triệu|m)/i);
const budget=bm?parseInt(bm[1])*1000000:50000000;
const typeMap=[{k:['bep tu','bep dien','bep'],n:'Bếp từ'},{k:['may hut','hut mui'],n:'Máy hút mùi'},{k:['chau rua','bon rua'],n:'Chậu rửa'}];
const qn=q.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/đ/g,'d');
let wantTypes=typeMap.filter(t=>t.k.some(k=>qn.includes(k)));
if(!wantTypes.length)wantTypes=typeMap.slice(0,3);
_doQuoteFallback(wantTypes,Math.floor(budget/wantTypes.length),budget,res);
}
}
function _doQuoteFallback(wantTypes,perBudget,budget,res){
let picks=[];let total=0;
function shuffle(a){for(let i=a.length-1;i>0;i--){let j=Math.floor(Math.random()*(i+1));[a[i],a[j]]=[a[j],a[i]]}return a}
wantTypes.forEach(typ=>{
let all=D.filter(p=>{
const pn=((p.name||'')+(p.cat||'')).toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/đ/g,'d');
return typ.k.some(k=>pn.includes(k))&&p.priceNum>0&&p.priceNum<=perBudget*1.5;
});
let m=shuffle(all.filter(p=>/malloca/i.test(p.brand)));
let g=shuffle(all.filter(p=>/grob/i.test(p.brand)));
let pool=[...m.slice(0,4),...g.slice(0,3),...shuffle(all).slice(0,3)];
shuffle(pool);
let pick=pool.find(p=>p.priceNum<=perBudget*1.3)||pool[0];
if(pick){picks.push({...pick,typeName:typ.n,reason:''});total+=pick.priceNum}
});
if(!picks.length){res.innerHTML='😅 Không đủ SP phù hợp';return}
_renderQuote(picks,total,budget,res,false);
}
function _renderQuote(picks,total,budget,res,isAI){
let h='<div style="font-weight:700;margin-bottom:10px;font-size:.95rem">'+(isAI?'🤖':'📋')+' AI BÁO GIÁ <span style="color:#64748b;font-weight:400">(~'+budget.toLocaleString('vi')+'đ)</span></div>';
h+='<table style="width:100%;border-collapse:collapse;font-size:.8rem">';
h+='<tr style="background:#003f62;color:#fff"><th style="padding:8px;text-align:left">Loại</th><th style="text-align:left">Sản phẩm</th><th>Brand</th><th style="text-align:right;padding-right:8px">Giá</th></tr>';
picks.forEach((p,i)=>{
h+='<tr style="border-bottom:1px solid #e2e8f0;'+(i%2?'background:#f8fafc':'')+'"><td style="padding:8px;font-weight:600">'+p.typeName+'</td><td>'+p.name.substring(0,38)+(p.reason?'<br><span style="font-size:.65rem;color:#28a745">💡 '+p.reason+'</span>':'')+'<br><span style="font-size:.62rem;color:#888">'+p.sku+(p.specs&&Object.keys(p.specs).length?' | '+Object.entries(p.specs).slice(0,2).map(([k,v])=>k+':'+v).join(', '):'')+'</span></td><td style="text-align:center;font-size:.75rem">'+p.brand+'</td><td style="text-align:right;font-weight:700;color:#003f62;padding-right:8px">'+(p.price||'LH')+'</td></tr>';
});
h+='<tr style="background:#003f62;color:#fff;font-weight:800"><td colspan="3" style="padding:10px;text-align:right">TỔNG:</td><td style="text-align:right;padding-right:8px;font-size:.95rem">'+total.toLocaleString('vi')+'đ</td></tr></table>';
const diff=budget-total;
h+=diff>=0?'<div style="margin-top:8px;color:#28a745;font-weight:600;font-size:.82rem">✅ Trong ngân sách! Dư '+diff.toLocaleString('vi')+'đ</div>':'<div style="margin-top:8px;color:#dc3545;font-weight:600;font-size:.82rem">⚠️ Vượt '+(0-diff).toLocaleString('vi')+'đ</div>';
h+='<div style="margin-top:12px;text-align:center;display:flex;gap:8px;justify-content:center;flex-wrap:wrap">';
h+='';
h+='<button onclick="_addQuotePicks()" style="padding:9px 18px;background:#db9815;color:#fff;border:none;border-radius:8px;font-weight:700;cursor:pointer;font-size:.82rem">🛒 Thêm giỏ</button></div>';
res.innerHTML=h;window._quotePicks=picks;
}
</script>
<script>
// Chat AI: intercept messages and show search/quote results directly
(function(){
// Hook into chat send if exists
const origChatSend=window.sendChatMessage;
if(typeof origChatSend==='function'){
window.sendChatMessage=function(msg){
if(_handleChatAI(msg))return;
origChatSend(msg);
}
}
// Also expose for manual use
window.chatAI=function(msg){_handleChatAI(msg)};
})();
function _handleChatAI(msg){
if(!msg||typeof D==='undefined')return false;
const lo=msg.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/đ/g,'d');
// Detect intent
const isSearch=lo.includes('tim')||lo.includes('kiem');
// "báo giá" NOT redirected — stays in chatbox for inline quote
const isBaoGia=lo.includes('bao gia')||lo.includes('combo')||lo.includes('du toan');
if(isBaoGia)return false; // let sendChat handle it
if(isSearch){
document.getElementById('aiSearch').value=msg;
doAISearch();
document.getElementById('aiResults').scrollIntoView({behavior:'smooth',block:'start'});
return true;
}
return false;
}
</script>
<script>
// Chat AI: only redirect pure SEARCH queries to AI search on main page
// "báo giá" handled by sendChat() inline (NOT redirected)
function _tryChatAI(msg){
var lo=msg.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/\u0111/g,'d');
// If it's a quote request → DO NOT intercept, let sendChat handle it
if(lo.includes('bao gia')||lo.includes('combo')||lo.includes('du toan'))return false;
return false; // Let all messages go through sendChat for AI response
}
</script>
<script>
// === FIX 1: Ensure quote table shows product images ===
// Override/patch the quote rendering to include images
(function(){
var origOpen = window.openQuoteModal || window.showQuoteModal;
// Patch: after quote modal opens, fix images
var fixQuoteImages = function(){
setTimeout(function(){
var rows = document.querySelectorAll('.quote-table tr');
var cart = window.CART || [];
rows.forEach(function(row, i){
if(i === 0) return; // skip header
var imgCell = row.querySelector('.qt-img, img');
var cartItem = cart[i-1];
if(cartItem && !imgCell){
// Add image if missing
var firstTd = row.querySelector('td');
if(firstTd && cartItem.image){
firstTd.innerHTML = '<img class="qt-img" src="'+cartItem.image+'" style="width:60px;height:60px;object-fit:contain;border-radius:4px">';
}
} else if(imgCell && cartItem && cartItem.image && imgCell.tagName === 'IMG' && !imgCell.src){
imgCell.src = cartItem.image;
}
});
}, 200);
};
// Hook into quote modal open
var origOpenCart = window.openCheckout || window.openQuote;
if(origOpenCart){
var wrapped = function(){
origOpenCart.apply(this, arguments);
fixQuoteImages();
};
if(window.openCheckout) window.openCheckout = wrapped;
if(window.openQuote) window.openQuote = wrapped;
}
document.addEventListener('click', function(e){
if(e.target && (e.target.textContent||'').includes('Báo giá')){
fixQuoteImages();
}
});
})();
// === FIX 2: CK discount calculation - client side, no API ===
(function(){
document.addEventListener('input', function(e){
if(e.target && e.target.classList.contains('qt-disc')){
var row = e.target.closest('tr');
if(!row) return;
var ckVal = parseFloat(e.target.value) || 0;
// Find price cell (don gia)
var cells = row.querySelectorAll('td');
var priceCell = null;
var ckPriceCell = null;
var ttCell = null;
var qtyCell = null;
cells.forEach(function(td, idx){
var text = td.textContent.replace(/[^\d]/g,'');
if(td.querySelector('.qt-disc')) return;
if(td.classList.contains('qt-price') || (text.length > 4 && !priceCell && idx > 2)){
if(!priceCell) priceCell = td;
else if(!ckPriceCell) ckPriceCell = td;
else if(!ttCell) ttCell = td;
}
});
// Get original price from data attribute or cell content
var origPrice = parseInt((row.dataset.price || (priceCell?priceCell.textContent:'')).replace(/[^\d]/g,'')) || 0;
var qty = parseInt(row.dataset.qty || '1') || 1;
if(origPrice > 0 && ckVal >= 0){
var discPrice = Math.round(origPrice * (1 - ckVal/100));
var thanhTien = discPrice * qty;
// Update cells
if(ckPriceCell) ckPriceCell.textContent = discPrice.toLocaleString('vi-VN');
if(ttCell) ttCell.textContent = thanhTien.toLocaleString('vi-VN');
// Update total
var totalEl = document.querySelector('.quote-total-row td:last-child') || document.querySelector('[class*="quote-total"]');
if(totalEl){
var allTT = 0;
document.querySelectorAll('.quote-table tr').forEach(function(r,i){
if(i===0)return;
var tds = r.querySelectorAll('td');
var lastNum = tds[tds.length-1];
if(lastNum) allTT += parseInt(lastNum.textContent.replace(/[^\d]/g,''))||0;
});
totalEl.textContent = allTT.toLocaleString('vi-VN') + ' VNĐ';
}
}
}
});
})();
// === FIX 3: Patch Excel export to use proper number format ===
(function(){
var origExport = window.exportQuoteExcel || window.downloadExcel;
window.exportQuoteExcel = window.downloadExcel = function(){
// If ExcelJS is available, create proper workbook
if(typeof ExcelJS === 'undefined'){
if(origExport) return origExport.apply(this, arguments);
alert('Excel library not loaded');
return;
}
var wb = new ExcelJS.Workbook();
var ws = wb.addWorksheet('Bao Gia');
// Header
ws.columns = [
{header:'STT', key:'stt', width:5},
{header:'Ma SP', key:'sku', width:15},
{header:'Ten SP', key:'name', width:40},
{header:'DVT', key:'dvt', width:6},
{header:'SL', key:'qty', width:5},
{header:'Don gia', key:'price', width:15},
{header:'CK %', key:'ck', width:8},
{header:'Don gia CK', key:'ckprice', width:15},
{header:'Thanh tien', key:'total', width:15},
];
// Style header
ws.getRow(1).font = {bold: true};
ws.getRow(1).fill = {type:'pattern', pattern:'solid', fgColor:{argb:'FF1F4E79'}};
ws.getRow(1).font = {bold:true, color:{argb:'FFFFFFFF'}};
// Get data from quote table
var rows = document.querySelectorAll('.quote-table tr');
var grandTotal = 0;
rows.forEach(function(row, i){
if(i===0) return; // skip header
if(row.classList.contains('quote-total-row')) return;
var cells = row.querySelectorAll('td');
var ckInput = row.querySelector('.qt-disc');
var ckVal = ckInput ? (parseFloat(ckInput.value)||0) : 0;
// Extract values
var texts = Array.from(cells).map(function(c){return c.textContent.trim()});
var price = parseInt((row.dataset.price || texts[5] || '0').replace(/[^\d]/g,'')) || 0;
var qty = parseInt(row.dataset.qty || texts[4] || '1') || 1;
var ckPrice = Math.round(price * (1 - ckVal/100));
var tt = ckPrice * qty;
grandTotal += tt;
var dataRow = ws.addRow({
stt: i,
sku: texts[1] || '',
name: texts[2] || '',
dvt: 'Bộ',
qty: qty,
price: price,
ck: ckVal,
ckprice: ckPrice,
total: tt
});
// Number format for money columns
dataRow.getCell('price').numFmt = '#,##0';
dataRow.getCell('ckprice').numFmt = '#,##0';
dataRow.getCell('total').numFmt = '#,##0';
});
// Total row
var totalRow = ws.addRow({name:'TONG CONG', total: grandTotal});
totalRow.font = {bold:true};
totalRow.getCell('total').numFmt = '#,##0';
// Add formulas
var dataStart = 2;
var dataEnd = ws.rowCount - 1;
for(var r=dataStart; r<=dataEnd; r++){
ws.getCell('H'+r).value = {formula: 'F'+r+'*(1-G'+r+'/100)'};
ws.getCell('I'+r).value = {formula: 'H'+r+'*E'+r};
}
ws.getCell('I'+(dataEnd+1)).value = {formula: 'SUM(I'+dataStart+':I'+dataEnd+')'};
// Download
wb.xlsx.writeBuffer().then(function(buf){
var blob = new Blob([buf], {type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
var url = URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.download = 'BaoGia_VAISTUDIO_' + new Date().toISOString().slice(0,10) + '.xlsx';
a.click();
URL.revokeObjectURL(url);
});
};
})();
</script>
<script>
if(localStorage.getItem('vas_quote_unlocked')==='1')document.body.classList.add('vas-unlocked');
</script>
<script>
// Saved orders list (stored in localStorage)
function getSavedOrders(){
try{return JSON.parse(localStorage.getItem('vas_orders')||'[]')}catch(e){return[]}
}
function saveSavedOrders(orders){
localStorage.setItem('vas_orders',JSON.stringify(orders));
}
function saveToOrder(idx){
if(typeof D==='undefined'||!D[idx])return;
var p=D[idx];
// Show order selection popup
var existing=document.getElementById('orderSelectPopup');
if(existing)existing.remove();
var orders=getSavedOrders();
var popup=document.createElement('div');
popup.id='orderSelectPopup';
popup.style.cssText='position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:9999;display:flex;align-items:center;justify-content:center;padding:20px';
var content='<div style="background:#fff;border-radius:12px;padding:20px;max-width:360px;width:100%;box-shadow:0 8px 30px rgba(0,0,0,.2)">';
content+='<h3 style="margin-bottom:12px;font-size:.95rem;color:#003f62">Chon don hang</h3>';
content+='<div style="font-size:.78rem;color:#666;margin-bottom:10px">SP: <b>'+((p.name||'').split('|').pop()||p.sku||'').trim().substring(0,40)+'</b></div>';
// List existing orders
if(orders.length>0){
content+='<div style="max-height:200px;overflow-y:auto;margin-bottom:10px">';
orders.forEach(function(ord,i){
content+='<div onclick="addToSelectedOrder('+idx+','+i+')" style="padding:8px 12px;border:1.5px solid #ddd;border-radius:6px;margin-bottom:6px;cursor:pointer;font-size:.8rem;transition:.2s" onmouseover="this.style.borderColor=\'#003f62\'" onmouseout="this.style.borderColor=\'#ddd\'">';
content+='<b>'+ord.name+'</b> <span style="color:#888">('+ord.items.length+' SP)</span>';
content+='</div>';
});
content+='</div>';
}else{
content+='<div style="color:#888;font-size:.8rem;margin-bottom:10px">Chua co don hang nao.</div>';
}
// New order button
content+='<div style="display:flex;gap:8px">';
content+='<button onclick="createNewOrderAndAdd('+idx+')" style="flex:1;padding:8px;background:#003f62;color:#fff;border:none;border-radius:6px;font-size:.8rem;cursor:pointer;font-weight:600">+ Don moi</button>';
content+='<button onclick="document.getElementById(\'orderSelectPopup\').remove()" style="padding:8px 14px;background:#eee;border:none;border-radius:6px;font-size:.8rem;cursor:pointer">Huy</button>';
content+='</div></div>';
popup.innerHTML=content;
popup.onclick=function(e){if(e.target===popup)popup.remove()};
document.body.appendChild(popup);
}
function createNewOrderAndAdd(idx){
var name=prompt('Ten don hang moi:','Don '+new Date().toLocaleDateString('vi'));
if(!name)return;
var orders=getSavedOrders();
var p=D[idx];
orders.push({name:name,items:[{sku:p.sku||p.model||'',name:p.name||'',price:p.priceNum||0,qty:1}],created:new Date().toISOString()});
saveSavedOrders(orders);
document.getElementById('orderSelectPopup').remove();
showToast('Da tao don "'+name+'" va them SP!');
}
function addToSelectedOrder(idx,orderIdx){
var orders=getSavedOrders();
var p=D[idx];
if(orders[orderIdx]){
orders[orderIdx].items.push({sku:p.sku||p.model||'',name:p.name||'',price:p.priceNum||0,qty:1});
saveSavedOrders(orders);
document.getElementById('orderSelectPopup').remove();
showToast('Da them vao don "'+orders[orderIdx].name+'"!');
}
}
function showToast(msg){
var t=document.createElement('div');
t.style.cssText='position:fixed;top:20px;right:20px;background:#28a745;color:#fff;padding:10px 16px;border-radius:8px;z-index:99999;font-size:.82rem;box-shadow:0 4px 12px rgba(0,0,0,.2)';
t.textContent=msg;
document.body.appendChild(t);
setTimeout(function(){t.remove()},2500);
}
</script>
<script src="malloca-shorts.js?v=3"></script>
<script src="ancuong.js?v=5"></script>
</body>
</html>