V.AISTUDIO / index.html
bep40's picture
Update: Fix UI functions
c324599 verified
Raw
History Blame
179 kB
<!DOCTYPE html>
<html lang="vi">
<head><script>window.huggingface={variables:{"SPACE_CREATOR_USER_ID":"661b9191e7b0ab12bceb66f3","VAISTUDIO":"HF_TOKEN_REDACTED","REBUILD_TRIGGER":"2"}};</script>
<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_main.png">
<link rel="canonical" id="canonicalUrl" href="https://bep40-v-aistudio.static.hf.space/">
<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_main.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}}
/* ===== ADD PRODUCT URL MODAL ===== */
.add-product-overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:360;align-items:center;justify-content:center;padding:18px;backdrop-filter:blur(4px)}
.add-product-overlay.open{display:flex}
.add-product-modal{background:#fff;border-radius:20px;max-width:560px;width:100%;box-shadow:0 12px 44px rgba(0,0,0,.22);overflow:hidden}
.add-product-head{background:linear-gradient(135deg,var(--p),#0077b6);color:#fff;padding:20px 24px;display:flex;align-items:center;justify-content:space-between;gap:12px}
.add-product-head h3{font-size:1.05rem;font-weight:800;display:flex;align-items:center;gap:9px;margin:0}
.add-product-close{width:34px;height:34px;border:none;background:rgba(255,255,255,.18);color:#fff;border-radius:10px;cursor:pointer}
.add-product-body{padding:22px 24px}
.add-product-body label{display:block;font-size:.78rem;font-weight:700;color:var(--d);margin:12px 0 6px}
.add-product-input{width:100%;padding:11px 13px;border:2px solid var(--gl);border-radius:10px;font-size:.86rem;font-family:inherit;outline:none}
.add-product-input:focus{border-color:var(--p);box-shadow:0 0 0 3px rgba(0,63,98,.08)}
.add-product-hint{font-size:.75rem;color:var(--g);line-height:1.55;margin-top:10px;background:#f8fafc;border-radius:10px;padding:10px 12px}
.add-product-actions{display:flex;gap:10px;margin-top:16px;flex-wrap:wrap}
.add-product-btn{border:none;border-radius:10px;padding:11px 16px;font-weight:800;cursor:pointer;font-family:inherit;display:inline-flex;align-items:center;justify-content:center;gap:8px}
.add-product-btn.primary{background:var(--p);color:#fff;flex:1}.add-product-btn.secondary{background:var(--gl);color:var(--d)}
.add-product-status{font-size:.8rem;margin-top:14px;line-height:1.6;white-space:pre-line}.add-product-status.ok{color:#047857}.add-product-status.err{color:#b91c1c}
@media(max-width:768px){.add-product-modal{border-radius:14px}.add-product-head,.add-product-body{padding:16px}.add-product-actions{flex-direction:column}.add-product-btn{width:100%}}
.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>
<style>
/* ===== ZALO BOT ===== */
.zalo-fab{position:fixed;bottom:24px;right:24px;z-index:200;width:60px;height:60px;border-radius:50%;box-shadow:0 4px 20px rgba(0,104,255,.35);transition:all .25s ease;overflow:hidden;display:block;cursor:pointer}
.zalo-fab:hover{transform:scale(1.1);box-shadow:0 6px 28px rgba(0,104,255,.5)}
.zalo-fab img{width:100%;height:100%;object-fit:cover;border-radius:50%}
@media(max-width:768px){.zalo-fab{bottom:16px;right:16px;width:52px;height:52px}}
</style>
</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>
<!-- ADD PRODUCT BY URL MODAL -->
<div class="add-product-overlay" id="addProductOverlay" onclick="if(event.target===this)closeAddProductModal()">
<div class="add-product-modal">
<div class="add-product-head">
<h3><i class="fas fa-link"></i> Thêm sản phẩm qua URL</h3>
<button class="add-product-close" onclick="closeAddProductModal()"><i class="fas fa-times"></i></button>
</div>
<div class="add-product-body">
<label>Mã truy cập</label>
<input class="add-product-input" id="addProductSecret" type="password" placeholder="Nhập mã V.AI STUDIO">
<label>URL sản phẩm</label>
<input class="add-product-input" id="addProductUrl" type="url" placeholder="https://.../san-pham/...">
<div class="add-product-hint">AI/scraper sẽ phân tích cấu trúc website để lấy hình ảnh, tên SP, mã/model, giá, brand, danh mục, mô tả, thông số kỹ thuật và tính năng; sau đó lưu vào <b>products_url_added.json</b>.</div>
<div class="add-product-actions">
<button class="add-product-btn secondary" onclick="closeAddProductModal()"><i class="fas fa-times"></i> Đóng</button>
<button class="add-product-btn primary" id="addProductSubmit" onclick="submitAddProductUrl()"><i class="fas fa-wand-magic-sparkles"></i> Thêm SP</button>
</div>
<div class="add-product-status" id="addProductStatus"></div>
</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_main.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="https://bot.zaloplatforms.com/bots/4013442364902370344" target="_blank" title="Tư vấn Zalo" style="background:#0068FF;color:#fff"><i class="fas fa-comment-dots"></i></a>
<a href="#" onclick="openAddProductModal();return false" title="Thêm sản phẩm qua URL" style="background:rgba(219,152,21,.12);color:var(--a)"><i class="fas fa-plus"></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_main.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.png" 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.jpg" 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 Bot tư vấn</strong><br><a href="https://bot.zaloplatforms.com/bots/4013442364902370344" target="_blank" style="color:var(--p);font-weight:700">Zalo Bot</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://bot.zaloplatforms.com/bots/4013442364902370344" 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="https://bot.zaloplatforms.com/bots/4013442364902370344" target="_blank" style="color:var(--p);font-weight:700">Tư vấn Zalo →</a></div></li>
</ul>
<div style="display:flex;gap:8px;margin-top:16px">
<a href="https://bot.zaloplatforms.com/bots/4013442364902370344" 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://bot.zaloplatforms.com/bots/4013442364902370344" 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_main.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="saveQuote()" style="background:#059669"><i class="fas fa-save"></i> Lưu</button>
<button class="quote-btn" onclick="createOrder()" style="background:#0068FF"><i class="fas fa-shopping-cart"></i> Đơn hàng</button>
<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>
<label class="quote-btn" style="background:#fff;color:#111;border:1px solid #111"><input id="vai-bw-export" type="checkbox" style="width:16px;height:16px;margin-right:6px"> Trắng đen</label>
<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_main.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://bot.zaloplatforms.com/bots/4013442364902370344" target="_blank" title="Zalo"><i class="fas fa-comment-dots"></i></a><a href="https://bot.zaloplatforms.com/bots/4013442364902370344" 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="https://bot.zaloplatforms.com/bots/4013442364902370344" target="_blank">💬 Tư vấn Zalo</a></li></ul></div>
<div><h4>Hỗ Trợ</h4><ul class="footer-links"><li><a href="https://bot.zaloplatforms.com/bots/4013442364902370344" target="_blank">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://bot.zaloplatforms.com/bots/4013442364902370344" target="_blank">Zalo: Zalo Bot</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_konox.json'].map(f=>fetch(f).then(r=>r.ok?r.json():[]).catch(()=>[]))).then(all=>{let deleted=(all.pop()||[]).map(x=>String((x&&typeof x==='object')?(x.key||x.sku||x.model||x.slug||x.url||''):x).toLowerCase().replace(/[^a-z0-9]/g,''));let isDel=p=>{let vals=[p.sku,p.mod,p.slug,p.l,p.n].filter(Boolean).map(v=>String(v).toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/đ/g,'d').replace(/[^a-z0-9]/g,''));return deleted.some(d=>d&&vals.some(v=>d===v||(d.length>=4&&v.includes(d))||(v.length>=4&&d.includes(v))));};let d=[].concat(...all).filter(p=>!isDel(p));D=d.map(p=>{let br=p.brand||(p.n.includes('| Demax |')?'Demax':p.n.includes('| Canzy |')?'Canzy':p.n.includes('| Garis |')?'Garis':p.n.includes('| Grob |')?'Grob':p.n.includes('| Eurogold |')||p.n.startsWith('Eurogold |')?'Eurogold':(p._source||'').toLowerCase().includes('điện máy xanh')?'_dmx':'Malloca');let logoMap={Malloca:'https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/partners/malloca.png',Eurogold:'https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/partners/eurogold.png',Grob:'https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/partners/grob.png',Garis:'https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/partners/garis.png',Canzy:'https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/partners/canzy.png',Demax:'https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/partners/demax.png',Hafele:'https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/partners/hafele.png',_dmx:'https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/partners/dmx.jpg'};let imgs=(p.imgs||[]).filter(Boolean);let img=p.i||imgs[0]||logoMap[br]||'https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/logo_main.png';if(!imgs.length&&img)imgs=[img];let o={name:p.n,link:p.l,image:img,price:p.p,priceNum:p.pn,cat:p.c,catSlug:p.cs,catIcon:p.ci,subCat:p.sub_c||'',subCatSlug:p.sub_cs||'',images: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:br,_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;});window.D=D;window.S=S;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+'/';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{_imgFallback(this)}"><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];
if(!p)return;
if(!p.images)p.images=[];
if(!p.feats)p.feats=[];
if(!p.specs)p.specs={};
if(!p.slug)p.slug='';
if(!p.name)p.name='';
if(!p.image)p.image='';
// Update URL with SEO-friendly slug using History API
if(!_skipHistory){
let slugUrl='/san-pham/'+p.slug+'/'; 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+'/';
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('canonicalUrl'))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}/" 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" id="detailInfo">
${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();window.open('https://bot.zaloplatforms.com/bots/4013442364902370344','_blank')"><i class="fas fa-comment-dots"></i> Tư vấn Zalo</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>`;
try{
var _di=document.getElementById('detailInfo')||document.getElementById('detailView');
if(_di){_di.scrollIntoView({behavior:'smooth',block:'start'});}
else{window.scrollTo({top:0,behavior:'smooth'});}
}catch(e){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_main.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('canonicalUrl'))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(){if(typeof hideSearchDropdown==='function')hideSearchDropdown();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'},'','/');}
var _dv=document.getElementById('detailView');if(_dv){_dv.scrollIntoView({behavior:'smooth',block:'start'});}else{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://bot.zaloplatforms.com/bots/4013442364902370344" target="_blank" style="color:var(--p)"><strong>Zalo Bot</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(){
try{
var u=new URL(location.href);
var shortId=u.searchParams.get('short')||u.searchParams.get('newshort');
if(!shortId){var m=location.pathname.match(/\/shorts\/([^\/]+)/);if(m)shortId=m[1];if(!shortId){m=location.pathname.match(/\/ac-shorts\/([^\/]+)/);if(m)shortId=m[1];}}
if(shortId){
if(typeof openShortById==='function'){openShortById(shortId);return;}
if(typeof openSharedShort==='function'){openSharedShort();return;}}
}catch(e){}
let path=location.pathname;
// Handle ?p= redirect from static OG pages
let qp=new URLSearchParams(location.search).get('p')||new URLSearchParams(location.search).get('product');
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'){
var pIdx=-1;
if(typeof state.idx==='number'&&state.idx>=0&&state.idx<D.length){pIdx=state.idx;}
else if(state.slug){pIdx=D.findIndex(function(p){return p.slug===state.slug;});}
if(pIdx>=0){
let dv=document.getElementById('detailView');
if(!dv.classList.contains('show')){showDetailNoHistory(pIdx);}
}
}else if(state&&state.type==='short'){
// Short video navigation handled by shorts JS files
if(typeof parseCurrentPath==='function')parseCurrentPath();
}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.href;
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://bot.zaloplatforms.com/bots/4013442364902370344'+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);
}
// ===== 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_main.png" style="height:30px;filter:brightness(10)" onerror="_imgFallback(this)">
<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://bot.zaloplatforms.com/bots/4013442364902370344" target="_blank" style="color:var(--p);font-weight:600">Zalo Bot</a> · <a href="https://bot.zaloplatforms.com/bots/4013442364902370344" target="_blank" style="color:var(--p);font-weight:600"></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: Zalo Bot | \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();setTimeout(function(){URL.revokeObjectURL(url);},60000);
showToast('Đã tải báo giá Excel!');
}
// ===== QUOTATION SYSTEM =====
function exportQuoteCSV(qd){
if(!qd) qd=getQuoteData();
let today=new Date().toLocaleDateString('vi-VN');
let total=qd.grandTotal||0;
// Build CSV with BOM for Excel UTF-8
let csv='\uFEFF';
csv+='BÁO GIÁ V.AI STUDIO\n';
csv+='Khách hàng:,'+(qd.customer.name||'')+'\n';
csv+='SĐT:,'+(qd.customer.phone||'')+'\n';
csv+='Email:,'+(qd.customer.email||'')+'\n';
csv+='Địa chỉ:,'+(qd.customer.addr||'')+'\n';
csv+='Mã đơn hàng:,'+(qd.customer.orderCode||'')+'\n';
csv+='Ngày:,'+(qd.customer.date||'')+'\n\n';
csv+='STT,Tên sản phẩm,Mã SP,Thông tin,SL,Đơn giá,Đơn giá CK,Thành tiền,Ghi chú\n';
qd.items.forEach((it,i)=>{
let name=(it.name||'').replace(/,/g,' -');
let model=(it.model||'').replace(/,/g,' -');
let specs=(it.specs||'').replace(/,/g,' -').substring(0,100);
csv+=(i+1)+','+name+','+model+','+specs+','+(it.qty||1)+','+(it.price||0)+','+(it.discPrice||0)+','+(it.total||0)+','+(it.note||'')+'\n';
});
csv+='\n,,,,,,,TỔNG CỘNG,'+total+'\n';
csv+='\nZalo: Zalo Bot | \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_'+(qd.customer.orderCode||'VAI_STUDIO')+'.csv';
a.click();setTimeout(function(){URL.revokeObjectURL(url);},60000);
showToast('Đã xuất file CSV (mở bằng Excel)!');
}
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="_imgFallback(this)" 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 vaiQuoteDimKey(k,v){let t=nVN(String(k||'')+' '+String(v||''));return /kich thuoc|dimension|size|rong|ngang|dai|sau|cao|height|width|depth|cut.?out|cat da|lo da|lap noi|lap am|am ban|lot long|phu bi|khoang tu|mat canh|quy cach|r\s*[x=]|w\s*[x=]|d\s*[x=]|h\s*[x=]/.test(t)}
function vaiQuoteDimSummary(specs,limit=900){let parts=[],seen=new Set();function add(k,v){if(!k||v==null||v==='')return;let t=String(k)+': '+String(v),key=nVN(t).replace(/[^a-z0-9]/g,'');if(seen.has(key))return;seen.add(key);parts.push(t)};if(specs&&typeof specs==='object'){let e=Object.entries(specs);e.filter(x=>vaiQuoteDimKey(x[0],x[1])).forEach(x=>add(x[0],x[1]));e.filter(x=>!vaiQuoteDimKey(x[0],x[1])).slice(0,8).forEach(x=>add(x[0],x[1]));}else if(specs){String(specs).split(/[;\n]+/).filter(Boolean).sort((a,b)=>(vaiQuoteDimKey(b,'')?1:0)-(vaiQuoteDimKey(a,'')?1:0)).forEach(x=>{let key=nVN(x).replace(/[^a-z0-9]/g,'');if(!seen.has(key)){seen.add(key);parts.push(x.trim())}})}return parts.join('; ').slice(0,limit)}
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:vaiQuoteDimSummary(product.specs||{},900)||(product.summary||product.desc||'').slice(0,900),
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 REAL NUMBERS + FORMULAS v2 =====
function _vaiExcelParseExtras(){
let txt='';
document.querySelectorAll('input,textarea').forEach(el=>{
let ph=(el.placeholder||'').toLowerCase();
let v=(el.value||'').trim();
if(!v)return;
if(v.toUpperCase().replace(/[\s.]/g,'')==='VAISTUDIO')return;
if(ph.includes('yêu cầu')||ph.includes('yeu cau')||ph.includes('chiết khấu')||ph.includes('chiet khau')||ph.includes('ghi chú')||ph.includes('ghi chu'))txt+=(txt?'\n':'')+v;
});
let out={fees:[],deposit:0,discountPercent:0,notes:[]};
if(!txt)return out;
txt.split(/[,;\n]+/).forEach(line=>{
line=(line||'').trim();if(!line)return;
let lo=line.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/[đĐ]/g,'d');
let ck=lo.match(/^(ck|chiet khau|giam|discount)\s*([\d.,]+)\s*%?/);
if(ck){let v=parseFloat(ck[2].replace(/\./g,'').replace(',','.'));if(v>0&&v<=100)out.discountPercent=v;return;}
if(/coc|dat coc|deposit/.test(lo)){
let m=lo.match(/([\d.,]+)\s*(k|nghin|tr|trieu|m|%)?/);
if(m){let a=parseFloat(m[1].replace(/\./g,'').replace(',','.'));let u=(m[2]||'').toLowerCase();if(u==='k'||u==='nghin')a*=1000;else if(u==='tr'||u==='trieu'||u==='m')a*=1000000;out.deposit=a||0;}
return;
}
let fm=lo.match(/^(giao hang|giao|ship|van chuyen|lap dat|lap|phi giao|phi lap|phi khac|phu phi|phi|boc xep|boc|xep)\s*([\d.,]+)\s*(k|nghin|tr|trieu|m)?/);
if(fm){
let a=parseFloat(fm[2].replace(/\./g,'').replace(',','.'));let u=(fm[3]||'').toLowerCase();
if(u==='k'||u==='nghin')a*=1000;else if(u==='tr'||u==='trieu'||u==='m')a*=1000000;else if(a>0&&a<500)a*=1000;
let labelMap={'giao hang':'Phí giao hàng','giao':'Phí giao hàng','ship':'Phí giao hàng','van chuyen':'Phí vận chuyển','lap dat':'Phí lắp đặt','lap':'Phí lắp đặt','phi giao':'Phí giao hàng','phi lap':'Phí lắp đặt','phi khac':'Phí khác','phu phi':'Phụ phí','phi':'Phụ phí','boc xep':'Phí bốc xếp','boc':'Phí bốc xếp','xep':'Phí bốc xếp'};
out.fees.push({label:labelMap[fm[1]]||'Phụ phí',amount:a||0});return;
}
out.notes.push(line);
});
return out;
}
async function exportExcel(){
exportExcel.__vaiOriginal = true;
if(typeof ExcelJS==='undefined'){
showToast('⚠️ Thư viện Excel chưa tải xong. Vui lòng chờ 2-3 giây rồi thử lại.');
return;
}
let qd=getQuoteData();
let extras=_vaiExcelParseExtras();
let wb=new ExcelJS.Workbook();
wb.creator='V.AI STUDIO';
let ws=wb.addWorksheet('Báo Giá');
let MONEY_FMT='#,##0"đ"', NUM_FMT='#,##0';
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++;
// 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;
// Determine discount: explicit CK35% wins; otherwise infer ratio from Giá CK/Đơn giá.
let ckPct=Number(extras.discountPercent||0);
if(!(ckPct>0&&ckPct<=100)&&qd.items.length>0&&qd.items[0].price>0){
let ratio=(Number(qd.items[0].discPrice||qd.items[0].price)||0)/(Number(qd.items[0].price)||1);
let allSameRatio=qd.items.every(it=>!it.price||Math.abs((Number(it.discPrice||it.price)/Number(it.price))-ratio)<0.005);
if(allSameRatio&&ratio<1&&ratio>0)ckPct=Math.round((1-ratio)*100);
}
qd.items.forEach((item,idx)=>{
let row=ws.getRow(r);row.height=64;
row.getCell(1).value=Number(item.stt||idx+1);row.getCell(1).numFmt=NUM_FMT;row.getCell(1).alignment={horizontal:'center',vertical:'middle'};row.getCell(1).font={size:10};
row.getCell(2).value=item.name||'';row.getCell(2).alignment={vertical:'middle',wrapText:true};row.getCell(2).font={size:10,bold:true,color:{argb:'FF1A1A1A'}};
row.getCell(3).value=item.model||'';row.getCell(3).alignment={horizontal:'center',vertical:'middle'};row.getCell(3).font={size:9,color:{argb:'FF003F62'}};
row.getCell(4).value=item.specs||'';row.getCell(4).alignment={vertical:'middle',wrapText:true};row.getCell(4).font={size:8,color:{argb:'FF888888'}};
row.getCell(5).value='';row.getCell(5).alignment={horizontal:'center',vertical:'middle'};
try{let imgEl=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);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(ie){}
row.getCell(6).value=Number(item.qty||1);row.getCell(6).numFmt=NUM_FMT;row.getCell(6).alignment={horizontal:'center',vertical:'middle'};row.getCell(6).font={size:10,bold:true};
row.getCell(7).value=Number(item.price)||0;row.getCell(7).numFmt=MONEY_FMT;row.getCell(7).alignment={horizontal:'right',vertical:'middle'};row.getCell(7).font={size:10};
let cH=row.getCell(8);
if(ckPct>0&&ckPct<100){let factor=Number((1-ckPct/100).toFixed(4));cH.value={formula:`G${r}*${factor}`,result:Math.round((Number(item.price)||0)*factor)};}
else{cH.value=Number(item.discPrice)||Number(item.price)||0;}
cH.numFmt=MONEY_FMT;cH.alignment={horizontal:'right',vertical:'middle'};if((Number(item.discPrice)||0)<(Number(item.price)||0)){cH.font={size:10,color:{argb:'FFDC3545'},bold:true}}else{cH.font={size:10}}
let cI=row.getCell(9);cI.value={formula:`F${r}*H${r}`,result:(Number(item.qty||1))*(Number(item.discPrice)||Number(item.price)||0)};cI.numFmt=MONEY_FMT;cI.alignment={horizontal:'right',vertical:'middle'};cI.font={size:10,bold:true,color:{argb:'FF003F62'}};
row.getCell(10).value=item.note||'';row.getCell(10).alignment={vertical:'middle',wrapText:true};row.getCell(10).font={size:9};
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((idx+1)%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;
ws.getColumn(6).numFmt=NUM_FMT;ws.getColumn(7).numFmt=MONEY_FMT;ws.getColumn(8).numFmt=MONEY_FMT;ws.getColumn(9).numFmt=MONEY_FMT;
// PHỤ PHÍ rows — included in TỔNG CỘNG
let totalFormulaParts=[`SUM(I${dataStartR}:I${dataEndR})`];
(extras.fees||[]).forEach(f=>{
ws.mergeCells(r,1,r,8);
ws.getCell('A'+r).value='⊕ '+(f.label||'Phụ phí');ws.getCell('A'+r).font={size:10,color:{argb:'FF92400E'}};ws.getCell('A'+r).alignment={horizontal:'right',vertical:'middle'};
ws.getCell('I'+r).value=Number(f.amount||0);ws.getCell('I'+r).numFmt=MONEY_FMT;ws.getCell('I'+r).font={bold:true,size:10,color:{argb:'FF92400E'}};ws.getCell('I'+r).alignment={horizontal:'right',vertical:'middle'};
for(let c=1;c<=10;c++){ws.getRow(r).getCell(c).fill={type:'pattern',pattern:'solid',fgColor:{argb:'FFFFFBEB'}};ws.getRow(r).getCell(c).border={top:{style:'thin',color:{argb:'FFD0D5DD'}},bottom:{style:'thin',color:{argb:'FFD0D5DD'}}};}
totalFormulaParts.push(`I${r}`);r++;
});
// TỔNG CỘNG = SUM sản phẩm + phụ phí nếu có
let totalR=r;
ws.mergeCells(totalR,1,totalR,8);
ws.getCell('A'+totalR).value='TỔNG CỘNG:';ws.getCell('A'+totalR).font={bold:true,size:12,color:{argb:'FFFFFFFF'}};ws.getCell('A'+totalR).alignment={horizontal:'right',vertical:'middle'};
// Tổng cộng số lượng nằm CÙNG DÒNG tổng cộng, tại cột SL (F)
ws.getCell('F'+totalR).value={formula:`SUM(F${dataStartR}:F${dataEndR})`,result:qd.items.reduce((s,it)=>s+Number(it.qty||1),0)};
ws.getCell('F'+totalR).numFmt=NUM_FMT;ws.getCell('F'+totalR).font={bold:true,size:12,color:{argb:'FFFFFFFF'}};ws.getCell('F'+totalR).alignment={horizontal:'center',vertical:'middle'};
ws.getCell('I'+totalR).value={formula:totalFormulaParts.join('+'),result:(qd.items.reduce((s,it)=>s+(Number(it.qty||1))*(Number(it.discPrice)||Number(it.price)||0),0)+(extras.fees||[]).reduce((s,f)=>s+Number(f.amount||0),0))};
ws.getCell('I'+totalR).numFmt=MONEY_FMT;ws.getCell('I'+totalR).font={bold:true,size:13,color:{argb:'FFFFFFFF'}};ws.getCell('I'+totalR).alignment={horizontal:'right',vertical:'middle'};
for(let c=1;c<=10;c++){ws.getRow(totalR).getCell(c).fill={type:'pattern',pattern:'solid',fgColor:{argb:'FF003F62'}};ws.getRow(totalR).getCell(c).border={top:{style:'medium',color:{argb:'FF003F62'}},bottom:{style:'thin',color:{argb:'FF003F62'}}};}
ws.getRow(totalR).height=30;r++;
// CỌC if có
let depositR=null;
if(Number(extras.deposit||0)>0){
depositR=r;ws.mergeCells(r,1,r,8);
ws.getCell('A'+r).value='Đã cọc:';ws.getCell('A'+r).font={bold:true,size:11,color:{argb:'FF166534'}};ws.getCell('A'+r).alignment={horizontal:'right',vertical:'middle'};
ws.getCell('I'+r).value=Number(extras.deposit||0);ws.getCell('I'+r).numFmt=MONEY_FMT;ws.getCell('I'+r).font={bold:true,size:11,color:{argb:'FF166534'}};ws.getCell('I'+r).alignment={horizontal:'right',vertical:'middle'};
for(let c=1;c<=10;c++){ws.getRow(r).getCell(c).fill={type:'pattern',pattern:'solid',fgColor:{argb:'FFF0FDF4'}};ws.getRow(r).getCell(c).border={top:{style:'thin',color:{argb:'FFD0D5DD'}},bottom:{style:'thin',color:{argb:'FFD0D5DD'}}};}
r++;
ws.mergeCells(r,1,r,8);
ws.getCell('A'+r).value='Còn lại:';ws.getCell('A'+r).font={bold:true,size:11,color:{argb:'FFDC2626'}};ws.getCell('A'+r).alignment={horizontal:'right',vertical:'middle'};
ws.getCell('I'+r).value={formula:`MAX(I${totalR}-I${depositR},0)`,result:Math.max((qd.items.reduce((s,it)=>s+(Number(it.qty||1))*(Number(it.discPrice)||Number(it.price)||0),0)+(extras.fees||[]).reduce((s,f)=>s+Number(f.amount||0),0))-Number(extras.deposit||0),0)};
ws.getCell('I'+r).numFmt=MONEY_FMT;ws.getCell('I'+r).font={bold:true,size:12,color:{argb:'FFDC2626'}};ws.getCell('I'+r).alignment={horizontal:'right',vertical:'middle'};
for(let c=1;c<=10;c++){ws.getRow(r).getCell(c).fill={type:'pattern',pattern:'solid',fgColor:{argb:'FFFEF2F2'}};ws.getRow(r).getCell(c).border={top:{style:'thin',color:{argb:'FFD0D5DD'}},bottom:{style:'thin',color:{argb:'FFD0D5DD'}}};}
r++;
}
// Optional notes
if(extras.notes&&extras.notes.length){ws.mergeCells(r,1,r,10);ws.getCell('A'+r).value='📝 Ghi chú: '+extras.notes.join('; ');ws.getCell('A'+r).font={italic:true,size:9,color:{argb:'FF166534'}};ws.getCell('A'+r).alignment={wrapText:true};r++;}
r++;
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: Zalo Bot | ';ws.getCell('A'+r).font={italic:true,size:9,color:{argb:'FF888888'}};ws.getCell('A'+r).alignment={wrapText:true};
wb.calcProperties.fullCalcOnLoad=true;
try{
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();setTimeout(function(){URL.revokeObjectURL(url);},60000);
showToast('Đã xuất file Excel số + công thức!');
}catch(e){
console.error('ExcelJS error:',e);
showToast('⚠️ Xuất Excel thất bại, chuyển sang CSV...');
setTimeout(function(){exportQuoteCSV(qd);},300);
}
}
// ===== PDF EXPORT (html2canvas for Vietnamese) =====
async function exportPDF(){
exportPDF.__vaiOriginal = true;
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="_imgFallback(this)"></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_main.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://bot.zaloplatforms.com/bots/4013442364902370344" target="_blank">Zalo Bot</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,backgroundColor:'#fff'});
let imgData=canvas.toDataURL('image/jpeg',0.95);
if(typeof window.jspdf==='undefined'||!window.jspdf.jsPDF){
showToast('⚠️ Thư viện PDF chưa tải. Vui lòng chờ 2-3 giây rồi thử lại.');
div.remove();return;
}
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;// html2canvas scale:2
let totalH=imgH*ratio;
let pageH=pdfH-10;let y=5;let srcY=0;
let sliceH=pageH/ratio;
// 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
});
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="_imgFallback(this)"></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_main.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> Zalo Bot</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',logging:false});
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>
<!-- 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>
<!-- AI Search and UI Fixes - embedded for reliability -->
<script>
/**
* AI Search - Semantic search with budget extraction
*/
window.aiSearch = async function(query, products, token) {
if (!products || !products.length) {
return { results: [], aiAnswer: null, categories: [], constraints: [], budget: 0, combos: [] };
}
const norm = (s) => String(s || '').toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/[đĐ]/g, 'd');
const q = norm(query);
let budget = 0, categories = [], constraints = [], combos = [];
const budgetPatterns = [/(dưới|duoi)\s*([\d,.]+)\s*(triệu|nghin|nghìn|k|đồng)/gi, /(tối đa)\s*([\d,.]+)\s*(triệu|nghin|nghìn|k|đồng)/gi, /([\d,.]+)\s*(triệu|nghin|nghìn|k|đồng)/gi];
for (const pattern of budgetPatterns) {
const match = query.replace(/[đĐ]/g, 'd').match(pattern);
if (match) {
const amt = parseFloat(match[0].replace(/[^\d,.]/g, '').replace(',', '.'));
const unit = match[0].toLowerCase().match(/(triệu|nghin|nghìn|k|đồng)/g);
if (unit) { if (unit[0] === 'triệu') budget = amt * 1000000; else if (unit[0] === 'nghìn' || unit[0] === 'nghin' || unit[0] === 'k') budget = amt * 1000; else budget = amt; }
constraints.push({ label: 'Ngân sách: ' + match[0].trim() }); break;
}
}
const catMap = {'bep':'Bếp','bep-tu':'Bếp từ','bep-gas':'Bếp gas','hut-mui':'Máy hút mùi','hut-khoi':'Máy hút khói','chau-rua':'Chậu rửa','voi-rua':'Vòi rửa','lo-nuong':'Lò nướng','tu-lanh':'Tủ lạnh','may-rua-chen':'Máy rửa chén','gia-bo':'Kệ bếp'};
Object.keys(catMap).forEach(key => { if (q.includes(key) || query.toLowerCase().includes(key)) categories.push(catMap[key]); });
let results = [];
for (let i = 0; i < products.length && results.length < 30; i++) {
const p = products[i]; let score = 0;
const name = norm(p.name || ''), sku = norm(p.sku || p.mod || ''), brand = norm(p.brand || ''), cat = norm(p.cat || ''), summary = norm(p.summary || '');
if (name.includes(q)) score += 10; if (sku.includes(q)) score += 8; if (brand.includes(q)) score += 6; if (cat.includes(q)) score += 4; if (summary.includes(q)) score += 3;
if (budget > 0 && p.priceNum && p.priceNum > budget) score = 0;
const words = q.split(/\s+/).filter(w => w.length > 1);
words.forEach(w => { if (name.includes(w)) score += 2; if (sku.includes(w)) score += 1; if (brand.includes(w)) score += 1; if (cat.includes(w)) score += 1; });
if (score > 0) { const labels = []; if (p.priceNum && budget > 0 && p.priceNum <= budget) labels.push('Trong ngân sách'); results.push({ p, idx: i, score, labels }); }
}
results.sort((a, b) => b.score - a.score);
if (budget > 0 && results.length >= 2) {
let comboItems = [];
for (let i = 0; i < results.length && comboItems.length < 5; i++) comboItems.push({ p: results[i].p, idx: results[i].idx });
if (comboItems.length >= 2) combos.push({ items: comboItems, total: comboItems.reduce((s, it) => s + (it.p.priceNum || 0), 0) });
}
let aiAnswer = results.length > 0 ? 'Tìm thấy ' + results.length + ' sản phẩm phù hợp.' : 'Không tìm thấy sản phẩm nào. Thử: "bếp từ", "máy hút mùi", "Malloca", "Eurogold".';
return { results, aiAnswer, categories, constraints, budget, combos };
};
window.doAISearch = function() {
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.innerHTML = '💡 Nhập mô tả: "bếp từ dưới 15 triệu", "máy hút mùi Malloca"'; resultsDiv.style.display = 'block'; return; }
resultsDiv.style.display = 'block'; resultsDiv.innerHTML = '⏳ Đang tìm...';
var D = (typeof window.D !== 'undefined' && window.D.length > 0) ? window.D : null;
if (typeof window.aiSearch === 'function' && D) {
window.aiSearch(query, D, '').then(function(aiResult) {
if (!aiResult || (!aiResult.results.length && !aiResult.aiAnswer)) { _fallbackAISearch(query, resultsDiv); return; }
_renderAIResultsToDiv(aiResult, resultsDiv, query);
}).catch(function(err) { _fallbackAISearch(query, resultsDiv); console.error('AI Search error:', err); });
} else { _fallbackAISearch(query, resultsDiv); }
};
function _renderAIResultsToDiv(aiResult, container, query) {
var badges = '';
if (aiResult.categories && aiResult.categories.length > 0) badges += '<span style="display:inline-block;background:rgba(0,63,98,.08);color:#003f62;padding:4px 10px;border-radius:20px;font-size:.7rem;margin-right:6px">' + aiResult.categories.join(', ') + '</span>';
if (aiResult.budget && aiResult.budget > 0) badges += '<span style="display:inline-block;background:rgba(46,125,50,.1);color:#2e7d32;padding:4px 10px;border-radius:20px;font-size:.7rem;margin-right:6px">Tối da ' + (aiResult.budget/1000000).toFixed(0) + ' trieu</span>';
var aiHtml = aiResult.aiAnswer ? '<div style="background:rgba(0,63,98,.04);border-radius:12px;padding:12px;margin-bottom:12px">' + aiResult.aiAnswer + '</div>' : '';
var items = aiResult.results || [];
var html = '<div style="margin-bottom:8px">' + badges + '</div>' + aiHtml;
if (items.length > 0) {
html += '<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:12px">';
html += items.map(function(r, i) {
var p = r.p, idx = r.idx;
var brandColor = p.brand === 'Eurogold' ? '#c8102e' : (p.brand === 'Grob' ? '#2e7d32' : '#003f62');
return '<div style="position:relative" onclick="showDetail(' + idx + ')">' +
'<div class="pc fade" style="cursor:pointer;transition-delay:' + Math.min(i * 20, 300) + 'ms">' +
'<div class="pi" style="height:150px"><img src="' + (p.image || '') + '" alt="' + p.name + '" loading="lazy" onerror="this.style.display=\'none\'">' +
'<span class="pi-badge">' + (p.cat || '') + '</span>' +
'<span style="position:absolute;top:8px;right:8px;background:' + brandColor + ';color:#fff;padding:3px 7px;border-radius:5px;font-size:.55rem">' + (p.brand || '') + '</span></div>' +
'<div class="pb"><div class="pn" style="font-size:.76rem">' + p.name + '</div><div class="pp">' + (p.price || 'Liên he') + '</div></div></div>' +
'<button onclick="event.stopPropagation();addToCart(' + idx + ');" style="position:absolute;top:8px;left:8px;background:var(--a);color:#fff;border:none;width:28px;height:28px;border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:.7rem;z-index:2;box-shadow:0 2px 6px rgba(0,0,0,.2)"><i class="fas fa-cart-plus"></i></button>' +
'</div>';
}).join('') + '</div>';
}
container.innerHTML = html;
}
function _fallbackAISearch(query, container) {
var D = (typeof window.D !== 'undefined') ? window.D : null;
if (!D || !D.length) { container.innerHTML = '⏳ Đang tai du lieu...'; return; }
var qNorm = query.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/đ/g, 'd');
var results = [];
for (var i = 0; i < D.length && results.length < 20; i++) {
var p = D[i]; if (!p) continue;
var nameNorm = (p.name || '').toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/đ/g, 'd');
if (nameNorm.indexOf(qNorm) !== -1) results.push(p);
}
if (!results.length) { container.innerHTML = '❌ Khong tim thay. Thử: "bep tu", "may hut mui"'; return; }
container.innerHTML = '<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:12px">' +
results.map(function(p) { var idx = window.D.indexOf(p); return '<div style="position:relative" onclick="showDetail(' + idx + ')">' +
'<div class="pc" style="cursor:pointer"><div class="pi" style="height:150px"><img src="' + p.image + '" alt="' + p.name + '"></div>' +
'<div class="pb"><div class="pn">' + p.name + '</div><div class="pp">' + (p.price || '') + '</div></div></div>' +
'<button onclick="event.stopPropagation();addToCart(' + idx + ');" style="position:absolute;top:8px;left:8px;background:var(--a);color:#fff;border:none;width:28px;height:28px;border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:.7rem;z-index:2"><i class="fas fa-cart-plus"></i></button></div>'; }).join('') + '</div>';
}
/**
* Order picker
*/
window._showOrderPicker = function(product, callback) {
if (!product) { callback && callback('cancel'); return; }
var idx = -1;
if (window.D && window.D.length) {
for (var i = 0; i < window.D.length; i++) {
var p = window.D[i]; if (!p) continue;
if (p.slug === product.slug || p.sku === product.sku || p.model === product.model) { idx = i; break; }
}
}
if (idx >= 0 && typeof addToCart === 'function') { addToCart(idx); callback && callback('ok'); }
else { callback && callback('cancel'); }
};
/** Export Excel/PDF */
window.exportExcel = function() {
if (!window.cart || !window.cart.length) { alert('Gio hang trong. Them san pham truoc.'); return; }
var total = 0; var csv = '\uFEFFBÁO GIÁ V.AI STUDIO\n\n';
csv += 'STT,Tên SP,SL,Đơn giá,Thành tiền\n';
window.cart.forEach(function(c, i) { var p = window.D[c.idx] || {}; var line = (c.priceNum || 0) * (c.qty || 1); total += line; csv += (i+1) + ',' + (p.name || c.name) + ',' + (c.qty || 1) + ',' + (c.price || '') + ',' + line + '\n'; });
csv += '\nTỔNG CỘNG: ' + total.toLocaleString('vi-VN') + 'đ\n';
var blob = new Blob([csv], {type:'text/csv;charset=utf-8'});
var url = URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = 'BaoGia.csv'; a.click();
};
window.exportPDF = function() {
if (!window.cart || !window.cart.length) { alert('Gio hang trong.'); return; }
if (typeof html2canvas !== 'undefined' && typeof jspdf !== 'undefined') {
var doc = new jspdf.jsPDF();
doc.text('BÁO GIÁ V.AI STUDIO', 105, 20, {align:'center'});
doc.autoTable({ head: [['STT','SP','SL','Tiền']], body: window.cart.map(function(c,i){ return [(i+1),(c.name||''),c.qty||1,c.price||0]; }), startY: 40 });
doc.save('BaoGia.pdf');
}
};
// Add to Order from product cards
window._vaiAddOrder = function(slug, btn) {
if (!slug) return;
var norm = function(s) { return String(s||'').normalize('NFD').replace(/[\u0300-\u036f]/g,'').replace(/[Đđ]/g,'d').toLowerCase(); };
var slugNorm = norm(slug), product = null;
if (typeof window.D !== 'undefined' && window.D.length) {
for (var i = 0; i < window.D.length; i++) {
if (window.D[i] && (norm(window.D[i].slug) === slugNorm || norm(window.D[i].sku) === slugNorm)) { product = window.D[i]; break; }
}
}
if (!product) { var t = document.createElement('div'); t.style.cssText = 'position:fixed;bottom:24px;right:24px;background:#dc2626;color:#fff;padding:10px;border-radius:8px;z-index:9999'; t.textContent = 'SP "' + slug + '" khong tim thay'; document.body.appendChild(t); setTimeout(function(){t.remove();},2500); return; }
if (window._showOrderPicker) { window._showOrderPicker(product, function(r) { if (r === 'ok') { btn.textContent = '✓ Da them'; btn.style.background = '#059669'; } setTimeout(function() { btn.textContent = '+ Don'; btn.style.background = '#003f62'; }, 2500); }); }
};
/** Save and create order functions */
window.saveQuote = function() {
if (!window.cart || !window.cart.length) { alert('Gio hang trong.'); return; }
var D = window.D || [];
var orderData = {
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 || ''
},
items: window.cart.map(function(c) { return { idx: c.idx, name: c.name, qty: c.qty, price: c.price, priceNum: c.priceNum }; }),
total: window.cart.reduce(function(s,c) { return s + (c.priceNum || 0) * (c.qty || 1); }, 0),
date: document.getElementById('qcDate').value || new Date().toISOString().split('T')[0]
};
localStorage.setItem('vai_quote_saved', JSON.stringify(orderData));
showToast('Da luu bao gia!');
};
window.createOrder = function() {
if (!window.cart || !window.cart.length) { alert('Gio hang trong.'); return; }
var name = document.getElementById('qcName').value;
var phone = document.getElementById('qcPhone').value;
var addr = document.getElementById('qcAddr').value;
if (!name || !phone || !addr) { alert('Vui long dien day du thong tin.'); return; }
showToast('Tao don hang thanh cong! Ma don: ' + (document.getElementById('qcCompany').value || 'N/A'));
window.cart = [];
localStorage.setItem('malloca_cart', JSON.stringify(window.cart));
if (typeof updateCartBadge === 'function') updateCartBadge();
closeQuotation();
};
</script><a href="https://bot.zaloplatforms.com/bots/4013442364902370344" target="_blank" rel="noopener" class="zalo-fab" title="Tư vấn qua Zalo"><img src="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/zalo-bot.png" alt="Zalo Bot"></a>
</body>
</html>
<!-- v1.0.1 -->