라이트루프 로고 라이트루프 포토스튜디오

즐겨찾기

비교하고 싶은 상품에 메모를 남겨 두세요. alphabotcoreai.pro

'; } try { const foot = await fetch('./footer.html').then(r=>r.text()); document.querySelector('footer[data-include]').innerHTML = foot; } catch(e) { document.querySelector('footer[data-include]').innerHTML = ''; } document.body.addEventListener('click', (e)=>{ const tbtn = e.target.closest('[data-theme-toggle]'); if (tbtn) { const cur = localStorage.getItem('theme')||'light'; const next = cur==='light'?'dark':'light'; localStorage.setItem('theme', next); document.documentElement.classList.toggle('dark', next==='dark'); } }); } includePartials(); const notesKey = 'favoriteNotes'; function getNotes() { return JSON.parse(localStorage.getItem(notesKey)||'{}'); } function setNotes(o) { localStorage.setItem(notesKey, JSON.stringify(o)); } const favKey = 'favorites'; function getFavs(){ return JSON.parse(localStorage.getItem(favKey)||'[]'); } function setFavs(arr){ localStorage.setItem(favKey, JSON.stringify(arr)); } const autosaveTimers = new Map(); async function loadFavs() { const list = document.getElementById('favList'); const ids = getFavs(); const statusBar = document.getElementById('statusBar'); statusBar.textContent = `선택한 상품 ${ids.length}개`; let data = []; try { const all = await fetch('./catalog.json').then(r=>r.json()); data = all.filter(d=>ids.includes(String(d.id))); } catch(e) { list.innerHTML = `
  • 데이터를 불러오지 못했습니다. 잠시 후 다시 시도해주세요.
  • `; return; } render(data); } function imageFor(it){ const src = (it.images && it.images[0]) || './images/maximum_ditailes_of_this_image.professional_photography_studio_softbox_tripod_background_lightroom_ready_ultra_hd_high_contrast_minimal_black_white.jpg'; return src; } function render(items) { const list = document.getElementById('favList'); const notes = getNotes(); if (!items.length) { list.innerHTML = `
  • 즐겨찾기한 상품이 없습니다.

    카탈로그에서 마음에 드는 상품을 추가해 보세요.

  • `; return; } list.innerHTML = items.map(it=>{ const nid = String(it.id); const noteVal = (notes && notes[nid]) || ''; const priceText = typeof it.price === 'number' ? it.price.toLocaleString() : (it.price||''); return `
  • ${it.title||'촬영 상품'} 대표 이미지

    ${it.title||'상품명 미지정'}

    ${priceText?`${priceText}원 • `:''}${it.category||'기타'}

  • `; }).join(''); } function showToast(msg){ const el = document.getElementById('toast'); const msgEl = document.getElementById('toastMsg'); msgEl.textContent = msg || '완료되었습니다'; el.classList.remove('hidden'); clearTimeout(showToast._t); showToast._t = setTimeout(()=>{ el.classList.add('hidden'); }, 2600); } document.getElementById('toastClose').addEventListener('click', ()=> { document.getElementById('toast').classList.add('hidden'); }); function openModal({title, body, actions=[]}){ const overlay = document.getElementById('modalOverlay'); const t = document.getElementById('modalTitle'); const b = document.getElementById('modalBody'); const a = document.getElementById('modalActions'); t.textContent = title||'알림'; if (typeof body === 'string') b.innerHTML = body; else { b.innerHTML=''; b.appendChild(body); } a.innerHTML = ''; actions.forEach(btn=>{ const el = document.createElement('button'); el.textContent = btn.label; el.className = btn.className || 'px-4 py-2 rounded-lg border border-gray-300 dark:border-neutral-700'; el.addEventListener('click', ()=>{ if (btn.onClick) btn.onClick(); }); a.appendChild(el); }); overlay.classList.remove('hidden'); const close = ()=> overlay.classList.add('hidden'); document.getElementById('modalClose').onclick = close; overlay.onclick = (e)=>{ if (e.target===overlay) close(); }; return { close }; } function confirmDelete(id){ const { close } = openModal({ title: '삭제 확인', body: `
    해당 상품을 즐겨찾기에서 제거하시겠어요? 메모는 유지됩니다.
    `, actions: [ { label:'취소', className:'px-4 py-2 rounded-lg border border-gray-300 dark:border-neutral-700', onClick:()=>close() }, { label:'삭제', className:'px-4 py-2 rounded-lg bg-red-600 text-white', onClick:()=>{ removeFav(id); close(); } } ] }); } function removeFav(id){ const ids = new Set(getFavs()); ids.delete(String(id)); setFavs([...ids]); loadFavs(); showToast('즐겨찾기에서 삭제했어요'); } document.addEventListener('click', (e)=>{ const rid = e.target.getAttribute('data-remove'); if (rid) { confirmDelete(rid); } const sid = e.target.getAttribute('data-save'); if (sid) { const ta = document.querySelector(`textarea[data-note="${sid}"]`); const notes = getNotes(); notes[sid] = (ta.value||'').slice(0,600); setNotes(notes); e.target.textContent = '저장됨'; setTimeout(()=>{e.target.textContent='메모 저장'}, 1000); const ind = document.querySelector(`[data-indicator="${sid}"]`); if (ind){ ind.textContent='수정사항이 저장되었습니다.'; ind.style.opacity='1'; setTimeout(()=>ind.style.opacity='0',1200); } } const cid = e.target.getAttribute('data-cart'); if (cid) { const cart = JSON.parse(localStorage.getItem('cart')||'[]'); const f = cart.find(x=>String(x.id)===String(cid)); if (f) f.qty=(f.qty||1)+1; else cart.push({id:String(cid), qty:1}); localStorage.setItem('cart', JSON.stringify(cart)); showToast('장바구니에 담았어요'); } }); document.addEventListener('input', (e)=>{ const ta = e.target.closest('textarea[data-note]'); if (!ta) return; const id = ta.getAttribute('data-note'); const ind = document.querySelector(`[data-indicator="${id}"]`); if (ind){ ind.textContent='자동 저장 대기중...'; ind.style.opacity='1'; } if (autosaveTimers.has(id)) clearTimeout(autosaveTimers.get(id)); let countdown = 2; if (ind){ ind.textContent = `자동 저장: ${countdown}s`; } const tick = setInterval(()=>{ countdown--; if (ind){ ind.textContent = countdown>0 ? `자동 저장: ${countdown}s` : '저장 중...'; } if (countdown<=0){ clearInterval(tick); } }, 1000); const t = setTimeout(()=>{ const notes = getNotes(); notes[id] = (ta.value||'').slice(0,600); setNotes(notes); if (ind){ ind.textContent='자동 저장됨'; setTimeout(()=>{ ind.style.opacity='0'; }, 1200); } }, 2000); autosaveTimers.set(id, t); }); window.addEventListener('storage', (e)=>{ if (e.key===favKey){ loadFavs(); } if (e.key===notesKey){ loadFavs(); } }); document.getElementById('exportNotes').addEventListener('click', ()=>{ const notes = getNotes(); const blob = new Blob([JSON.stringify(notes,null,2)], {type:'application/json'}); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'favorite_notes.json'; document.body.appendChild(a); a.click(); a.remove(); URL.revokeObjectURL(url); }); document.getElementById('clearAllFavs').addEventListener('click', ()=>{ const favs = getFavs(); if (!favs.length){ showToast('삭제할 항목이 없습니다'); return; } const { close } = openModal({ title: '전체 삭제', body: `
    모든 즐겨찾기를 삭제하시겠어요? 메모는 유지됩니다.
    `, actions: [ { label:'취소', className:'px-4 py-2 rounded-lg border border-gray-300 dark:border-neutral-700', onClick:()=>close() }, { label:'전체 삭제', className:'px-4 py-2 rounded-lg bg-red-600 text-white', onClick:()=>{ setFavs([]); loadFavs(); close(); showToast('전체 삭제 완료'); } } ] }); }); document.getElementById('shareFavs').addEventListener('click', ()=>{ const wrap = document.createElement('div'); wrap.innerHTML = `
    개인정보는 이 브라우저에만 저장됩니다. 서버로 전송되지 않습니다.
    `; const { close } = openModal({ title: '즐겨찾기 공유', body: wrap, actions: [ { label:'닫기', className:'px-4 py-2 rounded-lg border border-gray-300 dark:border-neutral-700', onClick:()=>close() }, { label:'공유 링크 생성', className:'px-4 py-2 rounded-lg bg-black text-white dark:bg-white dark:text-black', onClick:()=>{ const form = wrap.querySelector('#shareForm'); const name = form.name.value.trim(); const email = form.email.value.trim(); const msg = form.msg.value.trim(); const emailRe = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (name.length<2){ alert('이름을 2자 이상 입력하세요.'); return; } if (!emailRe.test(email)){ alert('올바른 이메일을 입력하세요.'); return; } const payload = { sender:{name,email}, msg, favorites:getFavs(), notes:getNotes(), ts:Date.now() }; const encoded = encodeURIComponent(btoa(unescape(encodeURIComponent(JSON.stringify(payload))))); const shareUrl = location.origin + location.pathname + '#share=' + encoded; const body = document.createElement('div'); body.innerHTML = `
    공유용 데이터가 생성되었습니다.

    아래 링크를 복사해 팀원과 공유하세요.

    ${shareUrl}
    `; const modal = openModal({ title:'공유 링크', body, actions:[{label:'닫기', className:'px-4 py-2 rounded-lg border border-gray-300 dark:border-neutral-700', onClick:()=>modal.close()}]}); body.querySelector('#copyShare').onclick = ()=>{ navigator.clipboard.writeText(shareUrl).then(()=>showToast('복사되었습니다')); }; } } ] }); }); document.addEventListener('click', (e)=>{ if (e.target && e.target.id==='openGuide'){ openModal({ title:'즐겨찾기 안내', body:`
    1. 카탈로그에서 하트 아이콘을 눌러 즐겨찾기에 추가하세요.
    2. 여기에서 메모를 작성하고 비교할 수 있어요.
    3. 장바구니에 담아 예약 절차를 이어가세요.
    `, actions:[{label:'닫기', className:'px-4 py-2 rounded-lg border border-gray-300 dark:border-neutral-700', onClick:function(){ document.getElementById('modalOverlay').classList.add('hidden'); }}] }); } }); function initCookieBar(){ const state = JSON.parse(localStorage.getItem('cookieConsent')||'null'); if (!state){ document.getElementById('cookieBar').classList.remove('hidden'); } document.getElementById('cookieAccept').onclick = ()=>{ localStorage.setItem('cookieConsent', JSON.stringify({necessary:true, analytics:true, marketing:true, ts:Date.now()})); document.getElementById('cookieBar').classList.add('hidden'); showToast('쿠키가 허용되었습니다'); }; document.getElementById('cookieSettings').onclick = ()=>{ const wrap = document.createElement('div'); wrap.innerHTML = `
    `; const { close } = openModal({ title:'쿠키 설정', body: wrap, actions:[ { label:'취소', className:'px-4 py-2 rounded-lg border border-gray-300 dark:border-neutral-700', onClick:()=>close() }, { label:'저장', className:'px-4 py-2 rounded-lg bg-black text-white dark:bg-white dark:text-black', onClick:()=>{ const analytics = wrap.querySelector('#ckAnalytics').checked; const marketing = wrap.querySelector('#ckMarketing').checked; localStorage.setItem('cookieConsent', JSON.stringify({necessary:true, analytics, marketing, ts:Date.now()})); document.getElementById('cookieBar').classList.add('hidden'); close(); showToast('쿠키 설정이 저장되었습니다'); } } ] }); }; } function parseShareHash(){ const hash = location.hash || ''; const m = hash.match(/#share=([^&]+)/); if (!m) return; try{ const decoded = JSON.parse(decodeURIComponent(escape(atob(decodeURIComponent(m[1]))))); const { favorites, notes } = decoded || {}; if (Array.isArray(favorites)) setFavs(favorites.map(String)); if (notes && typeof notes==='object') setNotes(notes); loadFavs(); showToast('공유 데이터를 불러왔습니다'); }catch(e){} } loadFavs(); initCookieBar(); parseShareHash();