bep40 commited on
Commit
23f66f1
·
verified ·
1 Parent(s): e161223

Add deep linking & share button: product URLs, hash routing fix, copy shareable link

Browse files
Files changed (1) hide show
  1. index.html +93 -4
index.html CHANGED
@@ -313,6 +313,13 @@ textarea.form-input{height:120px;resize:vertical}
313
  .cart-drawer{width:100%;max-width:100%}
314
  }
315
 
 
 
 
 
 
 
 
316
  </style>
317
  </head>
318
  <body>
@@ -464,6 +471,9 @@ textarea.form-input{height:120px;resize:vertical}
464
  </div>
465
  </div>
466
 
 
 
 
467
  <footer class="footer"><div class="container">
468
  <div class="footer-grid">
469
  <div class="footer-brand"><div class="logo-name" style="color:#fff;font-size:1.1rem">MALL<span style="color:var(--a)">OCA</span> & EURO<span style="color:var(--a)">GOLD</span> & GR<span style="color:var(--a)">OB</span></div><p>1979 sản phẩm với dữ liệu chi tiết đầy đủ — hình ảnh, thông số kỹ thuật, mô tả, tính năng — tích hợp từ malloca.com, eurogold.vn & catalogue Grob.</p>
@@ -493,7 +503,7 @@ if(p.specs){Object.entries(p.specs).forEach(([k,v])=>{parts.push(k);parts.push(S
493
  return nVN(parts.join(' '));
494
  }
495
 
496
- function init(){buildCats();buildPrices();buildBrands();render()}
497
 
498
  function buildCats(){
499
  let cats={};D.forEach(p=>{if(!cats[p.catSlug])cats[p.catSlug]={n:p.cat,i:p.catIcon,c:0};cats[p.catSlug].c++});
@@ -558,6 +568,8 @@ document.getElementById('pag').innerHTML=h;
558
  // ===== DETAIL VIEW =====
559
  function showDetail(idx){
560
  let p=D[idx];
 
 
561
  document.getElementById('listView').style.display='none';
562
  document.querySelector('.hero').style.display='none';
563
  let dv=document.getElementById('detailView');
@@ -627,6 +639,7 @@ ${p.summary?`<div class="detail-summary">${p.summary}</div>`:''}
627
  <div style="display:flex;gap:10px;flex-wrap:wrap">
628
  <button class="add-cart-btn" onclick="event.stopPropagation();addToCart(${idx})"><i class="fas fa-cart-plus"></i> Thêm vào giỏ</button>
629
  <a href="${p.link}" target="_blank" class="detail-btn detail-btn-outline"><i class="fas fa-external-link-alt"></i> ${p.brand==='Eurogold'?'eurogold.vn':p.brand==='Grob'?'grob.vn':'malloca.com'}</a>
 
630
  </div>
631
  </div>
632
  </div>
@@ -644,6 +657,8 @@ document.getElementById('detailView').classList.remove('show');
644
  document.getElementById('detailView').innerHTML='';
645
  document.getElementById('listView').style.display='';
646
  document.querySelector('.hero').style.display='';
 
 
647
  }
648
 
649
  function goHome(){
@@ -927,6 +942,17 @@ function parseUrlParams(){
927
  let hash=location.hash.replace(/^#/,'');
928
  let params=new URLSearchParams(hash);
929
 
 
 
 
 
 
 
 
 
 
 
 
930
  // Check for page navigation first
931
  let pageParam=params.get('page');
932
  if(pageParam&&pageParam!=='products'){
@@ -941,9 +967,9 @@ S.q=q;
941
  S.pg=1;
942
  }
943
  let cat=params.get('cat');
944
- if(cat){S.cat=cat;}
945
  let brand=params.get('brand');
946
- if(brand){S.brand=brand;}
947
  let pgNum=params.get('pg');
948
  if(pgNum){S.pg=parseInt(pgNum)||1;}
949
  let sort=params.get('sort');
@@ -974,22 +1000,85 @@ location.hash='page='+page;
974
  window.addEventListener('hashchange',()=>{
975
  let hash=location.hash.replace(/^#/,'');
976
  let params=new URLSearchParams(hash);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
977
  let pageParam=params.get('page');
978
  if(pageParam&&pageParam!=='products'){
979
  showPage(pageParam);
980
  return;
981
  }
 
 
 
 
 
982
  parseUrlParams();
983
  render();
984
  });
985
 
986
  // Init on load
987
  document.addEventListener('DOMContentLoaded',()=>{
988
- parseUrlParams();
989
  initCatalogue();
990
  updateCartBadge();
991
  });
992
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
993
  </script>
994
  </body>
995
  </html>
 
313
  .cart-drawer{width:100%;max-width:100%}
314
  }
315
 
316
+ /* SHARE BUTTON */
317
+ .share-btn{position:fixed;bottom:24px;right:24px;z-index:90;display:flex;align-items:center;gap:8px;padding:12px 20px;background:var(--p);color:#fff;border:none;border-radius:14px;font-weight:700;font-size:.82rem;cursor:pointer;font-family:inherit;box-shadow:0 4px 16px rgba(0,63,98,.35);transition:var(--t)}
318
+ .share-btn:hover{background:var(--pl);transform:translateY(-2px);box-shadow:0 8px 24px rgba(0,63,98,.4)}
319
+ .share-btn i{font-size:.9rem}
320
+ .share-toast{position:fixed;bottom:80px;right:24px;z-index:91;background:#28a745;color:#fff;padding:10px 18px;border-radius:10px;font-size:.82rem;font-weight:600;box-shadow:0 4px 12px rgba(0,0,0,.2);animation:fadeUp .3s ease;pointer-events:none}
321
+ @media(max-width:768px){.share-btn{bottom:16px;right:16px;padding:10px 16px;font-size:.78rem}.share-toast{bottom:70px;right:16px}}
322
+
323
  </style>
324
  </head>
325
  <body>
 
471
  </div>
472
  </div>
473
 
474
+ <!-- SHARE BUTTON -->
475
+ <button class="share-btn" onclick="copyShareLink()" title="Sao chép link chia sẻ trang hiện tại"><i class="fas fa-share-alt"></i> Chia sẻ trang này</button>
476
+
477
  <footer class="footer"><div class="container">
478
  <div class="footer-grid">
479
  <div class="footer-brand"><div class="logo-name" style="color:#fff;font-size:1.1rem">MALL<span style="color:var(--a)">OCA</span> & EURO<span style="color:var(--a)">GOLD</span> & GR<span style="color:var(--a)">OB</span></div><p>1979 sản phẩm với dữ liệu chi tiết đầy đủ — hình ảnh, thông số kỹ thuật, mô tả, tính năng — tích hợp từ malloca.com, eurogold.vn & catalogue Grob.</p>
 
503
  return nVN(parts.join(' '));
504
  }
505
 
506
+ function init(){buildCats();buildPrices();buildBrands();render();parseUrlParams();render()}
507
 
508
  function buildCats(){
509
  let cats={};D.forEach(p=>{if(!cats[p.catSlug])cats[p.catSlug]={n:p.cat,i:p.catIcon,c:0};cats[p.catSlug].c++});
 
568
  // ===== DETAIL VIEW =====
569
  function showDetail(idx){
570
  let p=D[idx];
571
+ // Update hash with product index for deep linking
572
+ location.hash='product='+idx;
573
  document.getElementById('listView').style.display='none';
574
  document.querySelector('.hero').style.display='none';
575
  let dv=document.getElementById('detailView');
 
639
  <div style="display:flex;gap:10px;flex-wrap:wrap">
640
  <button class="add-cart-btn" onclick="event.stopPropagation();addToCart(${idx})"><i class="fas fa-cart-plus"></i> Thêm vào giỏ</button>
641
  <a href="${p.link}" target="_blank" class="detail-btn detail-btn-outline"><i class="fas fa-external-link-alt"></i> ${p.brand==='Eurogold'?'eurogold.vn':p.brand==='Grob'?'grob.vn':'malloca.com'}</a>
642
+ <button class="detail-btn detail-btn-outline" onclick="event.stopPropagation();copyShareLink()" title="Sao chép link sản phẩm"><i class="fas fa-share-alt"></i> Chia sẻ</button>
643
  </div>
644
  </div>
645
  </div>
 
657
  document.getElementById('detailView').innerHTML='';
658
  document.getElementById('listView').style.display='';
659
  document.querySelector('.hero').style.display='';
660
+ // Remove product hash, restore list hash
661
+ updateHash();
662
  }
663
 
664
  function goHome(){
 
942
  let hash=location.hash.replace(/^#/,'');
943
  let params=new URLSearchParams(hash);
944
 
945
+ // Check for product detail deep link
946
+ let productIdx=params.get('product');
947
+ if(productIdx!==null){
948
+ let idx=parseInt(productIdx);
949
+ if(!isNaN(idx)&&idx>=0&&idx<D.length){
950
+ showPage('products');
951
+ showDetail(idx);
952
+ return;
953
+ }
954
+ }
955
+
956
  // Check for page navigation first
957
  let pageParam=params.get('page');
958
  if(pageParam&&pageParam!=='products'){
 
967
  S.pg=1;
968
  }
969
  let cat=params.get('cat');
970
+ if(cat){S.cat=cat;updActive('.cat-item','data-s',cat);}
971
  let brand=params.get('brand');
972
+ if(brand){S.brand=brand;document.querySelectorAll('[data-b]').forEach(e=>e.classList.toggle('active',e.getAttribute('data-b')===brand));}
973
  let pgNum=params.get('pg');
974
  if(pgNum){S.pg=parseInt(pgNum)||1;}
975
  let sort=params.get('sort');
 
1000
  window.addEventListener('hashchange',()=>{
1001
  let hash=location.hash.replace(/^#/,'');
1002
  let params=new URLSearchParams(hash);
1003
+
1004
+ // Handle product detail deep link
1005
+ let productIdx=params.get('product');
1006
+ if(productIdx!==null){
1007
+ let idx=parseInt(productIdx);
1008
+ if(!isNaN(idx)&&idx>=0&&idx<D.length){
1009
+ showPage('products');
1010
+ // Only re-render detail if not already showing this product
1011
+ let dv=document.getElementById('detailView');
1012
+ if(!dv.classList.contains('show')){
1013
+ showDetail(idx);
1014
+ }
1015
+ return;
1016
+ }
1017
+ }
1018
+
1019
  let pageParam=params.get('page');
1020
  if(pageParam&&pageParam!=='products'){
1021
  showPage(pageParam);
1022
  return;
1023
  }
1024
+ // If hash is empty or products page, go back to list
1025
+ if(!hash||pageParam==='products'){
1026
+ goBackToList();
1027
+ showPage('products');
1028
+ }
1029
  parseUrlParams();
1030
  render();
1031
  });
1032
 
1033
  // Init on load
1034
  document.addEventListener('DOMContentLoaded',()=>{
1035
+ // parseUrlParams is called from init() after data loads
1036
  initCatalogue();
1037
  updateCartBadge();
1038
  });
1039
 
1040
+ // ===== SHARE LINK =====
1041
+ function getShareableUrl(){
1042
+ // Build the direct .hf.space URL with current hash — this URL works when shared
1043
+ let base=window.location.origin+window.location.pathname;
1044
+ let hash=window.location.hash;
1045
+ return base+(hash||'');
1046
+ }
1047
+
1048
+ function copyShareLink(){
1049
+ let url=getShareableUrl();
1050
+ if(navigator.clipboard&&navigator.clipboard.writeText){
1051
+ navigator.clipboard.writeText(url).then(()=>showShareToast('Đã sao chép link!')).catch(()=>fallbackCopy(url));
1052
+ }else{fallbackCopy(url)}
1053
+ }
1054
+
1055
+ function fallbackCopy(text){
1056
+ let ta=document.createElement('textarea');
1057
+ ta.value=text;ta.style.cssText='position:fixed;top:-9999px';
1058
+ document.body.appendChild(ta);ta.select();
1059
+ try{document.execCommand('copy');showShareToast('Đã sao chép link!')}catch(e){showShareToast('Không thể sao chép. URL: '+text)}
1060
+ document.body.removeChild(ta);
1061
+ }
1062
+
1063
+ function showShareToast(msg){
1064
+ let old=document.querySelector('.share-toast');if(old)old.remove();
1065
+ let t=document.createElement('div');t.className='share-toast';
1066
+ t.innerHTML='<i class="fas fa-check-circle" style="margin-right:6px"></i>'+msg;
1067
+ document.body.appendChild(t);
1068
+ setTimeout(()=>{t.style.opacity='0';t.style.transition='opacity .3s';setTimeout(()=>t.remove(),300)},2500);
1069
+ }
1070
+
1071
+ // ===== SYNC HASH TO PARENT (forward-compatible) =====
1072
+ function syncHashToParent(){
1073
+ try{
1074
+ if(window.parent!==window){
1075
+ window.parent.postMessage({type:'hashchange',hash:location.hash,url:getShareableUrl()},'*');
1076
+ }
1077
+ }catch(e){}
1078
+ }
1079
+ // Sync on every hash change
1080
+ window.addEventListener('hashchange',syncHashToParent);
1081
+
1082
  </script>
1083
  </body>
1084
  </html>