| <!DOCTYPE html> |
| <html lang="vi"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width,initial-scale=1"> |
| <title>V.AI STUDIO | Niềm tin khách hàng là tài sản của chúng tôi</title> |
| <meta name="description" content="V.AI STUDIO — 2134 sản phẩm thiết bị nhà bếp & khóa thông minh Malloca, Eurogold, Grob, Canzy & Demax chính hãng. Niềm tin khách hàng là tài sản của chúng tôi."> |
| <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)} |
| ::-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 */ |
| .gallery{position:relative} |
| .gallery-main{width:100%;aspect-ratio:1;border-radius:16px;overflow:hidden;background:#f8fafc;display:flex;align-items:center;justify-content:center;margin-bottom:12px} |
| .gallery-main img{max-width:90%;max-height:90%;object-fit:contain;transition:opacity .3s} |
| .gallery-thumbs{display:flex;gap:8px;overflow-x:auto;padding-bottom:4px} |
| .gallery-thumbs::-webkit-scrollbar{height:4px} |
| .gallery-thumb{width:72px;height:72px;border-radius:10px;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:4px} |
|
|
| /* 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} |
|
|
| /* 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:20px}.detail-name{font-size:1.2rem}.detail-price{font-size:1.4rem} |
| .dtab-h{padding:12px 18px;font-size:.8rem} |
| .footer-grid{grid-template-columns:1fr 1fr} |
| .gallery-thumb{width:56px;height:56px} |
| } |
|
|
| .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:40px;height:40px;object-fit:contain;border-radius:4px;background:var(--l)} |
| .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}} |
|
|
| </style> |
| |
| <script src="https://cdn.jsdelivr.net/npm/exceljs@4.4.0/dist/exceljs.min.js"></script> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script> |
| </head> |
| <body> |
|
|
| <div class="topbar"><div class="container"> |
| <div class="topbar-item"><i class="fas fa-truck-fast"></i> Miễn phí vận chuyển</div> |
| <div class="topbar-item"><i class="fas fa-shield-halved"></i> Bảo hành chính hãng</div> |
| <div class="topbar-item"><i class="fas fa-headset"></i> Hỗ trợ 24/7</div> |
| <div class="topbar-item"><i class="fas fa-rotate-left"></i> Đổi trả 30 ngày</div> |
| </div></div> |
|
|
| <header class="header"><div class="container"> |
| <div class="logo" onclick="goHome()"><img src="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/logo_200.png" alt="V.AI STUDIO" style="height:44px;object-fit:contain"><div><div class="logo-name">V.AI <span>STUDIO</span></div><div class="logo-sub">Niềm tin khách hàng là tài sản của chúng tôi</div></div></div> |
| <div class="search-box"><i class="fas fa-search"></i><input type="text" id="q" placeholder="Tìm sản phẩm Malloca, Eurogold, Grob, Canzy & Demax..." oninput="doSearch()"><span class="search-count" id="sc"></span></div> |
| <div class="nav-tabs"> |
| <div class="nav-tab active" data-page="products" onclick="showPage('products')"><i class="fas fa-th-large"></i> Sản Phẩm</div> |
| <div class="nav-tab" data-page="catalogue" onclick="showPage('catalogue')"><i class="fas fa-book-open"></i> Catalogue</div> |
| <div class="nav-tab" data-page="contact" onclick="showPage('contact')"><i class="fas fa-envelope"></i> Liên Hệ</div> |
| </div> |
| <div class="nav-icons"> |
| <a href="#" onclick="toggleChat();return false" title="Tư Vấn AI" style="background:linear-gradient(135deg,var(--p),#0077b6);color:#fff"><i class="fas fa-robot"></i></a> |
| <a href="#" onclick="openCart();return false" title="Giỏ hàng" class="cart-badge"><i class="fas fa-shopping-cart"></i><span class="cart-num" id="cartNum" style="display:none">0</span></a> |
| </div> |
| <button class="hamburger" onclick="document.getElementById('msb').classList.toggle('open')"><span></span><span></span><span></span></button> |
| </div></header> |
|
|
| <section class="hero"><div class="container"> |
| <img src="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/logo_600.png" alt="V.AI STUDIO" style="height:80px;object-fit:contain;margin-bottom:12px"> |
| <h1>V.AI <span>STUDIO</span></h1> |
| <p style="font-size:1rem;font-style:italic;color:rgba(255,255,255,.9);margin-bottom:6px">❝ Niềm tin khách hàng là tài sản của chúng tôi ❞</p> |
| <p>Thiết bị nhà bếp & khóa thông minh — Malloca, Eurogold, Grob, Canzy & Demax</p> |
| <div class="hero-stats" id="heroStats"> |
| <div class="hero-stat"><strong id="statTotal">1979</strong><span>Sản phẩm</span></div> |
| <div class="hero-stat"><strong id="statCats">17</strong><span>Danh mục</span></div> |
| <div class="hero-stat"><strong>3</strong><span>Thương hiệu</span></div> |
| <div class="hero-stat"><strong>100%</strong><span>Dữ liệu thật</span></div> |
| </div> |
| </div></section> |
|
|
| <div class="container"> |
| |
| <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> |
|
|
| |
| <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> |
| |
| <div id="catTabs"></div> |
| |
| <div id="catContent"></div> |
| </div> |
|
|
| |
| <div class="page-section" id="page-contact"> |
| <div class="section-header" style="text-align:center;margin-bottom:10px"> |
| <h2 style="font-size:1.8rem;font-weight:800;color:var(--d)"><i class="fas fa-envelope" style="color:var(--a);margin-right:10px"></i>Liên Hệ <span style="color:var(--a)">Malloca</span></h2> |
| <p style="color:var(--g);font-size:.9rem;max-width:500px;margin:10px auto 0">Liên hệ trực tiếp hoặc gửi tin nhắn — chúng tôi sẵn sàng hỗ trợ bạn</p> |
| </div> |
| <div class="contact-grid"> |
| <div class="contact-card"> |
| <h3><i class="fas fa-building"></i> Thông Tin Liên Hệ</h3> |
| <ul class="contact-info"> |
| <li><i class="fas fa-map-marker-alt"></i><div><strong>Showroom Hà Nội</strong><br>10 Chương Dương Độ, Hoàn Kiếm, Hà Nội</div></li> |
| <li><i class="fas fa-comment-dots"></i><div><strong>Zalo tư vấn</strong><br><a href="https://zalo.me/0981873395" target="_blank" style="color:var(--p);font-weight:700">0981 873 395</a> · <a href="https://zalo.me/0918258385" target="_blank" style="color:var(--p);font-weight:700">0918 258 385</a></div></li> |
| <li><i class="fas fa-qrcode"></i><div><strong>QR Kết bạn Zalo</strong><br><img src="https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=https://zalo.me/0981873395" alt="QR Zalo 0981873395" style="width:120px;border-radius:8px;margin-top:6px;border:2px solid var(--gl)"><br><span style="font-size:.72rem;color:var(--g)">Quét mã để kết bạn Zalo</span></div></li> |
| <li><i class="fas fa-envelope"></i><div><strong>Email</strong><br><a href="mailto:info@malloca.com" style="color:var(--p)">info@malloca.com</a></div></li> |
| <li><i class="fas fa-clock"></i><div><strong>Giờ làm việc</strong><br>Thứ 2 - Thứ 7: 8:00 - 17:30<br>Chủ nhật: 9:00 - 12:00</div></li> |
| <li><i class="fas fa-robot"></i><div><strong>Tư Vấn AI</strong><br><a href="javascript:void(0)" onclick="toggleChat()" style="color:var(--p);font-weight:700">Chat ngay →</a></div></li> |
| </ul> |
| <div style="display:flex;gap:8px;margin-top:16px"> |
| <a href="https://zalo.me/0981873395" target="_blank" style="padding:10px 16px;background:#0068FF;color:#fff;border-radius:10px;font-size:.8rem;font-weight:600;display:flex;align-items:center;gap:6px"><i class="fas fa-comment-dots"></i> Zalo 1</a> |
| <a href="https://zalo.me/0918258385" target="_blank" style="padding:10px 16px;background:#0068FF;color:#fff;border-radius:10px;font-size:.8rem;font-weight:600;display:flex;align-items:center;gap:6px"><i class="fas fa-comment-dots"></i> Zalo 2</a> |
| </div> |
| </div> |
| <div class="contact-card"> |
| <h3><i class="fas fa-paper-plane"></i> Gửi Tin Nhắn</h3> |
| <form onsubmit="submitContact(event)"> |
| <div class="form-group"><label>Họ tên *</label><input class="form-input" id="ctName" placeholder="Nhập họ tên" required></div> |
| <div class="form-group"><label>Email *</label><input class="form-input" id="ctEmail" type="email" placeholder="email@example.com" required></div> |
| <div class="form-group"><label>Số điện thoại</label><input class="form-input" id="ctPhone" placeholder="Nhập SĐT"></div> |
| <div class="form-group"><label>Nội dung *</label><textarea class="form-input" id="ctMsg" placeholder="Nhập nội dung liên hệ..." required></textarea></div> |
| <button type="submit" class="form-btn"><i class="fas fa-paper-plane"></i> Gửi Tin Nhắn</button> |
| </form> |
| </div> |
| </div> |
| </div> |
|
|
| </div> |
|
|
| |
| <div class="msb" id="msb"> |
| <div class="msb-h"><img src="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/logo_200.png" alt="V.AI STUDIO" style="height:36px;object-fit:contain"><button class="msb-close" onclick="this.closest('.msb').classList.remove('open')"><i class="fas fa-times"></i></button></div> |
| <div style="display:flex;flex-direction:column;gap:4px;margin-bottom:16px;padding-bottom:16px;border-bottom:2px solid var(--gl)"> |
| <div style="padding:12px;border-radius:10px;font-weight:600;cursor:pointer;font-size:.9rem" onclick="closeMsb();showPage('products')"><i class="fas fa-th-large" style="margin-right:8px;color:var(--a)"></i> Sản Phẩm</div> |
| <div style="padding:12px;border-radius:10px;font-weight:600;cursor:pointer;font-size:.9rem" onclick="closeMsb();showPage('catalogue')"><i class="fas fa-book-open" style="margin-right:8px;color:var(--a)"></i> Catalogue</div> |
| <div style="padding:12px;border-radius:10px;font-weight:600;cursor:pointer;font-size:.9rem" onclick="closeMsb();showPage('contact')"><i class="fas fa-envelope" style="margin-right:8px;color:var(--a)"></i> Liên Hệ</div> |
| <div style="padding:12px;border-radius:10px;font-weight:600;cursor:pointer;font-size:.9rem" onclick="closeMsb();openCart()"><i class="fas fa-shopping-cart" style="margin-right:8px;color:var(--a)"></i> Giỏ Hàng</div> |
| </div> |
| <div class="sb-card"><h3><i class="fas fa-building"></i> Thương Hiệu</h3><div class="pr" id="brm"></div></div> |
| <div class="sb-card"><h3><i class="fas fa-layer-group"></i> Danh Mục</h3><ul class="cat-list" id="clm"></ul></div> |
| <div class="sb-card"><h3><i class="fas fa-tag"></i> Khoảng Giá</h3><div class="pr" id="prm"></div></div> |
| </div> |
|
|
|
|
| |
| <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">0đ</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> |
|
|
| |
| <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> |
|
|
| |
| <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>Công ty / Dự án</label><input id="qcCompany" placeholder="Nhập tên công ty (nếu có)"></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="Nhập yêu cầu chiết khấu... Ví dụ: Giảm 15% tất cả sản phẩm, hoặc: SP1 giảm 2 triệu, SP2 giảm 10%..." 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">0đ</td><td></td></tr></tfoot> |
| </table> |
| </div> |
| </div> |
|
|
| <div class="quote-actions"> |
| <button class="quote-btn quote-btn-excel" onclick="exportExcel()"><i class="fas fa-file-excel"></i> Xuất Excel</button> |
| <button class="quote-btn quote-btn-pdf" onclick="exportPDF()"><i class="fas fa-file-pdf"></i> Xuất PDF</button> |
| <button class="quote-btn quote-btn-print" onclick="printQuotation()"><i class="fas fa-print"></i> In báo giá</button> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <footer class="footer"><div class="container"> |
| <div class="footer-grid"> |
| <div class="footer-brand"><img src="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/logo_200.png" alt="V.AI STUDIO" style="height:50px;object-fit:contain;margin-bottom:8px;filter:brightness(10)"><div class="logo-name" style="color:#fff;font-size:1.1rem">V.AI <span style="color:var(--a)">STUDIO</span></div><p style="font-style:italic;opacity:.85;margin-bottom:4px">❝ Niềm tin khách hàng là tài sản của chúng tôi ❞</p><p>Thiết bị nhà bếp & khóa thông minh — Malloca, Eurogold, Grob, Canzy & Demax.</p> |
| <div class="social-links"><a href="https://zalo.me/0981873395" target="_blank" title="Zalo"><i class="fas fa-comment-dots"></i></a><a href="https://zalo.me/0918258385" target="_blank" title="Zalo 2"><i class="fas fa-comments"></i></a></div></div> |
| <div><h4>Thiết Bị Nhà Bếp</h4><ul class="footer-links"><li><a href="javascript:void(0)" onclick="setCat('may-hut-khoi-khu-mui')">Máy hút khói khử mùi</a></li><li><a href="javascript:void(0)" onclick="setCat('bep-dien-tu-bep-gas')">Bếp điện từ, bếp gas</a></li><li><a href="javascript:void(0)" onclick="setCat('lo-nuong')">Lò nướng, lò hấp</a></li><li><a href="javascript:void(0)" onclick="setCat('chau-rua-chen')">Chậu rửa chén</a></li><li><a href="javascript:void(0)" onclick="setCat('voi-rua-chen')">Vòi rửa chén</a></li></ul></div> |
| <div><h4>Thêm</h4><ul class="footer-links"><li><a href="javascript:void(0)" onclick="setCat('tu-lanh-tu-ruou')">Tủ lạnh, tủ rượu</a></li><li><a href="javascript:void(0)" onclick="setCat('may-rua-chen-may-say-chen')">Máy rửa chén</a></li><li><a href="javascript:void(0)" onclick="setCat('thiet-bi-gia-dung-nho')">Gia dụng nhỏ</a></li><li><a href="javascript:void(0)" onclick="showPage('catalogue')">Catalogue</a></li><li><a href="javascript:void(0)" onclick="toggleChat()">🤖 Tư Vấn AI</a></li></ul></div> |
| <div><h4>Hỗ Trợ</h4><ul class="footer-links"><li><a href="javascript:void(0)" onclick="toggleChat()">Tư vấn sản phẩm</a></li><li><a href="javascript:void(0)" onclick="showPage('contact')">Liên hệ</a></li><li><a href="https://zalo.me/0981873395" target="_blank">Zalo: 0981 873 395</a></li><li><a href="javascript:void(0)" onclick="showPage('catalogue')">Xem catalogue</a></li><li><a href="javascript:void(0)" onclick="goHome()">Trang chủ</a></li></ul></div> |
| </div> |
| <div class="footer-bottom"><p>© 2025 V.AI STUDIO • Thiết bị nhà bếp & khóa thông minh — Malloca, Eurogold, Grob, Canzy & Demax</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} |
| ]; |
| |
| fetch('products_with_slugs.json').then(r=>{if(!r.ok)throw new Error('HTTP '+r.status);return r.json()}).then(d=>{D=d.map(p=>{let o={name:p.n,link:p.l,image:p.i,price:p.p,priceNum:p.pn,cat:p.c,catSlug:p.cs,catIcon:p.ci,subCat:p.sub_c||'',subCatSlug:p.sub_cs||'',images:p.imgs||[],summary:p.sum||'',desc:p.desc||'',specs:p.specs||{},feats:p.feats||[],sku:p.sku||'',video:p.vid||'',model:p.mod||'',slug:p.slug||'',brand:p.brand||( p.n.includes('| Demax |')?'Demax':p.n.includes('| Canzy |')?'Canzy':p.n.includes('| Grob |')?'Grob':p.n.includes('| Eurogold |')||p.n.startsWith('Eurogold |')?'Eurogold':'Malloca')};o._idx=buildSearchIndex(o);return o});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){ |
| // 1. Exact substring (fastest path) |
| 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){ |
| if(text.includes(w)){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;} |
| } |
| // All words must match |
| return matched===words.length; |
| } |
| function buildSearchIndex(p){ |
| // Only index product-specific content, NOT generic category names |
| let parts=[p.name,p.sku,p.model,p.brand,p.price]; |
| 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 cats={};D.forEach(p=>{cats[p.catSlug]=1});document.getElementById('statCats').textContent=Object.keys(cats).length; |
| } |
| |
| |
| |
| function getF(){ |
| let r=D; |
| if(S.brand!=='all')r=r.filter(p=>p.brand===S.brand); |
| if(S.cat!=='all')r=r.filter(p=>p.catSlug===S.cat||p.subCatSlug===S.cat); |
| if(S.q){let q=nVN(S.q);r=r.filter(p=>smartSearch(q,p._idx))} |
| if(S.pmin>0||S.pmax<1e15)r=r.filter(p=>p.priceNum>=S.pmin&&p.priceNum<=S.pmax); |
| if(S.sort==='pa')r.sort((a,b)=>a.priceNum-b.priceNum); |
| else if(S.sort==='pd')r.sort((a,b)=>b.priceNum-a.priceNum); |
| else if(S.sort==='na')r.sort((a,b)=>a.name.localeCompare(b.name,'vi')); |
| return r; |
| } |
| |
| function render(){ |
| let f=getF(),tp=Math.ceil(f.length/PP);if(S.pg>tp)S.pg=tp||1; |
| let s=(S.pg-1)*PP,items=f.slice(s,s+PP); |
| document.getElementById('rc').innerHTML=`<strong>${f.length}</strong> sản phẩm`; |
| document.getElementById('sc').textContent=S.q?f.length+' kết quả':''; |
| let g=document.getElementById('grid'); |
| g.className='pg'+(S.view==='l'?' lv':''); |
| if(!items.length){g.innerHTML=`<div class="empty" style="grid-column:1/-1"><i class="fas fa-search"></i><h3>Không tìm thấy</h3><p>Thử thay đổi bộ lọc hoặc từ khóa</p><button class="reset-btn" onclick="resetAll()">Xóa bộ lọc</button></div>`; |
| }else{ |
| g.innerHTML=items.map((p,i)=>{let idx=D.indexOf(p);let brandColor=p.brand==='Eurogold'?'#c8102e':p.brand==='Grob'?'#2e7d32':'var(--p)';let href='/san-pham/'+p.slug+'/index.html';return`<a class="pc fade" href="${href}" onclick="event.preventDefault();showDetail(${idx})" style="transition-delay:${Math.min(i*25,400)}ms;display:block"><div class="pi"><img src="${p.image}" alt="${p.name}" loading="lazy" onerror="this.style.display='none'"><span class="pi-badge"><i class="fas ${p.catIcon}"></i> ${p.cat}</span><span style="position:absolute;top:8px;right:8px;background:${brandColor};color:#fff;padding:3px 8px;border-radius:5px;font-size:.58rem;font-weight:700">${p.brand}</span></div><div class="pb"><div style="font-size:.65rem;font-weight:700;color:${brandColor};letter-spacing:.5px;margin-bottom:4px">${p.model||p.brand}</div><div class="pn">${p.name}</div><div class="pf"><span class="pp">${p.price||'Liên hệ'}</span><i class="fas fa-chevron-right"></i></div></div></a>`}).join(''); |
| } |
| requestAnimationFrame(()=>document.querySelectorAll('.fade').forEach(e=>e.classList.add('vis'))); |
| renderPag(tp); |
| } |
| |
| function renderPag(tp){ |
| if(tp<=1){document.getElementById('pag').innerHTML='';return} |
| let h=`<button class="pgb" onclick="goPg(${S.pg-1})"${S.pg===1?' disabled':''}><i class="fas fa-chevron-left"></i></button>`; |
| let pages=[]; |
| if(tp<=7){for(let i=1;i<=tp;i++)pages.push(i)} |
| else{pages.push(1);if(S.pg>3)pages.push('.');for(let i=Math.max(2,S.pg-1);i<=Math.min(tp-1,S.pg+1);i++)pages.push(i);if(S.pg<tp-2)pages.push('.');pages.push(tp)} |
| pages.forEach(p=>{if(p==='.')h+=`<span class="pg-dots">...</span>`;else h+=`<button class="pgb${p===S.pg?' active':''}" onclick="goPg(${p})">${p}</button>`}); |
| h+=`<button class="pgb" onclick="goPg(${S.pg+1})"${S.pg===tp?' disabled':''}><i class="fas fa-chevron-right"></i></button>`; |
| document.getElementById('pag').innerHTML=h; |
| } |
| |
| // ===== DETAIL VIEW ===== |
| let _skipHistory=false; |
| function showDetail(idx){ |
| let p=D[idx]; |
| // Update URL with SEO-friendly slug using History API |
| if(!_skipHistory){ |
| let slugUrl='/san-pham/'+p.slug+'/index.html'; |
| history.pushState({type:'product',idx:idx},'',slugUrl); |
| } |
| _skipHistory=false; |
| document.title=p.name+' | '+p.brand+' - Malloca'; |
| document.getElementById('listView').style.display='none'; |
| document.querySelector('.hero').style.display='none'; |
| let dv=document.getElementById('detailView'); |
| dv.classList.add('show'); |
| |
| // Build gallery images |
| let allImgs=p.images.length?p.images:[p.image]; |
| let thumbs=allImgs.map((img,i)=>`<div class="gallery-thumb${i===0?' active':''}" onclick="switchImg(${i},'${img.replace(/'/g,"\\'")}')"><img src="${img}" alt="" loading="lazy"></div>`).join(''); |
| |
| // Specs table |
| let specsHtml=''; |
| if(Object.keys(p.specs).length){ |
| specsHtml='<table class="specs-table">'+Object.entries(p.specs).map(([k,v])=>`<tr><td>${k}</td><td>${v}</td></tr>`).join('')+'</table>'; |
| }else{specsHtml='<p style="color:var(--g);font-size:.88rem">Chưa có thông số kỹ thuật chi tiết.</p>'} |
| |
| // Features |
| let featsHtml=''; |
| if(p.feats.length){featsHtml='<ul class="feat-list">'+p.feats.map(f=>`<li class="feat-item"><i class="fas fa-check-circle"></i><span>${f}</span></li>`).join('')+'</ul>'} |
| else{featsHtml='<p style="color:var(--g);font-size:.88rem">Chưa có danh sách tính năng.</p>'} |
| |
| // Description |
| let descHtml=p.desc?`<div class="desc-text">${p.desc.replace(/\n/g,'<br>')}</div>`:'<p style="color:var(--g);font-size:.88rem">Chưa có mô tả chi tiết.</p>'; |
| |
| // Video |
| let videoHtml=p.video?`<div class="video-wrap"><iframe src="${p.video}" allowfullscreen loading="lazy"></iframe></div>`:'<p style="color:var(--g);font-size:.88rem">Chưa có video.</p>'; |
| |
| // Tab count for conditional display |
| let tabs=[ |
| {id:'specs',label:'Thông Số Kỹ Thuật',icon:'fa-list-check',content:specsHtml}, |
| {id:'feats',label:'Tính Năng',icon:'fa-star',content:featsHtml}, |
| {id:'desc',label:'Mô Tả Chi Tiết',icon:'fa-file-lines',content:descHtml}, |
| ]; |
| if(p.video)tabs.push({id:'video',label:'Video',icon:'fa-play',content:videoHtml}); |
| |
| let tabHeaders=tabs.map((t,i)=>`<div class="dtab-h${i===0?' active':''}" onclick="switchTab('${t.id}',this)"><i class="fas ${t.icon}"></i> ${t.label}</div>`).join(''); |
| let tabContents=tabs.map((t,i)=>`<div class="dtab-content${i===0?' active':''}" id="tab-${t.id}">${t.content}</div>`).join(''); |
| |
| // Related products (same category, max 4) |
| let related=D.filter(r=>r.catSlug===p.catSlug&&r!==p).slice(0,4); |
| let relatedHtml=''; |
| if(related.length){ |
| relatedHtml=`<div style="margin-top:30px"><h3 style="font-size:1.1rem;font-weight:700;margin-bottom:16px"><i class="fas fa-cubes" style="color:var(--a);margin-right:8px"></i>Sản phẩm liên quan</h3><div class="pg" style="grid-template-columns:repeat(auto-fill,minmax(200px,1fr))">`; |
| relatedHtml+=related.map(r=>{let ri=D.indexOf(r);return`<a class="pc" href="/san-pham/${r.slug}/index.html" onclick="event.preventDefault();showDetail(${ri});window.scrollTo({top:0,behavior:'smooth'})" style="display:block"><div class="pi" style="height:160px"><img src="${r.image}" alt="${r.name}" loading="lazy"></div><div class="pb"><div class="pn">${r.name}</div><div class="pp">${r.price||'Liên hệ'}</div></div></a>`}).join(''); |
| relatedHtml+='</div></div>'; |
| } |
| |
| dv.innerHTML=` |
| <div style="padding:28px 0"> |
| <div class="detail-back" onclick="goBack()"><i class="fas fa-arrow-left"></i> Quay lại danh sách</div> |
| <div class="detail-grid"> |
| <div class="gallery"> |
| <div class="gallery-main"><img id="mainImg" src="${allImgs[0]}" alt="${p.name}"></div> |
| ${allImgs.length>1?`<div class="gallery-thumbs">${thumbs}</div>`:''} |
| </div> |
| <div class="detail-info"> |
| ${p.sku?`<div class="detail-sku">SKU: ${p.sku} • 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í lắp đặt</div> |
| </div> |
| <div style="display:flex;gap:10px;flex-wrap:wrap"> |
| <button class="add-cart-btn" onclick="event.stopPropagation();addToCart(${idx})"><i class="fas fa-cart-plus"></i> Thêm vào giỏ</button> |
| <button class="detail-btn detail-btn-outline" onclick="event.stopPropagation();toggleChat()"><i class="fas fa-robot"></i> Tư Vấn AI</button> |
| <button class="detail-btn detail-btn-outline" onclick="event.stopPropagation();shareProduct('${p.slug}','${p.name.replace(/'/g,"\\'")}')" title="Chia sẻ sản phẩm"><i class="fas fa-share-alt"></i> Chia sẻ</button> |
| </div> |
| </div> |
| </div> |
| <div class="dtabs"> |
| <div class="dtab-headers">${tabHeaders}</div> |
| <div class="dtab-body">${tabContents}</div> |
| </div> |
| ${relatedHtml} |
| </div>`; |
| window.scrollTo({top:0,behavior:'smooth'}); |
| } |
| |
| function goBack(){ |
| document.getElementById('detailView').classList.remove('show'); |
| document.getElementById('detailView').innerHTML=''; |
| document.getElementById('listView').style.display=''; |
| document.querySelector('.hero').style.display=''; |
| document.title='V.AI STUDIO | Niềm tin khách hàng là tài sản của chúng tôi'; |
| history.pushState({type:'list'},'','/'); |
| } |
| |
| function goHome(){ |
| history.pushState({type:'list'},'','/'); |
| goBack();resetAll(); |
| } |
| |
| function switchImg(i,src){ |
| document.getElementById('mainImg').src=src; |
| document.querySelectorAll('.gallery-thumb').forEach((t,j)=>t.classList.toggle('active',j===i)); |
| } |
| |
| function switchTab(id,el){ |
| document.querySelectorAll('.dtab-h').forEach(h=>h.classList.remove('active')); |
| document.querySelectorAll('.dtab-content').forEach(c=>c.classList.remove('active')); |
| el.classList.add('active'); |
| document.getElementById('tab-'+id).classList.add('active'); |
| } |
| |
| // ===== ACTIONS ===== |
| function setCat(s){S.cat=s;S.pg=1;updActive('.cat-item','data-s',s);goBackToList();updateHash();render();closeMsb()} |
| function setPrice(min,max,i){S.pmin=min;S.pmax=max;S.pg=1;document.querySelectorAll('.pr-item[data-i]').forEach((e,j)=>e.classList.toggle('active',j===i));goBackToList();updateHash();render();closeMsb()} |
| function setBrand(b){S.brand=b;S.pg=1;document.querySelectorAll('[data-b]').forEach(e=>e.classList.toggle('active',e.getAttribute('data-b')===b));goBackToList();updateHash();render();closeMsb()} |
| function goBackToList(){document.getElementById('listView').style.display='';document.getElementById('detailView').classList.remove('show');document.getElementById('detailView').innerHTML='';document.querySelector('.hero').style.display='';} |
| function doSearch(){S.q=document.getElementById('q').value.trim();S.pg=1;updateHash();render();renderMainCatSearch()} |
| function doSort(){S.sort=document.getElementById('ss').value;S.pg=1;updateHash();render()} |
| function setV(v){S.view=v;document.getElementById('gb').classList.toggle('active',v==='g');document.getElementById('lb').classList.toggle('active',v==='l');updateHash();render()} |
| function goPg(p){let f=getF(),tp=Math.ceil(f.length/PP);if(p<1||p>tp)return;S.pg=p;updateHash();render();document.querySelector('.toolbar').scrollIntoView({behavior:'smooth',block:'start'})} |
| function resetAll(){S={cat:'all',q:'',pmin:0,pmax:1e15,sort:'default',pg:1,view:S.view,brand:'all'};document.getElementById('q').value='';document.getElementById('ss').value='default';updActive('.cat-item','data-s','all');document.querySelectorAll('.pr-item[data-i]').forEach((e,i)=>e.classList.toggle('active',i===0));document.querySelectorAll('[data-b]').forEach(e=>e.classList.toggle('active',e.getAttribute('data-b')==='all'));history.replaceState({type:'list'},'','/');render();let csr=document.getElementById('catSearchResults');if(csr)csr.innerHTML='';} |
| function updActive(sel,attr,val){document.querySelectorAll(sel).forEach(e=>e.classList.toggle('active',e.getAttribute(attr)===val))} |
| function closeMsb(){document.getElementById('msb').classList.remove('open')} |
| document.addEventListener('keydown',e=>{if(e.key==='Escape')goBack();if(e.key==='/'&&document.activeElement.tagName!=='INPUT'){e.preventDefault();document.getElementById('q').focus()}}); |
| |
| // ===== CATALOGUE (all pages rendered as images, loaded from catalogues.json) ===== |
| let CAT_LIST=[]; |
| let catLoaded=false; |
| let activeCat=0; |
| const CAT_PER_PAGE=12; |
| let catPage=1; |
| |
| function loadCatalogueData(){ |
| return Promise.all([ |
| fetch('catalogues.json').then(r=>r.json()), |
| fetch('catalogue_ocr.json').then(r=>r.json()).catch(()=>[]) |
| ]).then(([data,ocr])=>{ |
| CAT_LIST=data; |
| window._catOcr=ocr; |
| initCatTabs(); |
| loadCatalogue(0); |
| catLoaded=true; |
| }); |
| } |
| |
| function initCatTabs(){ |
| let tabs=document.getElementById('catTabs'); |
| if(!tabs||!CAT_LIST.length)return; |
| const icons={'bep':'fa-fire','may-hut':'fa-wind','lo':'fa-temperature-high','chau':'fa-sink','khac':'fa-box','gia':'fa-tags','master':'fa-layer-group','grob':'fa-cogs','canzy':'fa-kitchen-set','demax':'fa-lock'}; |
| tabs.innerHTML=CAT_LIST.map((c,i)=>{ |
| let slug=c.slug||''; |
| let icon='fa-book-open'; |
| for(let k in icons){if(slug.includes(k)){icon=icons[k];break;}} |
| let pCount=c.pages?c.pages.length:0; |
| return `<div class="ctab${i===0?' active':''}" onclick="loadCatalogue(${i})"><i class="fas ${icon}"></i>${c.name||'Catalogue '+(i+1)}<span class="cnt">${pCount}</span></div>`; |
| }).join(''); |
| } |
| |
| function loadCatalogue(idx){ |
| activeCat=idx; |
| let c=CAT_LIST[idx]; |
| if(!c)return; |
| // Update active tab |
| let tabBtns=document.querySelectorAll('#catTabs .ctab'); |
| tabBtns.forEach((b,i)=>b.classList.toggle('active',i===idx)); |
| let content=document.getElementById('catContent'); |
| if(!content)return; |
| let pages=c.pages||[]; |
| let totalPages=pages.length; |
| let pageHtml=`<div style="text-align:center;margin-bottom:16px"> |
| <div style="font-size:.85rem;color:var(--g)">${c.desc||c.name||''} — <strong>${totalPages}</strong> trang</div> |
| </div>`; |
| pageHtml+=`<div style="display:flex;flex-direction:column;gap:16px;align-items:center">`; |
| pageHtml+=pages.map((pg,i)=>{ |
| // pg can be a URL string or an object with .file |
| let imgSrc=typeof pg==='string'?pg:(pg.file?'grob_img/'+pg.file:pg.image||pg.url||''); |
| let label='Trang '+(i+1); |
| return `<div style="width:100%;max-width:900px"><img src="${imgSrc}" alt="${label}" style="width:100%;border-radius:12px;box-shadow:0 2px 12px rgba(0,0,0,.1)" loading="lazy"><div style="text-align:center;font-size:.75rem;color:var(--g);margin-top:6px">${label}</div></div>`; |
| }).join(''); |
| pageHtml+=`</div>`; |
| content.innerHTML=pageHtml; |
| window.scrollTo({top:0,behavior:'smooth'}); |
| } |
| |
| function searchCatalogue(){ |
| let q=document.getElementById('catSearch').value.trim(); |
| if(!q){loadCatalogue(activeCat);document.getElementById('catSearchCount').textContent='';return} |
| let nq=nVN(q); |
| let results=[]; |
| CAT_LIST.forEach((c,ci)=>{ |
| let ocrTexts=(window._catOcr&&window._catOcr[ci])?window._catOcr[ci].texts||[]:[]; |
| (c.pages||[]).forEach((pg,pi)=>{ |
| let ocrText=ocrTexts[pi]||''; |
| let text=nVN(ocrText+' '+(c.name||'')); |
| if(smartSearch(nq,text)){ |
| let imgSrc=typeof pg==='string'?pg:(pg.file?'grob_img/'+pg.file:''); |
| results.push({cat:c.name||'Catalogue',imgSrc:imgSrc,pageNum:pi+1}); |
| } |
| }); |
| }); |
| document.getElementById('catSearchCount').textContent=results.length+' kết quả'; |
| let content=document.getElementById('catContent'); |
| if(!results.length){content.innerHTML='<div class="empty" style="padding:40px"><i class="fas fa-search"></i><h3>Không tìm thấy trong catalogue</h3></div>';return} |
| content.innerHTML=`<div style="display:flex;flex-direction:column;gap:16px;align-items:center">`+results.map(r=>`<div style="width:100%;max-width:900px"><div style="font-size:.8rem;color:var(--p);font-weight:700;margin-bottom:6px">${r.cat} — Trang ${r.pageNum}</div><img src="${r.imgSrc}" alt="Trang ${r.pageNum}" style="width:100%;border-radius:12px;box-shadow:0 2px 12px rgba(0,0,0,.1)" loading="lazy"></div>`).join('')+`</div>`; |
| } |
| |
| function renderMainCatSearch(){ |
| let q=document.getElementById('q').value.trim(); |
| let csr=document.getElementById('catSearchResults'); |
| if(!csr)return; |
| if(!q){csr.innerHTML='';return} |
| let nq=nVN(q); |
| let results=[]; |
| CAT_LIST.forEach((c,ci)=>{ |
| let ocrTexts=(window._catOcr&&window._catOcr[ci])?window._catOcr[ci].texts||[]:[]; |
| (c.pages||[]).forEach((pg,pi)=>{ |
| let ocrText=ocrTexts[pi]||''; |
| let text=nVN(ocrText+' '+(c.name||'')); |
| if(smartSearch(nq,text)){ |
| let imgSrc=typeof pg==='string'?pg:(pg.file?'grob_img/'+pg.file:''); |
| results.push({cat:c.name||'Catalogue',imgSrc:imgSrc,pageNum:pi+1}); |
| } |
| }); |
| }); |
| if(!results.length){csr.innerHTML='';return} |
| let searchVal=document.getElementById('q').value.replace(/'/g,"\\'"); |
| csr.innerHTML=`<div style="margin-top:24px;padding-top:24px;border-top:2px solid var(--gl)"><h3 style="font-size:1rem;font-weight:700;margin-bottom:12px"><i class="fas fa-book-open" style="color:var(--a);margin-right:8px"></i>Kết quả trong Catalogue (${results.length})</h3><div style="display:flex;flex-wrap:wrap;gap:12px">`+results.slice(0,6).map(r=>`<div style="width:200px;cursor:pointer" onclick="showPage('catalogue');document.getElementById('catSearch').value='${searchVal}';searchCatalogue();"><img src="${r.imgSrc}" style="width:100%;border-radius:8px;border:2px solid var(--gl)" loading="lazy"><div style="font-size:.7rem;color:var(--g);margin-top:4px">${r.cat} — Trang ${r.pageNum}</div></div>`).join('')+`</div></div>`; |
| } |
| |
| function showPage(page){ |
| document.querySelectorAll('.page-section').forEach(el=>el.classList.remove('active')); |
| document.querySelectorAll('.nav-tab').forEach(el=>el.classList.toggle('active',el.dataset.page===page)); |
| let el=document.getElementById('page-'+page); |
| if(el){el.classList.add('active');} |
| if(page==='catalogue'&&!catLoaded){loadCatalogueData();} |
| if(page==='catalogue'){history.pushState({type:'page',page:'catalogue'},'','/catalogue/index.html');} |
| else if(page==='contact'){history.pushState({type:'page',page:'contact'},'','/lien-he/index.html');} |
| else if(page==='products'){history.pushState({type:'list'},'','/');} |
| window.scrollTo({top:0,behavior:'smooth'}); |
| } |
| |
| function initCatalogue(){ |
| // catalogue loads on demand |
| } |
| |
| |
| // ===== CART ===== |
| let cart=JSON.parse(localStorage.getItem('malloca_cart')||'[]'); |
| |
| function updateCartBadge(){ |
| let n=cart.reduce((s,i)=>s+i.qty,0); |
| let badge=document.getElementById('cartNum'); |
| if(badge){badge.textContent=n;badge.style.display=n>0?'flex':'none'} |
| } |
| |
| function addToCart(idx){ |
| let p=D[idx]; |
| let exist=cart.find(c=>c.idx===idx); |
| if(exist){exist.qty++}else{cart.push({idx,name:p.name,price:p.price,priceNum:p.priceNum,image:p.image,qty:1})} |
| localStorage.setItem('malloca_cart',JSON.stringify(cart)); |
| updateCartBadge(); |
| // Show brief toast notification instead of opening cart |
| let toast=document.createElement('div'); |
| toast.style.cssText='position:fixed;bottom:24px;right:24px;background:var(--p);color:#fff;padding:12px 20px;border-radius:12px;font-size:.85rem;font-weight:600;z-index:999;display:flex;align-items:center;gap:8px;box-shadow:0 8px 24px rgba(0,0,0,.2);animation:fadeUp .3s ease'; |
| toast.innerHTML='<i class="fas fa-check-circle"></i> Đã thêm vào giỏ hàng'; |
| document.body.appendChild(toast); |
| setTimeout(()=>{toast.style.opacity='0';toast.style.transition='opacity .3s';setTimeout(()=>toast.remove(),300)},2000); |
| } |
| |
| function removeFromCart(i){ |
| cart.splice(i,1); |
| localStorage.setItem('malloca_cart',JSON.stringify(cart)); |
| updateCartBadge();renderCart(); |
| } |
| |
| function changeQty(i,delta){ |
| cart[i].qty+=delta; |
| if(cart[i].qty<1)cart.splice(i,1); |
| localStorage.setItem('malloca_cart',JSON.stringify(cart)); |
| updateCartBadge();renderCart(); |
| } |
| |
| function openCart(){ |
| document.getElementById('cartOverlay').classList.add('open'); |
| document.getElementById('cartDrawer').classList.add('open'); |
| document.body.style.overflow='hidden'; |
| renderCart(); |
| } |
| |
| function closeCart(){ |
| document.getElementById('cartOverlay').classList.remove('open'); |
| document.getElementById('cartDrawer').classList.remove('open'); |
| document.body.style.overflow=''; |
| } |
| |
| function renderCart(){ |
| let el=document.getElementById('cartItems'); |
| let footer=document.getElementById('cartFooter'); |
| if(!cart.length){ |
| el.innerHTML=`<div class="cart-empty"><i class="fas fa-shopping-bag"></i><h3>Giỏ hàng trống</h3><p style="font-size:.82rem">Thêm sản phẩm vào giỏ để đặt hàng</p></div>`; |
| footer.style.display='none'; |
| return; |
| } |
| footer.style.display='block'; |
| el.innerHTML=cart.map((c,i)=>`<div class="cart-item"><img src="${c.image}" alt="${c.name}"><div class="cart-item-info"><div class="cart-item-name">${c.name}</div><div class="cart-item-price">${c.price}</div><div class="cart-item-qty"><button class="qty-btn" onclick="changeQty(${i},-1)">−</button><span class="qty-val">${c.qty}</span><button class="qty-btn" onclick="changeQty(${i},1)">+</button></div></div><button class="cart-item-del" onclick="removeFromCart(${i})"><i class="fas fa-trash"></i></button></div>`).join(''); |
| let total=cart.reduce((s,c)=>s+c.priceNum*c.qty,0); |
| document.getElementById('cartTotal').textContent=total.toLocaleString('vi-VN')+'đ'; |
| } |
| |
| // ===== CHECKOUT ===== |
| function openCheckout(){ |
| closeCart(); |
| let overlay=document.getElementById('checkoutOverlay'); |
| overlay.classList.add('open'); |
| document.body.style.overflow='hidden'; |
| let total=cart.reduce((s,c)=>s+c.priceNum*c.qty,0); |
| let totalStr=total.toLocaleString('vi-VN')+'đ'; |
| // Build order summary |
| let itemsHtml=cart.map(c=>`<div class="checkout-summary-item"><span>${c.name} x${c.qty}</span><span style="font-weight:600">${(c.priceNum*c.qty).toLocaleString('vi-VN')}đ</span></div>`).join(''); |
| // Build QR URL (VietQR standard) |
| let qrUrl=`https://img.vietqr.io/image/VIB-918258385-compact2.jpg?amount=${total}&addInfo=${encodeURIComponent('Thanh toan don hang Malloca')}&accountName=${encodeURIComponent('TRAN QUOC VUONG')}`; |
| document.getElementById('checkoutContent').innerHTML=` |
| <h2 class="checkout-title"><i class="fas fa-qrcode" style="color:var(--a)"></i> Thanh Toán Đơn Hàng</h2> |
| <p class="checkout-sub">Quét mã QR để thanh toán qua ứng dụng ngân hàng</p> |
| <div class="checkout-summary">${itemsHtml}<div class="checkout-summary-item total"><span>Tổng cộng</span><span>${totalStr}</span></div></div> |
| <div class="form-group"><label>Họ tên người nhận *</label><input class="form-input" id="coName" placeholder="Nhập họ tên"></div> |
| <div class="form-group"><label>Số điện thoại *</label><input class="form-input" id="coPhone" placeholder="Nhập SĐT"></div> |
| <div class="form-group"><label>Địa chỉ giao hàng *</label><input class="form-input" id="coAddr" placeholder="Nhập địa chỉ đầy đủ"></div> |
| <div class="form-group"><label>Ghi chú</label><textarea class="form-input" id="coNote" placeholder="Ghi chú đơn hàng (nếu có)" style="height:70px"></textarea></div> |
| <div class="qr-section"> |
| <h3 style="font-size:1rem;font-weight:700;margin-bottom:14px"><i class="fas fa-university" style="color:var(--p);margin-right:6px"></i> Chuyển khoản ngân hàng</h3> |
| <img src="${qrUrl}" alt="QR Thanh toán" onerror="this.parentElement.innerHTML+='<p style=color:red>Không tải được QR. Vui lòng chuyển khoản thủ công.</p>'"> |
| <div class="qr-bank-info"> |
| <strong>Ngân hàng:</strong> VIB - Ngân hàng TMCP Quốc Tế Việt Nam<br> |
| <strong>Số TK:</strong> 918258385 <button class="copy-btn" onclick="copyText('918258385')"><i class="fas fa-copy"></i> Copy</button><br> |
| <strong>Chủ TK:</strong> TRẦN QUỐC VƯƠNG<br> |
| <strong>Số tiền:</strong> <span style="color:var(--p);font-weight:800">${totalStr}</span><br> |
| <strong>Nội dung CK:</strong> Thanh toan don hang Malloca <button class="copy-btn" onclick="copyText('Thanh toan don hang Malloca')"><i class="fas fa-copy"></i> Copy</button> |
| </div> |
| </div> |
| <button class="checkout-done-btn" onclick="confirmOrder()"><i class="fas fa-check-circle"></i> Xác nhận đã thanh toán</button> |
| <p class="checkout-note">Đơn hàng sẽ được xử lý sau khi xác nhận thanh toán thành công.<br>Zalo hỗ trợ: <a href="https://zalo.me/0981873395" target="_blank" style="color:var(--p)"><strong>0981 873 395</strong></a></p>`; |
| } |
| |
| function closeCheckout(){ |
| document.getElementById('checkoutOverlay').classList.remove('open'); |
| document.body.style.overflow=''; |
| } |
| |
| function copyText(text){ |
| navigator.clipboard.writeText(text).then(()=>{ |
| let btns=document.querySelectorAll('.copy-btn'); |
| btns.forEach(b=>{if(b.previousSibling&&b.previousSibling.textContent&&b.previousSibling.textContent.includes(text)){b.innerHTML='<i class="fas fa-check"></i> Đã copy';setTimeout(()=>{b.innerHTML='<i class="fas fa-copy"></i> Copy'},2000)}}); |
| }); |
| } |
| |
| function confirmOrder(){ |
| let name=document.getElementById('coName').value.trim(); |
| let phone=document.getElementById('coPhone').value.trim(); |
| let addr=document.getElementById('coAddr').value.trim(); |
| if(!name||!phone||!addr){alert('Vui lòng điền đầy đủ Họ tên, SĐT và Địa chỉ.');return} |
| // Clear cart |
| cart=[];localStorage.setItem('malloca_cart','[]');updateCartBadge(); |
| // Show success |
| document.getElementById('checkoutContent').innerHTML=` |
| <div style="text-align:center;padding:40px 0"> |
| <div style="width:80px;height:80px;background:rgba(40,167,69,.1);border-radius:50%;display:flex;align-items:center;justify-content:center;margin:0 auto 20px"><i class="fas fa-check" style="font-size:2rem;color:#28a745"></i></div> |
| <h2 style="font-size:1.4rem;font-weight:800;color:var(--d);margin-bottom:8px">Đặt hàng thành công!</h2> |
| <p style="color:var(--g);font-size:.9rem;margin-bottom:24px">Cảm ơn bạn đã đặt hàng. Chúng tôi sẽ liên hệ xác nhận trong thời gian sớm nhất.</p> |
| <div style="background:var(--l);border-radius:12px;padding:16px;text-align:left;font-size:.85rem;color:var(--d);line-height:1.8"> |
| <strong>Người nhận:</strong> ${name}<br> |
| <strong>SĐT:</strong> ${phone}<br> |
| <strong>Địa chỉ:</strong> ${addr} |
| </div> |
| <button class="checkout-done-btn" onclick="closeCheckout();showPage('products')" style="margin-top:20px"><i class="fas fa-home"></i> Về trang chủ</button> |
| </div>`; |
| } |
| |
| // Contact form |
| function submitContact(e){ |
| e.preventDefault(); |
| let form=e.target; |
| let name=form.querySelector('#ctName').value; |
| let email=form.querySelector('#ctEmail').value; |
| let msg=form.querySelector('#ctMsg').value; |
| if(!name||!email||!msg){alert('Vui lòng điền đầy đủ thông tin.');return} |
| form.innerHTML=`<div style="text-align:center;padding:30px"><i class="fas fa-check-circle" style="font-size:2.5rem;color:#28a745;margin-bottom:12px;display:block"></i><h3 style="font-size:1.1rem;font-weight:700;margin-bottom:8px">Gửi thành công!</h3><p style="color:var(--g);font-size:.88rem">Cảm ơn bạn đã liên hệ. Chúng tôi sẽ phản hồi sớm nhất có thể.</p></div>`; |
| } |
| |
| // Override goHome to also handle page nav |
| let _origGoHome=goHome; |
| goHome=function(){ |
| if(document.getElementById('detailView').classList.contains('show')){ |
| goBack(); |
| } |
| showPage('products');resetAll(); |
| } |
| |
| // ===== SEO URL ROUTING ===== |
| |
| // Parse current path on load (for SPA navigation within iframe) |
| function parseCurrentPath(){ |
| let path=location.pathname; |
| |
| // Product page: /san-pham/<slug>/ |
| let prodMatch=path.match(/^\/san-pham\/([^\/]+)/); |
| if(prodMatch){ |
| let slug=prodMatch[1]; |
| if(slug==='index.html')slug=null; // skip if just /san-pham/index.html |
| let idx=slug?D.findIndex(p=>p.slug===slug):-1; |
| if(idx>=0){showDetailNoHistory(idx);return true;} |
| } |
| |
| // Category page: /danh-muc/<slug>/ |
| let catMatch=path.match(/^\/danh-muc\/([^\/]+)/); |
| if(catMatch){ |
| let catSlug=catMatch[1]; |
| S.cat=catSlug; |
| updActive('.cat-item','data-s',catSlug); |
| document.querySelectorAll('[data-b]').forEach(e=>e.classList.toggle('active',e.getAttribute('data-b')==='all')); |
| render(); |
| return true; |
| } |
| |
| // Catalogue page |
| if(path.startsWith('/catalogue')){showPage('catalogue');return true;} |
| |
| // Contact page |
| if(path.startsWith('/lien-he')){showPage('contact');return true;} |
| |
| return false; |
| } |
| |
| // Also parse hash params (backward compatibility) |
| function parseUrlParams(){ |
| let hash=location.hash.replace(/^#/,''); |
| if(!hash)return; |
| let params=new URLSearchParams(hash); |
| |
| // Legacy hash page navigation |
| let pageParam=params.get('page'); |
| if(pageParam&&pageParam!=='products'){ |
| showPage(pageParam); |
| return; |
| } |
| |
| // Legacy product hash (supports both index and slug) |
| let productParam=params.get('product'); |
| if(productParam!==null){ |
| let idx=parseInt(productParam); |
| if(!isNaN(idx)&&idx>=0&&idx<D.length){showDetailNoHistory(idx);return;} |
| // Try slug match |
| let slugIdx=D.findIndex(p=>p.slug===productParam); |
| if(slugIdx>=0){showDetailNoHistory(slugIdx);return;} |
| } |
| |
| let q=params.get('q')||params.get('search')||''; |
| if(q){document.getElementById('q').value=q;S.q=q;S.pg=1;} |
| let cat=params.get('cat'); |
| if(cat){S.cat=cat;updActive('.cat-item','data-s',cat);} |
| let brand=params.get('brand'); |
| if(brand){S.brand=brand;document.querySelectorAll('[data-b]').forEach(e=>e.classList.toggle('active',e.getAttribute('data-b')===brand));} |
| let pgNum=params.get('pg'); |
| if(pgNum){S.pg=parseInt(pgNum)||1;} |
| let sort=params.get('sort'); |
| if(sort){S.sort=sort;document.getElementById('ss').value=sort;} |
| let p=params.get('p'); |
| if(p==='list'){S.view='l';document.getElementById('gb').classList.remove('active');document.getElementById('lb').classList.add('active');} |
| } |
| |
| // Update hash based on current list state (for filters) |
| function updateHash(){ |
| let parts=[]; |
| if(S.q)parts.push('q='+encodeURIComponent(S.q)); |
| if(S.cat!=='all')parts.push('cat='+encodeURIComponent(S.cat)); |
| if(S.brand!=='all')parts.push('brand='+encodeURIComponent(S.brand)); |
| if(S.pg>1)parts.push('pg='+S.pg); |
| if(S.sort!=='default')parts.push('sort='+encodeURIComponent(S.sort)); |
| if(S.view==='l')parts.push('p=list'); |
| let hash=parts.length?'#'+parts.join('&'):''; |
| history.replaceState({type:'list'},'','/'+hash); |
| } |
| |
| // Handle browser back/forward buttons |
| window.addEventListener('popstate',function(e){ |
| let state=e.state; |
| if(state&&state.type==='product'&&typeof state.idx==='number'){ |
| if(state.idx>=0&&state.idx<D.length){ |
| let dv=document.getElementById('detailView'); |
| if(!dv.classList.contains('show')){showDetailNoHistory(state.idx);} |
| } |
| }else{ |
| // Go back to list view |
| document.getElementById('detailView').classList.remove('show'); |
| document.getElementById('detailView').innerHTML=''; |
| document.getElementById('listView').style.display=''; |
| document.querySelector('.hero').style.display=''; |
| document.title='V.AI STUDIO | Niềm tin khách hàng là tài sản của chúng tôi'; |
| // Check if we need to show a specific page |
| let path=location.pathname; |
| if(path.startsWith('/catalogue')){showPage('catalogue');} |
| else if(path.startsWith('/lien-he')){showPage('contact');} |
| else{ |
| document.querySelectorAll('.page-section').forEach(el=>el.classList.remove('active')); |
| let prodPage=document.getElementById('page-products'); |
| if(prodPage)prodPage.classList.add('active'); |
| document.querySelectorAll('.nav-tab').forEach(el=>el.classList.toggle('active',el.dataset.page==='products')); |
| parseUrlParams(); |
| render(); |
| } |
| } |
| }); |
| |
| // showDetail without pushing history (for popstate) |
| function showDetailNoHistory(idx){ |
| _skipHistory=true; |
| showDetail(idx); |
| } |
| |
| // ===== SHARE (Web Share API + Popover fallback) ===== |
| let _shareData={title:'',text:'',url:''}; |
| |
| async function shareProduct(slug,name){ |
| let url=location.origin+'/san-pham/'+slug+'/index.html'; |
| let title=name||document.title; |
| let text=title+' - Malloca Thiết bị nhà bếp'; |
| // Try native share first |
| if(navigator.share){ |
| try{await navigator.share({title,text,url});return}catch(e){if(e.name==='AbortError')return} |
| } |
| // Fallback: show share popover |
| openSharePopover(url,title,text); |
| } |
| |
| function shareCurrentPage(){ |
| let url=location.origin+location.pathname; |
| let title=document.title; |
| let text=title; |
| if(navigator.share){ |
| navigator.share({title,text,url}).catch(()=>{}); |
| return; |
| } |
| openSharePopover(url,title,text); |
| } |
| |
| function openSharePopover(url,title,text){ |
| _shareData={title:title||'',text:text||'',url:url||location.href}; |
| document.getElementById('shareLinkInput').value=_shareData.url; |
| document.getElementById('shareOverlay').classList.add('open'); |
| } |
| function closeSharePopover(){document.getElementById('shareOverlay').classList.remove('open')} |
| |
| function shareVia(platform){ |
| let u=encodeURIComponent(_shareData.url); |
| let t=encodeURIComponent(_shareData.title); |
| let txt=encodeURIComponent(_shareData.text+' '+_shareData.url); |
| let win=null; |
| switch(platform){ |
| case'facebook':win=window.open('https://www.facebook.com/sharer/sharer.php?u='+u,'_blank','width=600,height=400');break; |
| case'messenger':win=window.open('https://www.facebook.com/dialog/send?link='+u+'&redirect_uri='+u,'_blank');break; |
| case'zalo':win=window.open('https://zalo.me/share?url='+u,'_blank');break; |
| case'twitter':win=window.open('https://twitter.com/intent/tweet?text='+t+'&url='+u,'_blank','width=600,height=400');break; |
| case'telegram':win=window.open('https://t.me/share/url?url='+u+'&text='+t,'_blank');break; |
| case'whatsapp':win=window.open('https://wa.me/?text='+txt,'_blank');break; |
| case'email':window.location.href='mailto:?subject='+t+'&body='+txt;break; |
| case'copy': |
| if(navigator.clipboard){navigator.clipboard.writeText(_shareData.url).then(()=>showToast('Đã sao chép link!'))} |
| else{let ta=document.createElement('textarea');ta.value=_shareData.url;ta.style.cssText='position:fixed;top:-9999px';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);showToast('Đã sao chép link!')} |
| break; |
| } |
| closeSharePopover(); |
| } |
| |
| function showToast(msg){ |
| let old=document.querySelector('.share-toast');if(old)old.remove(); |
| let t=document.createElement('div');t.className='share-toast'; |
| t.style.cssText='position:fixed;bottom:24px;right:24px;background:#28a745;color:#fff;padding:12px 20px;border-radius:12px;font-size:.85rem;font-weight:600;z-index:999;box-shadow:0 4px 12px rgba(0,0,0,.2)'; |
| t.innerHTML='<i class="fas fa-check-circle" style="margin-right:6px"></i>'+msg; |
| document.body.appendChild(t); |
| setTimeout(()=>{t.style.opacity='0';t.style.transition='opacity .3s';setTimeout(()=>t.remove(),300)},2500); |
| } |
| |
| // ===== AI CHATBOX ===== |
| let chatHistory=[]; |
| let chatBusy=false; |
| |
| function toggleChat(){ |
| let box=document.getElementById('chatbox'); |
| let ov=document.getElementById('chatOverlay2'); |
| if(box.classList.contains('open')){closeChat()} |
| else{box.classList.add('open');ov.classList.add('open');document.getElementById('chatInput').focus()} |
| } |
| function closeChat(){ |
| document.getElementById('chatbox').classList.remove('open'); |
| document.getElementById('chatOverlay2').classList.remove('open'); |
| } |
| |
| function sendSuggestion(el){ |
| document.getElementById('chatInput').value=el.textContent; |
| document.getElementById('chatSuggestions').style.display='none'; |
| sendChat(); |
| } |
| |
| function appendMsg(role,html){ |
| let body=document.getElementById('chatBody'); |
| let div=document.createElement('div'); |
| div.className='chat-msg '+role; |
| div.innerHTML=html; |
| body.appendChild(div); |
| body.scrollTop=body.scrollHeight; |
| return div; |
| } |
| |
| function showTyping(){ |
| let body=document.getElementById('chatBody'); |
| let div=document.createElement('div'); |
| div.className='chat-typing';div.id='typingIndicator'; |
| div.innerHTML='<span></span><span></span><span></span>'; |
| body.appendChild(div); |
| body.scrollTop=body.scrollHeight; |
| } |
| function hideTyping(){let el=document.getElementById('typingIndicator');if(el)el.remove()} |
| |
| // Search products locally |
| function searchProducts(query){ |
| if(!D||!D.length)return[]; |
| let q=nVN(query); |
| let words=q.split(/\s+/).filter(w=>w.length>1); |
| let scored=D.map((p,i)=>{ |
| let idx=p._idx; |
| let score=0; |
| words.forEach(w=>{ |
| if(idx.includes(w))score+=1; |
| if(p.name&&nVN(p.name).includes(w))score+=3; |
| if(p.cat&&nVN(p.cat).includes(w))score+=2; |
| }); |
| return{p,score}; |
| }).filter(x=>x.score>0).sort((a,b)=>b.score-a.score); |
| return scored.slice(0,8).map(x=>x.p); |
| } |
| |
| // Format products for context |
| function productsToContext(products){ |
| return products.map(p=>{ |
| let specs=''; |
| if(p.specs&&Object.keys(p.specs).length){specs=' | Thông số: '+Object.entries(p.specs).slice(0,5).map(([k,v])=>k+': '+v).join(', ')} |
| return '- '+p.name+' | Giá: '+(p.price||'Liên hệ')+' | Danh mục: '+p.cat+specs; |
| }).join('\n'); |
| } |
| |
| // Format bot response |
| function formatBotResponse(text,matchedProducts){ |
| // Strip any raw URLs or markdown links — keep text only |
| text=text.replace(/\[([^\]]+)\]\([^)]+\)/g,'$1'); |
| text=text.replace(/https?:\/\/[^\s<)]+/g,''); |
| text=text.replace(/\/san-pham\/[a-z0-9-]+\/index\.html/g,''); |
| // Bold |
| text=text.replace(/\*\*([^*]+)\*\*/g,'<strong>$1</strong>'); |
| // Line breaks |
| text=text.replace(/\n/g,'<br>'); |
| // Smart product cards: only show products MENTIONED in the AI response |
| if(matchedProducts&&matchedProducts.length>0){ |
| let mentionedProducts=filterMentionedProducts(text,matchedProducts); |
| if(mentionedProducts.length>0){ |
| let cards=mentionedProducts.slice(0,4).map(p=>{ |
| let pidx=D.findIndex(d=>d.slug===p.slug); |
| let cartBtn=pidx>=0&&p.priceNum>0?'<button onclick="event.stopPropagation();addToCart('+pidx+');this.innerHTML=\'✅ Đã thêm\';this.disabled=true" style="padding:4px 10px;background:var(--a);color:#fff;border:none;border-radius:6px;font-size:.68rem;font-weight:600;cursor:pointer;display:inline-flex;align-items:center;gap:4px;font-family:inherit"><i class="fas fa-cart-plus"></i> Thêm giỏ</button>':''; |
| return '<div style="display:flex;gap:10px;background:var(--l);border-radius:10px;padding:10px;margin-top:6px;border:1px solid var(--gl)">' |
| +'<a href="/san-pham/'+p.slug+'/index.html" style="flex-shrink:0;text-decoration:none"><img src="'+p.image+'" style="width:50px;height:50px;object-fit:contain;border-radius:6px;background:#fff" onerror="this.style.display=\'none\'"></a>' |
| +'<div style="flex:1;min-width:0"><a href="/san-pham/'+p.slug+'/index.html" style="text-decoration:none;color:inherit"><div style="font-size:.76rem;font-weight:600;color:var(--d);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden">'+p.name+'</div>' |
| +'<div style="font-size:.8rem;font-weight:800;color:var(--p);margin-top:2px">'+(p.price||'Liên hệ')+'</div></a>' |
| +'<div style="display:flex;gap:6px;margin-top:6px;flex-wrap:wrap">' |
| +cartBtn |
| +'<a href="/san-pham/'+p.slug+'/index.html" style="padding:4px 10px;background:var(--p);color:#fff;border-radius:6px;font-size:.68rem;font-weight:600;text-decoration:none;display:inline-flex;align-items:center;gap:4px"><i class="fas fa-eye"></i> Xem</a>' |
| +'</div></div></div>'; |
| }).join(''); |
| text+='<div style="margin-top:10px;padding-top:10px;border-top:1px solid var(--gl)"><div style="font-size:.68rem;font-weight:700;color:var(--g);margin-bottom:4px;text-transform:uppercase;letter-spacing:.5px">👉 Sản phẩm được đề cập</div>'+cards+'</div>'; |
| } |
| } |
| return text; |
| } |
| |
| // Filter products that are actually mentioned in the AI response |
| function filterMentionedProducts(responseText,products){ |
| if(!products||!products.length)return[]; |
| let text=responseText.toLowerCase().replace(/<[^>]*>/g,' '); |
| let textClean=text.replace(/[^a-z0-9]/g,''); |
| let mentioned=[]; |
| let scores=[]; |
| for(let p of products){ |
| let score=0; |
| // Check model code (strongest signal) |
| let model=(p.model||'').toLowerCase().replace(/[^a-z0-9]/g,''); |
| if(model.length>=4&&textClean.includes(model)){score+=10} |
| // Check significant name words (need >=3 matches for generic names) |
| let nameWords=p.name.toLowerCase().replace(/[|]/g,' ').split(/\s+/).filter(w=>w.length>3&&!['malloca','eurogold','grob','phụ','kiện'].includes(w)); |
| let matchCount=nameWords.filter(w=>text.includes(w)).length; |
| if(matchCount>=3){score+=matchCount} |
| if(score>0)scores.push({p,score}); |
| } |
| // Sort by score descending, take top matches |
| scores.sort((a,b)=>b.score-a.score); |
| return scores.slice(0,4).map(s=>s.p); |
| } |
| |
| async function sendChat(){ |
| if(chatBusy)return; |
| let input=document.getElementById('chatInput'); |
| let msg=input.value.trim(); |
| if(!msg)return; |
| input.value=''; |
| input.style.height='auto'; |
| document.getElementById('chatSuggestions').style.display='none'; |
| |
| // Show user message |
| appendMsg('user',msg.replace(/</g,'<').replace(/>/g,'>').replace(/\n/g,'<br>')); |
| |
| // Handle cart/quote/checkout commands |
| let msgLower=nVN(msg); |
| if(msgLower.match(/bao gia|lap bao gia|len bao gia|xuat bao gia|in bao gia/)){ |
| if(cart.length===0){ |
| appendMsg('bot','Giỏ hàng đang trống. Anh/chị hãy chọn sản phẩm trước rồi em lên báo giá nhé! 😊<br><br>Anh/chị cần tìm sản phẩm gì ạ?'); |
| chatBusy=false;return; |
| } |
| let total=cart.reduce((s,c)=>s+c.priceNum*c.qty,0); |
| let rows=cart.map((c,i)=>`<tr><td style="padding:8px;font-size:.78rem">${i+1}</td><td style="padding:8px;font-size:.78rem">${c.name}</td><td style="padding:8px;text-align:center;font-size:.78rem">${c.qty}</td><td style="padding:8px;text-align:right;font-size:.78rem;white-space:nowrap">${c.priceNum?c.priceNum.toLocaleString('vi-VN')+'đ':'Liên hệ'}</td><td style="padding:8px;text-align:right;font-size:.78rem;font-weight:700;white-space:nowrap">${(c.priceNum*c.qty).toLocaleString('vi-VN')}đ</td></tr>`).join(''); |
| let quoteHtml=`<div style="background:var(--l);border-radius:12px;padding:14px;margin-top:8px"> |
| <div style="font-weight:800;font-size:.9rem;color:var(--p);margin-bottom:10px">📋 BẢNG BÁO GIÁ</div> |
| <table style="width:100%;border-collapse:collapse;border:1px solid var(--gl);border-radius:8px;overflow:hidden"> |
| <thead><tr style="background:var(--p);color:#fff"><th style="padding:8px;font-size:.72rem;text-align:left">STT</th><th style="padding:8px;font-size:.72rem;text-align:left">Sản phẩm</th><th style="padding:8px;font-size:.72rem;text-align:center">SL</th><th style="padding:8px;font-size:.72rem;text-align:right">Đơn giá</th><th style="padding:8px;font-size:.72rem;text-align:right">Thành tiền</th></tr></thead> |
| <tbody>${rows}</tbody> |
| <tfoot><tr style="background:rgba(0,63,98,.05)"><td colspan="4" style="padding:10px;font-weight:800;font-size:.85rem;text-align:right">TỔNG CỘNG:</td><td style="padding:10px;font-weight:900;font-size:.95rem;color:var(--p);text-align:right;white-space:nowrap">${total.toLocaleString('vi-VN')}đ</td></tr></tfoot> |
| </table> |
| <div style="display:flex;gap:8px;margin-top:10px;flex-wrap:wrap"> |
| <button onclick="viewQuoteInline(this)" style="padding:8px 14px;background:#fff;color:var(--p);border:2px solid var(--p);border-radius:8px;font-size:.78rem;font-weight:600;cursor:pointer;font-family:inherit;display:flex;align-items:center;gap:6px"><i class="fas fa-eye"></i> Xem chi tiết</button> |
| <button onclick="openQuotation()" style="padding:8px 14px;background:var(--p);color:#fff;border:none;border-radius:8px;font-size:.78rem;font-weight:600;cursor:pointer;font-family:inherit;display:flex;align-items:center;gap:6px"><i class="fas fa-file-pdf"></i> PDF</button> |
| <button onclick="exportQuoteExcel()" style="padding:8px 14px;background:#217346;color:#fff;border:none;border-radius:8px;font-size:.78rem;font-weight:600;cursor:pointer;font-family:inherit;display:flex;align-items:center;gap:6px"><i class="fas fa-file-excel"></i> Excel</button> |
| <button onclick="openCheckout()" style="padding:8px 14px;background:var(--a);color:#fff;border:none;border-radius:8px;font-size:.78rem;font-weight:600;cursor:pointer;font-family:inherit;display:flex;align-items:center;gap:6px"><i class="fas fa-qrcode"></i> Thanh toán</button> |
| </div> |
| </div>`; |
| appendMsg('bot','Dạ, em lên bảng báo giá cho anh/chị ngay ạ! 📋'+quoteHtml); |
| chatBusy=false;return; |
| } |
| |
| if(msgLower.match(/thanh toan|chuyen khoan|qr|quyet toan/)){ |
| if(cart.length===0){ |
| appendMsg('bot','Giỏ hàng đang trống ạ. Anh/chị chọn sản phẩm trước rồi em hỗ trợ thanh toán nhé! 😊'); |
| chatBusy=false;return; |
| } |
| openCheckout();closeChat(); |
| chatBusy=false;return; |
| } |
| |
| if(msgLower.match(/^gio hang$|^xem gio$|^xem gio hang$/)){ |
| if(cart.length===0){ |
| appendMsg('bot','Giỏ hàng đang trống ạ. Anh/chị muốn tìm sản phẩm gì để em tư vấn? 😊'); |
| }else{ |
| let items=cart.map((c,i)=>`${i+1}. **${c.name}** x${c.qty} — ${c.priceNum?c.priceNum.toLocaleString('vi-VN')+'đ':'Liên hệ'}`).join('<br>'); |
| let total=cart.reduce((s,c)=>s+c.priceNum*c.qty,0); |
| appendMsg('bot','🛒 Giỏ hàng hiện tại:<br>'+items+'<br><br><strong>Tổng: '+total.toLocaleString('vi-VN')+'đ</strong><br><br>Anh/chị có muốn em <strong>lên báo giá</strong> hoặc <strong>thanh toán</strong> không ạ?'); |
| } |
| chatBusy=false;return; |
| } |
| |
| // Search related products |
| let matchedProducts=searchProducts(msg); |
| let productContext=matchedProducts.length |
| ? matchedProducts.map((p,i)=>{ |
| let specs=''; |
| if(p.specs&&Object.keys(p.specs).length){ |
| specs=Object.entries(p.specs).slice(0,8).map(([k,v])=>k+': '+v).join('; '); |
| } |
| let features=(p.feats&&p.feats.length)?p.feats.slice(0,4).join('; '):''; |
| return `${i+1}. ${p.name} |
| Giá: ${p.price||'Liên hệ'} | ${p.cat} | ${p.brand} |
| ${specs?'Thông số: '+specs:''} |
| ${features?'Tính năng: '+features:''} |
| ${p.summary?'Mô tả: '+p.summary.substring(0,200):''}`; |
| }).join('\n\n') |
| : ''; |
| |
| // Prepare messages for AI |
| chatHistory.push({role:'user',content:msg}); |
| |
| let systemPrompt=`Bạn là V.AI STUDIO — chuyên gia tư vấn thiết bị nhà bếp & khóa thông minh, với nhiều năm kinh nghiệm. |
| |
| CÁCH TRẢ LỜI: |
| - Nói chuyện tự nhiên: xưng "em", gọi khách "anh/chị" |
| - PHÂN TÍCH nhu cầu cụ thể trước (diện tích bếp, số người, ngân sách) |
| - Giải thích TẠI SAO sản phẩm phù hợp — đừng chỉ liệt kê |
| - KHÔNG chèn link URL. KHÔNG dùng markdown link |
| - Dùng **in đậm** cho tên SP, emoji vừa phải |
| - Kết thúc bằng câu hỏi mở |
| |
| KHẢ NĂNG ĐẶC BIỆT — hướng dẫn khách dùng: |
| - Nếu khách muốn mua, hướng dẫn: "Anh/chị gõ **'thêm giỏ'** hoặc bấm nút 🛒 bên cạnh sản phẩm" |
| - Nếu khách muốn báo giá, hướng dẫn: "Anh/chị gõ **'báo giá'** để em lên bảng báo giá chi tiết" |
| - Nếu khách muốn thanh toán, hướng dẫn: "Anh/chị gõ **'thanh toán'** để chuyển khoản qua QR" |
| - Nếu khách muốn xem giỏ hàng: "Anh/chị gõ **'giỏ hàng'** để xem" |
| |
| ${cart.length>0?'GIỎHÀNG HIỆN TẠI ('+cart.length+' SP):\\n'+cart.map(c=>c.name+' x'+c.qty+' — '+(c.priceNum?c.priceNum.toLocaleString('vi-VN')+'đ':'Liên hệ')).join('\\n')+'\\nTổng: '+cart.reduce((s,c)=>s+c.priceNum*c.qty,0).toLocaleString('vi-VN')+'đ':'Giỏ hàng đang trống.'} |
| |
| ${productContext ? 'DỮ LIỆU SẢN PHẨM:\\n'+productContext : 'Không có SP khớp — tư vấn bằng kiến thức chuyên môn.'} |
| |
| THÔNG TIN: |
| - 2256 sản phẩm, 5 thương hiệu: Malloca, Eurogold, Grob, Canzy, Demax |
| - Malloca: thiết bị bếp cao cấp châu Âu |
| - Eurogold: phụ kiện tủ bếp inox 304 |
| - Grob: phụ kiện nhập khẩu |
| - Canzy: bếp từ, máy hút mùi |
| - Demax: khóa cửa thông minh Made in Malaysia |
| - Zalo tư vấn: 0981 873 395 (zalo.me/0981873395) hoặc 0918 258 385 (zalo.me/0918258385)`; |
| |
| let messages=[{role:'system',content:systemPrompt}]; |
| // Keep last 8 messages for context |
| let recent=chatHistory.slice(-8); |
| messages.push(...recent); |
| |
| chatBusy=true; |
| document.getElementById('chatSend').disabled=true; |
| showTyping(); |
| |
| try{ |
| // Auto-detect token from any variable name |
| let vars=window.huggingface?.variables||{}; |
| let token=vars.HF_TOKEN||vars.VAISTUDIO||vars.AI_TOKEN||vars.TOKEN||''; |
| // Filter out non-token values |
| if(token&&!token.startsWith('hf_'))token=''; |
| |
| if(!token){ |
| hideTyping(); |
| // Smart local response without AI |
| let reply=''; |
| if(matchedProducts.length>0){ |
| reply='Tôi tìm thấy <strong>'+matchedProducts.length+' sản phẩm</strong> phù hợp với yêu cầu của bạn:'; |
| }else{ |
| reply='Tôi chưa tìm thấy sản phẩm cụ thể. Bạn có thể thử:<br>• Gõ tên sản phẩm (ví dụ: "bếp từ", "máy hút mùi")<br>• Gõ danh mục (ví dụ: "lò nướng", "chậu rửa")<br>• Gõ mã sản phẩm hoặc thương hiệu<br><br>💬 Hoặc nhắn Zalo <a href="https://zalo.me/0981873395" target="_blank" style="color:var(--p);font-weight:700">0981 873 395</a> để được tư vấn trực tiếp!'; |
| } |
| appendMsg('bot',formatBotResponse(reply,matchedProducts)); |
| chatHistory.push({role:'assistant',content:reply}); |
| chatBusy=false;document.getElementById('chatSend').disabled=false; |
| return; |
| } |
| |
| let resp=await fetch('https://router.huggingface.co/v1/chat/completions',{ |
| method:'POST', |
| headers:{'Authorization':'Bearer '+token,'Content-Type':'application/json'}, |
| body:JSON.stringify({ |
| model:'meta-llama/Llama-3.3-70B-Instruct', |
| messages:messages, |
| max_tokens:500, |
| temperature:0.7, |
| top_p:0.9, |
| stream:true |
| }) |
| }); |
| |
| hideTyping(); |
| |
| if(!resp.ok){ |
| let err=await resp.text(); |
| console.error('Chat API error:',resp.status,err); |
| appendMsg('bot',formatBotResponse('Xin lỗi, hệ thống AI đang bận. Đây là sản phẩm tôi tìm được cho anh/chị:',matchedProducts)); |
| chatBusy=false;document.getElementById('chatSend').disabled=false; |
| return; |
| } |
| |
| // Stream response — typewriter effect |
| let fullReply=''; |
| let botDiv=appendMsg('bot','<span class="typing-cursor">▊</span>'); |
| let reader=resp.body.getReader(); |
| let decoder=new TextDecoder(); |
| let buffer=''; |
| |
| while(true){ |
| let {done,value}=await reader.read(); |
| if(done)break; |
| buffer+=decoder.decode(value,{stream:true}); |
| let lines=buffer.split('\n'); |
| buffer=lines.pop(); |
| for(let line of lines){ |
| line=line.trim(); |
| if(!line.startsWith('data: '))continue; |
| let data=line.slice(6); |
| if(data==='[DONE]')break; |
| try{ |
| let j=JSON.parse(data); |
| let delta=j.choices?.[0]?.delta?.content; |
| if(delta){ |
| fullReply+=delta; |
| botDiv.innerHTML=formatBotResponse(fullReply,null)+'<span class="typing-cursor" style="display:inline;animation:blink .8s step-end infinite;color:var(--p)">▊</span>'; |
| document.getElementById('chatBody').scrollTop=document.getElementById('chatBody').scrollHeight; |
| } |
| }catch(e){} |
| } |
| } |
| // Remove cursor, add product cards |
| botDiv.innerHTML=formatBotResponse(fullReply,matchedProducts); |
| chatHistory.push({role:'assistant',content:fullReply}); |
| document.getElementById('chatBody').scrollTop=document.getElementById('chatBody').scrollHeight; |
| |
| }catch(e){ |
| hideTyping(); |
| console.error('Chat error:',e); |
| appendMsg('bot',formatBotResponse('Xin lỗi, đã có lỗi xảy ra. Đây là sản phẩm tôi tìm được cho anh/chị:',matchedProducts)); |
| } |
| |
| chatBusy=false; |
| document.getElementById('chatSend').disabled=false; |
| } |
| |
| // Legacy compatibility |
| function copyProductLink(slug){shareProduct(slug)} |
| function copyCurrentLink(){shareCurrentPage()} |
| function fallbackCopy(text){ |
| if(navigator.clipboard){navigator.clipboard.writeText(text).then(()=>showToast('Đã sao chép!'))} |
| else{let ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;top:-9999px';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);showToast('Đã sao chép!')} |
| } |
| |
| // ===== INLINE QUOTE VIEW + EXCEL EXPORT ===== |
| function viewQuoteInline(btn){ |
| let container=btn.closest('div').parentElement; |
| let existing=container.querySelector('.quote-inline-detail'); |
| if(existing){existing.remove();return;} |
| let today=new Date().toLocaleDateString('vi-VN'); |
| let total=cart.reduce((s,c)=>s+c.priceNum*c.qty,0); |
| let rows=cart.map((c,i)=>{ |
| let unit=c.priceNum?c.priceNum.toLocaleString('vi-VN')+'đ':'Liên hệ'; |
| let sub=(c.priceNum*c.qty).toLocaleString('vi-VN')+'đ'; |
| return `<tr style="border-bottom:1px solid var(--gl)"> |
| <td style="padding:10px 8px;font-size:.8rem;text-align:center;color:var(--g)">${i+1}</td> |
| <td style="padding:10px 8px"><div style="font-size:.8rem;font-weight:600">${c.name}</div></td> |
| <td style="padding:10px 8px;text-align:center;font-size:.8rem">${c.qty}</td> |
| <td style="padding:10px 8px;text-align:right;font-size:.8rem;white-space:nowrap">${unit}</td> |
| <td style="padding:10px 8px;text-align:right;font-size:.85rem;font-weight:700;color:var(--p);white-space:nowrap">${sub}</td> |
| </tr>`;}).join(''); |
| |
| let html=`<div class="quote-inline-detail" style="margin-top:12px;background:#fff;border-radius:12px;border:2px solid var(--p);overflow:hidden"> |
| <div style="background:linear-gradient(135deg,var(--p),#0077b6);color:#fff;padding:16px 18px"> |
| <div style="display:flex;align-items:center;gap:10px;margin-bottom:6px"> |
| <img src="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/logo_200.png" style="height:30px;filter:brightness(10)" onerror="this.style.display='none'"> |
| <div style="font-weight:800;font-size:1rem">V.AI STUDIO</div> |
| </div> |
| <div style="font-size:.72rem;opacity:.8">Niềm tin khách hàng là tài sản của chúng tôi</div> |
| </div> |
| <div style="padding:16px 18px"> |
| <div style="display:flex;justify-content:space-between;margin-bottom:12px"> |
| <div><div style="font-size:.7rem;color:var(--g);text-transform:uppercase;letter-spacing:1px">Báo giá</div><div style="font-size:1.1rem;font-weight:800;color:var(--d)">BG-${Date.now().toString(36).toUpperCase()}</div></div> |
| <div style="text-align:right"><div style="font-size:.72rem;color:var(--g)">Ngày lập</div><div style="font-size:.85rem;font-weight:600">${today}</div></div> |
| </div> |
| <table style="width:100%;border-collapse:collapse"> |
| <thead><tr style="background:var(--l)"> |
| <th style="padding:8px;font-size:.7rem;text-align:center;color:var(--g);font-weight:700">STT</th> |
| <th style="padding:8px;font-size:.7rem;text-align:left;color:var(--g);font-weight:700">SẢN PHẨM</th> |
| <th style="padding:8px;font-size:.7rem;text-align:center;color:var(--g);font-weight:700">SL</th> |
| <th style="padding:8px;font-size:.7rem;text-align:right;color:var(--g);font-weight:700">ĐƠN GIÁ</th> |
| <th style="padding:8px;font-size:.7rem;text-align:right;color:var(--g);font-weight:700">THÀNH TIỀN</th> |
| </tr></thead> |
| <tbody>${rows}</tbody> |
| </table> |
| <div style="display:flex;justify-content:flex-end;padding:12px 8px;background:var(--l);border-radius:8px;margin-top:8px"> |
| <div style="text-align:right"><div style="font-size:.72rem;color:var(--g)">TỔNG CỘNG</div><div style="font-size:1.2rem;font-weight:900;color:var(--p)">${total.toLocaleString('vi-VN')}đ</div></div> |
| </div> |
| <div style="margin-top:12px;padding-top:12px;border-top:1px dashed var(--gl);font-size:.72rem;color:var(--g);line-height:1.6"> |
| 💬 Zalo: <a href="https://zalo.me/0981873395" target="_blank" style="color:var(--p);font-weight:600">0981 873 395</a> · <a href="https://zalo.me/0918258385" target="_blank" style="color:var(--p);font-weight:600">0918 258 385</a><br> |
| 💡 Giá trên đã bao gồm VAT · Miễn phí vận chuyển & lắp đặt |
| </div> |
| </div> |
| </div>`; |
| container.insertAdjacentHTML('beforeend',html); |
| document.getElementById('chatBody').scrollTop=document.getElementById('chatBody').scrollHeight; |
| } |
| |
| function exportQuoteExcel(){ |
| if(!cart.length)return; |
| let today=new Date().toLocaleDateString('vi-VN'); |
| let total=cart.reduce((s,c)=>s+c.priceNum*c.qty,0); |
| // Build CSV with BOM for Excel UTF-8 |
| let csv='\uFEFF'; |
| csv+='BÁO GIÁ V.AI STUDIO\n'; |
| csv+='Ngày: '+today+'\n\n'; |
| csv+='STT,Mã SP,Sản phẩm,SL,Đơn giá,Thành tiền\n'; |
| cart.forEach((c,i)=>{ |
| let name=c.name.replace(/,/g,' -'); |
| let p=D[c.idx]; |
| let sku=p?p.sku||p.model:''; |
| csv+=(i+1)+','+sku+','+name+','+c.qty+','+(c.priceNum||0)+','+(c.priceNum*c.qty)+'\n'; |
| }); |
| csv+=',,,,,\n'; |
| csv+=',,,,TỔNG CỘNG,'+total+'\n'; |
| csv+='\n'; |
| csv+='Zalo: 0981 873 395 | 0918 258 385\n'; |
| csv+='Niềm tin khách hàng là tài sản của chúng tôi\n'; |
| let blob=new Blob([csv],{type:'text/csv;charset=utf-8'}); |
| let url=URL.createObjectURL(blob); |
| let a=document.createElement('a'); |
| a.href=url;a.download='BaoGia_VAI_STUDIO_'+today.replace(/\//g,'-')+'.csv'; |
| a.click();URL.revokeObjectURL(url); |
| showToast('Đã tải báo giá Excel!'); |
| } |
| |
| // ===== QUOTATION SYSTEM ===== |
| function openQuotation(){ |
| closeCart(); |
| let overlay=document.getElementById('quoteOverlay'); |
| overlay.classList.add('open'); |
| document.body.style.overflow='hidden'; |
| // Set default date |
| document.getElementById('qcDate').value=new Date().toISOString().split('T')[0]; |
| // Build product table |
| renderQuoteTable(); |
| } |
| |
| 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; |
| status.textContent='🔓 Đã mở'; |
| status.style.color='#28a745'; |
| icon.className='fas fa-lock-open';icon.style.color='#28a745'; |
| promptBox.style.display='block'; |
| 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 prompt=document.getElementById('qcAiPrompt').value.trim(); |
| if(!prompt)return; |
| let btn=document.getElementById('aiDiscBtn'); |
| let statusEl=document.getElementById('aiDiscStatus'); |
| btn.disabled=true;btn.innerHTML='<i class="fas fa-spinner fa-spin"></i> Đang xử lý...'; |
| statusEl.textContent=''; |
| |
| // Build product list for AI |
| let productList=cart.map((c,i)=>{ |
| let product=D[c.idx]||{}; |
| return `SP${i+1}: ${c.name} | Mã: ${product.model||''} | Đơn giá: ${c.priceNum}`; |
| }).join('\n'); |
| |
| let systemMsg=`Bạn là hệ thống tính chiết khấu. Người dùng sẽ yêu cầu giảm giá cho danh sách sản phẩm. |
| |
| DANH SÁCH SẢN PHẨM HIỆN TẠI: |
| ${productList} |
| |
| NHIỆM VỤ: Trả về JSON array giá chiết khấu cho TỪNG sản phẩm. Format CHÍNH XÁC: |
| [số1, số2, số3, ...] |
| Trong đó mỗi số là giá chiết khấu (VNĐ, số nguyên) cho từng sản phẩm theo thứ tự. |
| CHỈ trả về JSON array, KHÔNG giải thích gì thêm. |
| |
| Ví dụ: nếu có 3 SP giá 10000000, 5000000, 8000000 và user yêu cầu "giảm 10%": |
| [9000000, 4500000, 7200000]`; |
| |
| try{ |
| let vars=window.huggingface?.variables||{}; |
| let token=vars.HF_TOKEN||vars.VAISTUDIO||''; |
| if(token&&!token.startsWith('hf_'))token=''; |
| if(!token){statusEl.textContent='⚠️ Chưa có API token';btn.disabled=false;btn.innerHTML='<i class="fas fa-robot"></i> Áp dụng';return} |
| |
| let resp=await fetch('https://router.huggingface.co/v1/chat/completions',{ |
| method:'POST', |
| headers:{'Authorization':'Bearer '+token,'Content-Type':'application/json'}, |
| body:JSON.stringify({model:'meta-llama/Llama-3.3-70B-Instruct',messages:[{role:'system',content:systemMsg},{role:'user',content:prompt}],max_tokens:200,temperature:0.1}) |
| }); |
| let data=await resp.json(); |
| let reply=data.choices[0].message.content.trim(); |
| // Parse JSON array from reply |
| let match=reply.match(/\[[\d,\s]+\]/); |
| if(match){ |
| let prices=JSON.parse(match[0]); |
| if(prices.length===cart.length){ |
| prices.forEach((p,i)=>{ |
| let el=document.querySelector(`.qt-disc[data-idx="${i}"]`); |
| if(el){el.value=Math.round(p).toLocaleString('vi-VN');el.disabled=false;el.style.background='#fff';el.style.cursor='text'} |
| }); |
| updateQuoteTotal(); |
| statusEl.innerHTML='✅ Đã áp dụng chiết khấu cho '+prices.length+' sản phẩm'; |
| statusEl.style.color='#28a745'; |
| }else{statusEl.textContent='⚠️ Số SP không khớp ('+prices.length+' vs '+cart.length+')';statusEl.style.color='#dc3545'} |
| }else{statusEl.textContent='⚠️ AI: '+reply.substring(0,80);statusEl.style.color='#dc3545'} |
| }catch(e){statusEl.textContent='❌ Lỗi: '+e.message;statusEl.style.color='#dc3545'} |
| btn.disabled=false;btn.innerHTML='<i class="fas fa-robot"></i> Áp dụng'; |
| } |
| |
| 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}" alt="" crossorigin="anonymous" onerror="this.style.display='none'"></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:c.priceNum; |
| let lineTotal=discVal*c.qty; |
| let cell=document.querySelector(`.qt-linetotal[data-idx="${i}"]`); |
| if(cell)cell.textContent=lineTotal>0?lineTotal.toLocaleString('vi-VN')+'đ':''; |
| total+=lineTotal; |
| }); |
| document.getElementById('quoteTotalCell').textContent=total.toLocaleString('vi-VN')+'đ'; |
| } |
| |
| function getQuoteData(){ |
| let customer={ |
| name:document.getElementById('qcName').value||'', |
| phone:document.getElementById('qcPhone').value||'', |
| email:document.getElementById('qcEmail').value||'', |
| addr:document.getElementById('qcAddr').value||'', |
| company:document.getElementById('qcCompany').value||'', |
| date:document.getElementById('qcDate').value||new Date().toISOString().split('T')[0] |
| }; |
| let items=cart.map((c,i)=>{ |
| let product=D[c.idx]||{}; |
| let discInput=document.querySelector(`.qt-disc[data-idx="${i}"]`); |
| let noteInput=document.querySelector(`.qt-note[data-idx="${i}"]`); |
| let discPrice=discInput?parseInt(discInput.value.replace(/\D/g,''))||0:c.priceNum; |
| return{ |
| stt:i+1, name:c.name, model:product.model||product.sku||'', |
| specs:product.specs?Object.entries(product.specs).slice(0,3).map(([k,v])=>k+': '+v).join(', '):'', |
| image:c.image, qty:c.qty, price:c.priceNum, discPrice:discPrice, |
| total:discPrice*c.qty, note:noteInput?noteInput.value:'' |
| }; |
| }); |
| let grandTotal=items.reduce((s,it)=>s+it.total,0); |
| return{customer,items,grandTotal}; |
| } |
| |
| function fmtVND(n){return n>0?n.toLocaleString('vi-VN')+'đ':'Liên hệ'} |
| |
| // ===== EXCEL EXPORT WITH FORMULAS ===== |
| async function exportExcel(){ |
| let qd=getQuoteData(); |
| let wb=new ExcelJS.Workbook(); |
| wb.creator='Malloca Vietnam'; |
| let ws=wb.addWorksheet('Báo Giá'); |
| ws.columns=[ |
| {key:'stt',width:6},{key:'name',width:32},{key:'model',width:16},{key:'specs',width:28}, |
| {key:'image',width:14},{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ÁO GIÁ SẢN PHẨM'; |
| 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],['Công ty:',qd.customer.company],['Ngày:',qd.customer.date]]; |
| ci.forEach(pair=>{ |
| ws.getCell('A'+r).value=pair[0];ws.getCell('A'+r).font={bold:true,size:10}; |
| ws.getCell('B'+r).value=pair[1];ws.getCell('B'+r).font={size:10}; |
| r++; |
| }); |
| r++; |
| let headerR=r; |
| |
| // Table header |
| let headers=['STT','Tên sản phẩm','Mã SP','Thông tin SP','Hình ảnh','SL','Đơn giá','Đơn giá CK','Thành tiền','Ghi chú']; |
| let headerRow=ws.getRow(r); |
| headers.forEach((h,i)=>{headerRow.getCell(i+1).value=h}); |
| headerRow.height=26; |
| headerRow.eachCell(cell=>{ |
| cell.font={bold:true,color:{argb:'FFFFFFFF'},name:'Arial',size:9}; |
| cell.fill={type:'pattern',pattern:'solid',fgColor:{argb:'FF003F62'}}; |
| cell.border={top:{style:'thin'},left:{style:'thin'},bottom:{style:'thin'},right:{style:'thin'}}; |
| cell.alignment={horizontal:'center',vertical:'middle',wrapText:true}; |
| }); |
| r++; |
| let dataStartR=r; |
| |
| // Data rows with FORMULAS |
| // Detect if AI applied a percentage discount |
| let discountPct=0; |
| if(qd.items.length>0&&qd.items[0].price>0){ |
| let ratio=qd.items[0].discPrice/qd.items[0].price; |
| let allSameRatio=qd.items.every(it=>it.price===0||Math.abs(it.discPrice/it.price-ratio)<0.005); |
| if(allSameRatio&&ratio<1&&ratio>0)discountPct=Math.round(ratio*100); |
| } |
| |
| qd.items.forEach((item,idx)=>{ |
| let row=ws.getRow(r); |
| row.height=36; |
| // A: STT |
| let cA=row.getCell(1);cA.value=item.stt; |
| cA.alignment={horizontal:'center',vertical:'middle'}; |
| cA.font={size:10}; |
| // B: Tên SP |
| let cB=row.getCell(2);cB.value=item.name; |
| cB.alignment={vertical:'middle',wrapText:true}; |
| cB.font={size:10,bold:true,color:{argb:'FF1A1A1A'}}; |
| // C: Mã SP |
| let cC=row.getCell(3);cC.value=item.model; |
| cC.alignment={horizontal:'center',vertical:'middle'}; |
| cC.font={size:9,color:{argb:'FF003F62'}}; |
| // D: Thông tin |
| let cD=row.getCell(4);cD.value=item.specs; |
| cD.alignment={vertical:'middle',wrapText:true}; |
| cD.font={size:8,color:{argb:'FF888888'}}; |
| // E: Hình ảnh |
| row.getCell(5).value=''; |
| row.getCell(5).alignment={horizontal:'center',vertical:'middle'}; |
| // F: SL |
| let cF=row.getCell(6);cF.value=item.qty; |
| cF.alignment={horizontal:'center',vertical:'middle'}; |
| cF.font={size:10,bold:true}; |
| // G: Đơn giá |
| let cG=row.getCell(7);cG.value=item.price; |
| cG.numFmt='#,##0'; |
| cG.alignment={horizontal:'right',vertical:'middle'}; |
| cG.font={size:10}; |
| // H: Đơn giá CK = FORMULA if %, else value |
| let cH=row.getCell(8); |
| if(discountPct>0&&discountPct<100){ |
| cH.value={formula:`G${r}*${discountPct}%`}; |
| }else{ |
| cH.value=item.discPrice; |
| } |
| cH.numFmt='#,##0'; |
| cH.alignment={horizontal:'right',vertical:'middle'}; |
| if(item.discPrice<item.price){cH.font={size:10,color:{argb:'FFDC3545'},bold:true}}else{cH.font={size:10}} |
| // I: Thành tiền = H × F |
| let cI=row.getCell(9); |
| cI.value={formula:`H${r}*F${r}`}; |
| cI.numFmt='#,##0'; |
| cI.alignment={horizontal:'right',vertical:'middle'}; |
| cI.font={size:10,bold:true,color:{argb:'FF003F62'}}; |
| // J: Ghi chú |
| let cJ=row.getCell(10);cJ.value=item.note; |
| cJ.alignment={vertical:'middle',wrapText:true}; |
| cJ.font={size:9}; |
| // Borders + alternate colors |
| for(let c=1;c<=10;c++){ |
| row.getCell(c).border={top:{style:'thin',color:{argb:'FFD0D5DD'}},left:{style:'thin',color:{argb:'FFD0D5DD'}},bottom:{style:'thin',color:{argb:'FFD0D5DD'}},right:{style:'thin',color:{argb:'FFD0D5DD'}}}; |
| } |
| if(item.stt%2===0){for(let c=1;c<=10;c++){row.getCell(c).fill={type:'pattern',pattern:'solid',fgColor:{argb:'FFF8FAFC'}}}} |
| r++; |
| }); |
| let dataEndR=r-1; |
| |
| // ═══ SUMMARY SECTION ═══ |
| let sumStyle={border:{top:{style:'thin',color:{argb:'FFD0D5DD'}},bottom:{style:'thin',color:{argb:'FFD0D5DD'}}}}; |
| |
| // TỔNG CỘNG |
| let tr=r; |
| ws.mergeCells(tr,1,tr,8); |
| ws.getCell('A'+tr).value='TỔNG CỘNG:'; |
| ws.getCell('A'+tr).font={bold:true,size:11,color:{argb:'FF003F62'}}; |
| ws.getCell('A'+tr).alignment={horizontal:'right',vertical:'middle'}; |
| ws.getCell('I'+tr).value={formula:`SUM(I${dataStartR}:I${dataEndR})`}; |
| ws.getCell('I'+tr).numFmt='#,##0'; |
| ws.getCell('I'+tr).font={bold:true,size:12,color:{argb:'FF003F62'}}; |
| ws.getCell('I'+tr).alignment={horizontal:'right',vertical:'middle'}; |
| ws.getRow(tr).height=28; |
| for(let c=1;c<=10;c++){ws.getRow(tr).getCell(c).border={top:{style:'medium',color:{argb:'FF003F62'}},bottom:{style:'thin',color:{argb:'FFD0D5DD'}}};ws.getRow(tr).getCell(c).fill={type:'pattern',pattern:'solid',fgColor:{argb:'FFE8F0FE'}}} |
| r=tr+1; |
| |
| // ĐẶT CỌC (30%) |
| ws.mergeCells(r,1,r,8); |
| ws.getCell('A'+r).value='Đặt cọc (30%):'; |
| ws.getCell('A'+r).font={size:10,color:{argb:'FF555555'}}; |
| ws.getCell('A'+r).alignment={horizontal:'right',vertical:'middle'}; |
| ws.getCell('I'+r).value={formula:`ROUND(I${tr}*30%,0)`}; |
| ws.getCell('I'+r).numFmt='#,##0'; |
| ws.getCell('I'+r).font={bold:true,size:10,color:{argb:'FFDB9815'}}; |
| ws.getCell('I'+r).alignment={horizontal:'right',vertical:'middle'}; |
| ws.getRow(r).height=22; |
| r++; |
| |
| // CÒN LẠI |
| ws.mergeCells(r,1,r,8); |
| ws.getCell('A'+r).value='Còn lại:'; |
| ws.getCell('A'+r).font={size:10,color:{argb:'FF555555'}}; |
| ws.getCell('A'+r).alignment={horizontal:'right',vertical:'middle'}; |
| ws.getCell('I'+r).value={formula:`I${tr}-I${r-1}`}; |
| ws.getCell('I'+r).numFmt='#,##0'; |
| ws.getCell('I'+r).font={size:10}; |
| ws.getCell('I'+r).alignment={horizontal:'right',vertical:'middle'}; |
| ws.getRow(r).height=22; |
| r++; |
| |
| // PHỤ PHÍ |
| ws.mergeCells(r,1,r,8); |
| ws.getCell('A'+r).value='Phụ phí (nếu có):'; |
| ws.getCell('A'+r).font={size:10,color:{argb:'FF555555'}}; |
| ws.getCell('A'+r).alignment={horizontal:'right',vertical:'middle'}; |
| ws.getCell('I'+r).value=0; |
| ws.getCell('I'+r).numFmt='#,##0'; |
| ws.getCell('I'+r).font={size:10,italic:true}; |
| ws.getCell('I'+r).alignment={horizontal:'right',vertical:'middle'}; |
| ws.getCell('I'+r).fill={type:'pattern',pattern:'solid',fgColor:{argb:'FFFFFFEE'}}; |
| let surchargeR=r; |
| ws.getRow(r).height=22; |
| r++; |
| |
| // TỔNG THANH TOÁN |
| ws.mergeCells(r,1,r,8); |
| ws.getCell('A'+r).value='TỔNG THANH TOÁN:'; |
| ws.getCell('A'+r).font={bold:true,size:12,color:{argb:'FFFFFFFF'}}; |
| ws.getCell('A'+r).alignment={horizontal:'right',vertical:'middle'}; |
| ws.getCell('A'+r).fill={type:'pattern',pattern:'solid',fgColor:{argb:'FF003F62'}}; |
| ws.getCell('I'+r).value={formula:`I${tr}+I${surchargeR}`}; |
| ws.getCell('I'+r).numFmt='#,##0'; |
| ws.getCell('I'+r).font={bold:true,size:13,color:{argb:'FFFFFFFF'}}; |
| ws.getCell('I'+r).alignment={horizontal:'right',vertical:'middle'}; |
| ws.getCell('I'+r).fill={type:'pattern',pattern:'solid',fgColor:{argb:'FF003F62'}}; |
| for(let c=1;c<=10;c++){ws.getRow(r).getCell(c).fill={type:'pattern',pattern:'solid',fgColor:{argb:'FF003F62'}}} |
| ws.getRow(r).height=30; |
| r+=2; |
| |
| // Footer notes |
| ws.mergeCells(r,1,r,10); |
| ws.getCell('A'+r).value='📌 Báo giá có hiệu lực 30 ngày kể từ ngày lập. Giá đã bao gồm VAT.'; |
| ws.getCell('A'+r).font={italic:true,size:9,color:{argb:'FF888888'}}; |
| ws.getCell('A'+r).alignment={wrapText:true}; |
| r++; |
| ws.mergeCells(r,1,r,10); |
| ws.getCell('A'+r).value='💬 Zalo: 0981 873 395 | 0918 258 385 | ✅ Miễn phí vận chuyển & lắp đặt'; |
| ws.getCell('A'+r).font={italic:true,size:9,color:{argb:'FF888888'}}; |
| ws.getCell('A'+r).alignment={wrapText:true}; |
| |
| // Save |
| let buf=await wb.xlsx.writeBuffer(); |
| let blob=new Blob([buf],{type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'}); |
| let url=URL.createObjectURL(blob); |
| let a=document.createElement('a');a.href=url;a.download='Bao-gia-Malloca-'+(qd.customer.name||'KH').replace(/\s+/g,'-')+'.xlsx'; |
| a.click();URL.revokeObjectURL(url); |
| showToast('Đã xuất file Excel!'); |
| } |
| |
| // ===== PDF EXPORT (html2canvas for Vietnamese) ===== |
| async function exportPDF(){ |
| let qd=getQuoteData(); |
| // Build a hidden HTML table for rendering |
| let div=document.createElement('div'); |
| div.id='pdfRender'; |
| div.style.cssText='position:fixed;top:-9999px;left:0;width:1100px;background:#fff;padding:40px;font-family:Inter,Arial,sans-serif'; |
| let itemsHtml=qd.items.map(it=>`<tr style="border-bottom:1px solid #e2e8f0"> |
| <td style="padding:10px 6px;text-align:center;font-size:13px">${it.stt}</td> |
| <td style="padding:10px 6px"><img src="${it.image}" style="width:45px;height:45px;object-fit:contain;border-radius:4px" crossorigin="anonymous" onerror="this.style.display='none'"></td> |
| <td style="padding:10px 6px;font-size:12px;font-weight:600">${it.name}</td> |
| <td style="padding:10px 6px;text-align:center;font-size:12px">${it.model}</td> |
| <td style="padding:10px 6px;font-size:11px;color:#64748b;max-width:180px">${it.specs}</td> |
| <td style="padding:10px 6px;text-align:center;font-size:13px">${it.qty}</td> |
| <td style="padding:10px 6px;text-align:right;font-size:12px">${fmtVND(it.price)}</td> |
| <td style="padding:10px 6px;text-align:right;font-size:12px;${it.discPrice<it.price?'color:#dc3545;font-weight:700':''}">${fmtVND(it.discPrice)}</td> |
| <td style="padding:10px 6px;text-align:right;font-size:13px;font-weight:700">${fmtVND(it.total)}</td> |
| <td style="padding:10px 6px;font-size:11px">${it.note}</td> |
| </tr>`).join(''); |
| |
| div.innerHTML=` |
| <div style="text-align:center;margin-bottom:20px"> |
| <img src="https://huggingface.co/spaces/bep40/V.AISTUDIO/resolve/main/logo/logo_200.png" alt="V.AI STUDIO" style="height:40px;object-fit:contain"> |
| <div style="font-size:11px;color:#64748b;letter-spacing:3px">SMART KITCHEN • SMART LIFE</div> |
| <div style="font-size:20px;font-weight:800;color:#db9815;margin-top:10px">BÁO GIÁ SẢN PHẨM</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>Công ty:</strong> ${qd.customer.company}<br><strong>Ngày:</strong> ${qd.customer.date}<br><strong>Zalo:</strong> <a href="https://zalo.me/0981873395" target="_blank">0981 873 395</a></div> |
| </div> |
| <table style="width:100%;border-collapse:collapse"> |
| <thead><tr style="background:#003f62;color:#fff"> |
| <th style="padding:10px 6px;font-size:11px">STT</th><th style="padding:10px 6px;font-size:11px">Hình ảnh</th><th style="padding:10px 6px;font-size:11px;text-align:left">Tên sản phẩm</th><th style="padding:10px 6px;font-size:11px">Mã SP</th><th style="padding:10px 6px;font-size:11px">Thông tin SP</th><th style="padding:10px 6px;font-size:11px">SL</th><th style="padding:10px 6px;font-size:11px">Đơn giá</th><th style="padding:10px 6px;font-size:11px">Đơn giá CK</th><th style="padding:10px 6px;font-size:11px">Thành tiền</th><th style="padding:10px 6px;font-size:11px">Ghi chú</th> |
| </tr></thead> |
| <tbody>${itemsHtml}</tbody> |
| <tfoot><tr style="border-top:3px solid #003f62"> |
| <td colspan="8" style="padding:12px 6px;text-align:right;font-size:14px;font-weight:800;color:#003f62">TỔNG CỘNG:</td> |
| <td style="padding:12px 6px;text-align:right;font-size:15px;font-weight:900;color:#003f62">${fmtVND(qd.grandTotal)}</td><td></td> |
| </tr></tfoot> |
| </table> |
| <div style="margin-top:20px;font-size:11px;color:#64748b;font-style:italic"> |
| Báo giá có hiệu lực 30 ngày kể từ ngày lập. | Miễn phí vận chuyển & lắp đặt | Bảo hành chính hãng. |
| </div>`; |
| |
| document.body.appendChild(div); |
| // Wait for images and fonts |
| await new Promise(r=>setTimeout(r,500)); |
| |
| try{ |
| let canvas=await html2canvas(div,{scale:2,useCORS:true,allowTaint:true,logging:false}); |
| let imgData=canvas.toDataURL('image/jpeg',0.95); |
| let {jsPDF}=window.jspdf; |
| let pdf=new jsPDF({orientation:'landscape',unit:'mm',format:'a4'}); |
| let pdfW=pdf.internal.pageSize.getWidth(); |
| let pdfH=pdf.internal.pageSize.getHeight(); |
| let imgW=canvas.width;let imgH=canvas.height; |
| let ratio=pdfW/imgW*2;// scale:2 correction |
| let totalH=imgH*ratio/2; |
| let pageH=pdfH-10;let y=5;let srcY=0; |
| let sliceH=pageH/ratio*2; |
| // Multi-page support |
| while(srcY<imgH){ |
| if(srcY>0)pdf.addPage(); |
| let clipCanvas=document.createElement('canvas'); |
| clipCanvas.width=imgW;clipCanvas.height=Math.min(sliceH,imgH-srcY); |
| clipCanvas.getContext('2d').drawImage(canvas,0,srcY,imgW,clipCanvas.height,0,0,imgW,clipCanvas.height); |
| let sliceData=clipCanvas.toDataURL('image/jpeg',0.95); |
| let sliceRatio=pdfW/imgW; |
| pdf.addImage(sliceData,'JPEG',0,y,pdfW,clipCanvas.height*sliceRatio); |
| srcY+=sliceH; |
| } |
| pdf.save('Bao-gia-Malloca-'+(qd.customer.name||'KH').replace(/\s+/g,'-')+'.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();} |
| } |
| |
| document.addEventListener('DOMContentLoaded',()=>{ |
| initCatalogue(); |
| updateCartBadge(); |
| // Auto-open chat if ?chat=1 |
| if(location.search.includes('chat=1')){setTimeout(()=>toggleChat(),500)} |
| }); |
| |
| </script> |
|
|
| |
| <a href="https://zalo.me/0981873395" target="_blank" title="Chat Zalo" style="position:fixed;bottom:90px;right:24px;z-index:180;width:50px;height:50px;border-radius:50%;background:#0068FF;color:#fff;display:flex;align-items:center;justify-content:center;font-size:1.3rem;box-shadow:0 4px 16px rgba(0,104,255,.4);text-decoration:none;transition:all .25s ease"><i class="fas fa-comment-dots"></i></a> |
| <button class="chat-fab" onclick="toggleChat()" title="Tư Vấn AI"><i class="fas fa-robot"></i></button> |
| <div class="chat-fab-label">Tư Vấn AI</div> |
| <div class="chat-overlay2" id="chatOverlay2" onclick="closeChat()"></div> |
| <div class="chatbox" id="chatbox"> |
| <div class="chat-head"> |
| <div class="chat-head-avatar"><i class="fas fa-robot"></i></div> |
| <div class="chat-head-info"><h4>Tư Vấn AI</h4><span>Trợ lý thiết bị nhà bếp thông minh</span></div> |
| <button class="chat-close" onclick="closeChat()" title="Đóng"><i class="fas fa-times"></i></button> |
| </div> |
| <div class="chat-body" id="chatBody"> |
| <div class="chat-msg bot">Xin chào anh/chị! 👋 Em là <strong>Malloca AI</strong> — trợ lý tư vấn thiết bị nhà bếp.<br><br>Em có thể giúp anh/chị:<br>🔍 Tìm & so sánh sản phẩm phù hợp<br>💡 Tư vấn theo nhu cầu & ngân sách<br>📋 Giải đáp thông số kỹ thuật<br><br>Anh/chị cần tư vấn gì ạ?</div> |
| </div> |
| <div class="chat-suggestions" id="chatSuggestions"> |
| <div class="chat-sug" onclick="sendSuggestion(this)">Tư vấn bếp từ cho gia đình</div> |
| <div class="chat-sug" onclick="sendSuggestion(this)">Máy hút mùi nào tốt?</div> |
| <div class="chat-sug" onclick="sendSuggestion(this)">Khóa cửa thông minh</div> |
| <div class="chat-sug" onclick="sendSuggestion(this)">📋 Lên báo giá</div> |
| <div class="chat-sug" onclick="sendSuggestion(this)">🛒 Giỏ hàng</div> |
| </div> |
| <div class="chat-foot"> |
| <textarea class="chat-input" id="chatInput" placeholder="Nhập câu hỏi..." rows="1" onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();sendChat()}"></textarea> |
| <button class="chat-send" id="chatSend" onclick="sendChat()"><i class="fas fa-paper-plane"></i></button> |
| </div> |
| </div> |
|
|
| |
| <div class="share-popover-overlay" id="shareOverlay" onclick="if(event.target===this)closeSharePopover()"> |
| <div class="share-popover" style="position:relative"> |
| <button class="share-close-btn" onclick="closeSharePopover()"><i class="fas fa-times"></i></button> |
| <h4><i class="fas fa-share-alt"></i> Chia sẻ</h4> |
| <div class="share-apps"> |
| <div class="share-app" onclick="shareVia('facebook')"><div class="share-app-icon" style="background:#1877F2"><i class="fab fa-facebook-f"></i></div><span>Facebook</span></div> |
| <div class="share-app" onclick="shareVia('messenger')"><div class="share-app-icon" style="background:#0099FF"><i class="fab fa-facebook-messenger"></i></div><span>Messenger</span></div> |
| <div class="share-app" onclick="shareVia('zalo')"><div class="share-app-icon" style="background:#0068FF"><i class="fas fa-comment-dots"></i></div><span>Zalo</span></div> |
| <div class="share-app" onclick="shareVia('twitter')"><div class="share-app-icon" style="background:#000"><i class="fab fa-x-twitter"></i></div><span>X</span></div> |
| <div class="share-app" onclick="shareVia('telegram')"><div class="share-app-icon" style="background:#2AABEE"><i class="fab fa-telegram-plane"></i></div><span>Telegram</span></div> |
| <div class="share-app" onclick="shareVia('whatsapp')"><div class="share-app-icon" style="background:#25D366"><i class="fab fa-whatsapp"></i></div><span>WhatsApp</span></div> |
| <div class="share-app" onclick="shareVia('email')"><div class="share-app-icon" style="background:var(--g)"><i class="fas fa-envelope"></i></div><span>Email</span></div> |
| <div class="share-app" onclick="shareVia('copy')"><div class="share-app-icon" style="background:var(--p)"><i class="fas fa-link"></i></div><span>Copy link</span></div> |
| </div> |
| <div class="share-link-box"> |
| <input class="share-link-input" id="shareLinkInput" readonly> |
| <button class="share-link-copy" onclick="shareVia('copy')"><i class="fas fa-copy"></i> Sao chép</button> |
| </div> |
| </div> |
| </div> |
| <script src="search-plus-boot.js"></script> |
| </body> |
| </html> |