아파트 직거래 아직 공유 링크 만들기 Facebook X Pinterest 이메일 기타 앱 12월 24, 2024 🏢 아파트직거래 미니앱 중개수수료 없이 직거래를 돕는 최소기능(MVP) 데모입니다. (데이터는 방문자 브라우저에만 저장) ({ '&':'&','<':'<','>':'>','\"':'"',\"'\":''' }[m]))} function priceLabel(x){ if(x.deal==='월세') return `월 ${money(x.price)}만원`; if(x.deal==='전세') return `전세 ${money(x.price)}만원`; return `매매 ${money(x.price)}만원`; } function cardHTML(x){ const badge = x.deal; const img = x.img ? x.img : fallbackImg(x.title); return ` ${badge} ${esc(x.title)} ${esc(x.city)} ${esc(x.dong||'')} ${x.area? `${x.area}㎡`:''} ${x.floor? `${esc(x.floor)}`:''} ${priceLabel(x)} ${x.fav?`★ 즐겨찾기`:''} ${x.hidden?`숨김`:''} ${x.note?`${esc(x.note)}`:''} 공유 연락 ${x.fav?'즐겨찾기 해제':'즐겨찾기'} ${x.hidden?'숨김 해제':'숨김'} ID: ${x.id} `; } function filtered(){ const term=(q.value||'').trim().toLowerCase(); let rows = listings.filter(x=>{ if(state.hideExcluded && x.hidden) return false; if(state.favOnly && !x.fav) return false; if(city.value && x.city!==city.value) return false; if(deal.value && x.deal!==deal.value) return false; if(term){ const t = `${x.title} ${x.city} ${x.dong||''} ${x.note||''}`.toLowerCase(); if(!t.includes(term)) return false; } return true; }); // sort const s = sort.value; rows.sort((a,b)=>{ if(s==='new') return (b.ts||0)-(a.ts||0); if(s==='low') return (a.price||0)-(b.price||0); if(s==='high') return (b.price||0)-(a.price||0); if(s==='area') return (b.area||0)-(a.area||0); return 0; }); return rows; } function render(){ const rows=filtered(); grid.innerHTML = rows.map(cardHTML).join(''); empty.style.display = rows.length ? 'none' : 'block'; // bind actions (event delegation) grid.onclick = async (e)=>{ const btn = e.target.closest('button'); if(!btn) return; const act = btn.dataset.act; const id = btn.dataset.id; const x = listings.find(v=>v.id===id); if(!x) return; if(act==='fav'){ x.fav=!x.fav; save(); render(); } if(act==='hide'){ x.hidden=!x.hidden; save(); render(); } if(act==='contact'){ const tel=(x.contact||'').replace(/[^0-9]/g,''); if(tel.length>=8) location.href='tel:'+tel; else alert('연락처가 없습니다.'); } if(act==='share'){ const url = location.href.split('#')[0] + '#apt=' + encodeURIComponent(x.id); if(navigator.share){ try{ await navigator.share({title:x.title,text:'아파트직거래 매물',url}); }catch(_){} }else if(navigator.clipboard){ await navigator.clipboard.writeText(url); alert('링크 복사 완료:\\n'+url); }else{ prompt('복사해서 공유하세요:', url); } } }; } function openModal(){ back.style.display='grid'; } function closeModal(){ back.style.display='none'; } apply.onclick=render; [q,city,deal,sort].forEach(el=> el.addEventListener('keydown',e=>{ if(e.key==='Enter') render(); })); chipFav.onclick=()=>{ state.favOnly=!state.favOnly; chipFav.classList.toggle('on'); render(); }; chipHide.onclick=()=>{ state.hideExcluded=!state.hideExcluded; chipHide.classList.toggle('on'); render(); }; reset.onclick=()=>{ q.value=''; city.value=''; deal.value=''; sort.value='new'; state.favOnly=false; state.hideExcluded=true; chipFav.classList.remove('on'); chipHide.classList.add('on'); render(); }; sharePage.onclick=async ()=>{ const url = location.href.split('#')[0]; if(navigator.share){ try{ await navigator.share({title:'아파트직거래',text:'아파트직거래 미니앱',url}); }catch(_){} }else if(navigator.clipboard){ await navigator.clipboard.writeText(url); alert('페이지 주소 복사 완료:\\n'+url); }else{ prompt('복사해서 공유하세요:', url); } }; openAdd.onclick=openModal; closeAdd.onclick=closeModal; back.onclick=(e)=>{ if(e.target===back) closeModal(); }; clearAdd.onclick=()=>{ [f_title,f_city,f_dong,f_price,f_area,f_floor,f_contact,f_note,f_img].forEach(i=>i.value=''); f_deal.value='매매'; }; saveAdd.onclick=()=>{ const title=f_title.value.trim(); const cityv=f_city.value.trim(); const dealv=f_deal.value; const price=Number(f_price.value||0); if(!title || !cityv || !dealv || !price){ alert('제목/지역/거래유형/가격은 필수입니다.'); return; } const id='U'+Math.random().toString(36).slice(2,8).toUpperCase(); const item={ id, title, city:cityv, dong:f_dong.value.trim(), deal:dealv, price, area:Number(f_area.value||0), floor:f_floor.value.trim(), contact:f_contact.value.trim(), note:f_note.value.trim(), img:f_img.value.trim(), fav:false, hidden:false, ts:Date.now() }; listings=[item, ...listings]; save(); closeModal(); render(); alert('등록 완료! (이 브라우저에만 저장됩니다)'); }; // initial render render(); })();