bep40 commited on
Commit
a8fd67c
·
verified ·
1 Parent(s): 47b3881

Product titles: MODEL CODE | Malloca | Name. Catalogue search UI ready.

Browse files
Files changed (1) hide show
  1. index.html +76 -5
index.html CHANGED
@@ -378,7 +378,12 @@ textarea.form-input{height:120px;resize:vertical}
378
  <div class="page-section" id="page-catalogue">
379
  <div class="section-header" style="text-align:center;margin-bottom:10px">
380
  <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)">Malloca</span></h2>
381
- <p style="color:var(--g);font-size:.9rem;max-width:550px;margin:10px auto 0">Chọn catalogue để xem trực tiếp — mỗi cuốn flipbook lật trang tương tác đầy đủ</p>
 
 
 
 
 
382
  </div>
383
  <!-- Tab selector -->
384
  <div id="catTabs" style="display:flex;gap:8px;flex-wrap:wrap;justify-content:center;margin-bottom:20px"></div>
@@ -475,7 +480,7 @@ const PRICES=[
475
  {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}
476
  ];
477
 
478
- fetch('products.json').then(r=>r.json()).then(d=>{D=d.map(p=>({name:p.n,link:p.l,image:p.i,price:p.p,priceNum:p.pn,cat:p.c,catSlug:p.cs,catIcon:p.ci,images:p.imgs||[],summary:p.sum||'',desc:p.desc||'',specs:p.specs||{},feats:p.feats||[],sku:p.sku||'',video:p.vid||''}));init()});
479
 
480
  function init(){buildCats();buildPrices();render()}
481
 
@@ -494,7 +499,7 @@ document.getElementById('pr').innerHTML=h;document.getElementById('prm').innerHT
494
  function getF(){
495
  let r=D;
496
  if(S.cat!=='all')r=r.filter(p=>p.catSlug===S.cat);
497
- if(S.q){let q=S.q.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'');r=r.filter(p=>{let n=(p.name+' '+p.cat+' '+p.sku).toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'');return n.includes(q)})}
498
  if(S.pmin>0||S.pmax<1e15)r=r.filter(p=>p.priceNum>=S.pmin&&p.priceNum<=S.pmax);
499
  if(S.sort==='pa')r.sort((a,b)=>a.priceNum-b.priceNum);
500
  else if(S.sort==='pd')r.sort((a,b)=>b.priceNum-a.priceNum);
@@ -511,7 +516,7 @@ let g=document.getElementById('grid');
511
  g.className='pg'+(S.view==='l'?' lv':'');
512
  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>`;
513
  }else{
514
- g.innerHTML=items.map((p,i)=>{let idx=D.indexOf(p);return`<div class="pc fade" onclick="showDetail(${idx})" style="transition-delay:${Math.min(i*25,400)}ms"><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></div><div class="pb"><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></div>`}).join('');
515
  }
516
  requestAnimationFrame(()=>document.querySelectorAll('.fade').forEach(e=>e.classList.add('vis')));
517
  renderPag(tp);
@@ -586,9 +591,10 @@ dv.innerHTML=`
586
  ${allImgs.length>1?`<div class="gallery-thumbs">${thumbs}</div>`:''}
587
  </div>
588
  <div class="detail-info">
589
- ${p.sku?`<div class="detail-sku">SKU: ${p.sku}</div>`:''}
590
  <div class="detail-cat"><i class="fas ${p.catIcon}"></i> ${p.cat}</div>
591
  <h1 class="detail-name">${p.name}</h1>
 
592
  <div class="detail-price">${p.price||'Liên hệ'}</div>
593
  ${p.summary?`<div class="detail-summary">${p.summary}</div>`:''}
594
  <div class="detail-badges">
@@ -726,6 +732,71 @@ document.body.appendChild(overlay);
726
  }
727
 
728
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
729
  // ===== NAVIGATION =====
730
  let currentPage='products';
731
 
 
378
  <div class="page-section" id="page-catalogue">
379
  <div class="section-header" style="text-align:center;margin-bottom:10px">
380
  <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)">Malloca</span></h2>
381
+ <p style="color:var(--g);font-size:.9rem;max-width:550px;margin:10px auto 0">Tất cả 607 trang catalogue hiển thị trực tiếp — tìm kiếm theo văn bản trong ảnh</p>
382
+ <div style="max-width:480px;margin:16px auto 0;position:relative">
383
+ <i class="fas fa-search" style="position:absolute;left:14px;top:50%;transform:translateY(-50%);color:var(--g);font-size:.85rem"></i>
384
+ <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">
385
+ <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>
386
+ </div>
387
  </div>
388
  <!-- Tab selector -->
389
  <div id="catTabs" style="display:flex;gap:8px;flex-wrap:wrap;justify-content:center;margin-bottom:20px"></div>
 
480
  {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}
481
  ];
482
 
483
+ fetch('products.json').then(r=>r.json()).then(d=>{D=d.map(p=>({name:p.n,link:p.l,image:p.i,price:p.p,priceNum:p.pn,cat:p.c,catSlug:p.cs,catIcon:p.ci,images:p.imgs||[],summary:p.sum||'',desc:p.desc||'',specs:p.specs||{},feats:p.feats||[],sku:p.sku||'',video:p.vid||'',model:p.mod||''}));init()});
484
 
485
  function init(){buildCats();buildPrices();render()}
486
 
 
499
  function getF(){
500
  let r=D;
501
  if(S.cat!=='all')r=r.filter(p=>p.catSlug===S.cat);
502
+ if(S.q){let q=S.q.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'');r=r.filter(p=>{let n=(p.name+' '+p.cat+' '+p.sku+' '+p.model+' malloca').toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'');return n.includes(q)})}
503
  if(S.pmin>0||S.pmax<1e15)r=r.filter(p=>p.priceNum>=S.pmin&&p.priceNum<=S.pmax);
504
  if(S.sort==='pa')r.sort((a,b)=>a.priceNum-b.priceNum);
505
  else if(S.sort==='pd')r.sort((a,b)=>b.priceNum-a.priceNum);
 
516
  g.className='pg'+(S.view==='l'?' lv':'');
517
  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>`;
518
  }else{
519
+ g.innerHTML=items.map((p,i)=>{let idx=D.indexOf(p);return`<div class="pc fade" onclick="showDetail(${idx})" style="transition-delay:${Math.min(i*25,400)}ms"><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></div><div class="pb"><div style="font-size:.65rem;font-weight:700;color:var(--a);letter-spacing:.5px;margin-bottom:4px">${p.model}</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></div>`}).join('');
520
  }
521
  requestAnimationFrame(()=>document.querySelectorAll('.fade').forEach(e=>e.classList.add('vis')));
522
  renderPag(tp);
 
591
  ${allImgs.length>1?`<div class="gallery-thumbs">${thumbs}</div>`:''}
592
  </div>
593
  <div class="detail-info">
594
+ ${p.sku?`<div class="detail-sku">SKU: ${p.sku} &bull; Model: ${p.model}</div>`:''}
595
  <div class="detail-cat"><i class="fas ${p.catIcon}"></i> ${p.cat}</div>
596
  <h1 class="detail-name">${p.name}</h1>
597
+ <div style="font-size:.95rem;font-weight:800;color:var(--a);letter-spacing:1px;margin-bottom:12px">${p.model} | Malloca</div>
598
  <div class="detail-price">${p.price||'Liên hệ'}</div>
599
  ${p.summary?`<div class="detail-summary">${p.summary}</div>`:''}
600
  <div class="detail-badges">
 
732
  }
733
 
734
 
735
+ // ===== CATALOGUE SEARCH (OCR text index) =====
736
+ let catOcrData=null;
737
+ let catSearchMode=false;
738
+
739
+ function loadCatOcr(){
740
+ if(catOcrData)return Promise.resolve();
741
+ return fetch('catalogue_ocr.json').then(r=>r.ok?r.json():null).then(d=>{catOcrData=d}).catch(()=>{catOcrData=null});
742
+ }
743
+
744
+ function searchCatalogue(){
745
+ let q=document.getElementById('catSearch').value.trim().toLowerCase();
746
+ let countEl=document.getElementById('catSearchCount');
747
+ if(!q){
748
+ catSearchMode=false;
749
+ countEl.textContent='';
750
+ renderCatPages();
751
+ return;
752
+ }
753
+ catSearchMode=true;
754
+ if(!catOcrData){
755
+ loadCatOcr().then(()=>searchCatalogue());
756
+ countEl.textContent='Đang tải...';
757
+ return;
758
+ }
759
+ // Search through OCR texts
760
+ let results=[];
761
+ for(let ci=0;ci<CAT_LIST.length;ci++){
762
+ let ocr=catOcrData&&catOcrData[ci]?catOcrData[ci].texts:[];
763
+ let pages=CAT_LIST[ci].pages;
764
+ for(let pi=0;pi<pages.length;pi++){
765
+ let txt=(ocr[pi]||'').toLowerCase();
766
+ if(txt.includes(q)){
767
+ results.push({catIdx:ci,catName:CAT_LIST[ci].name,pageIdx:pi,pageNum:pi+1,img:pages[pi],text:ocr[pi]||''});
768
+ }
769
+ }
770
+ }
771
+ countEl.textContent=results.length?results.length+' trang':'Không tìm thấy';
772
+ renderCatSearchResults(results,q);
773
+ }
774
+
775
+ function renderCatSearchResults(results,q){
776
+ let container=document.getElementById('catContent');
777
+ if(!results.length){
778
+ container.innerHTML=`<div style="text-align:center;padding:50px;color:var(--g)"><i class="fas fa-search" style="font-size:2.5rem;opacity:.3;display:block;margin-bottom:14px"></i><h3 style="font-size:1rem;font-weight:700;color:var(--d);margin-bottom:6px">Không tìm thấy "${q}"</h3><p style="font-size:.85rem">Thử từ khóa khác hoặc xóa bộ lọc</p></div>`;
779
+ return;
780
+ }
781
+ // Hide tabs selection when showing search results
782
+ container.innerHTML=`
783
+ <div style="background:#fff;border-radius:20px;padding:24px;box-shadow:0 2px 16px rgba(0,0,0,.06)">
784
+ <div style="margin-bottom:16px"><h3 style="font-size:1.1rem;font-weight:700;color:var(--d)"><i class="fas fa-search" style="color:var(--a);margin-right:8px"></i>${results.length} trang chứa "${q}"</h3></div>
785
+ <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:14px">
786
+ ${results.map(r=>`<div style="position:relative;border-radius:12px;overflow:hidden;border:2px solid var(--gl);cursor:pointer;transition:all .2s" onmouseover="this.style.borderColor='var(--p)';this.style.transform='translateY(-2px)'" onmouseout="this.style.borderColor='var(--gl)';this.style.transform=''" onclick="openCatImage('${r.img.replace(/'/g,"\\'")}')">
787
+ <img src="${r.img}" alt="" loading="lazy" style="width:100%;display:block">
788
+ <div style="position:absolute;top:8px;left:8px;background:var(--p);color:#fff;padding:3px 10px;border-radius:6px;font-size:.68rem;font-weight:700">${r.catName} — Trang ${r.pageNum}</div>
789
+ <div style="position:absolute;bottom:0;left:0;right:0;background:linear-gradient(transparent,rgba(0,0,0,.7));padding:10px 12px 8px;color:#fff;font-size:.72rem;line-height:1.4">${highlightText(r.text.substring(0,120),q)}...</div>
790
+ </div>`).join('')}
791
+ </div>
792
+ </div>`;
793
+ }
794
+
795
+ function highlightText(text,q){
796
+ let re=new RegExp('('+q.replace(/[.*+?^${}()|[\]\\]/g,'\\$&')+')','gi');
797
+ return text.replace(re,'<mark style="background:var(--al);color:var(--d);padding:1px 2px;border-radius:2px">$1</mark>');
798
+ }
799
+
800
  // ===== NAVIGATION =====
801
  let currentPage='products';
802