← back to Patterndesignlab

public/designer.html

148 lines

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Designer · Pattern Design Lab</title>
<link rel="stylesheet" href="/styles.css">
<style>
  .dprofile{padding:28px 0 8px}
  .dhead{display:flex;gap:24px;align-items:flex-start;flex-wrap:wrap}
  .dphoto{width:120px;height:120px;border-radius:50%;object-fit:cover;background:#eee;flex:0 0 auto;box-shadow:0 2px 10px rgba(0,0,0,.12)}
  .dlogo{max-height:34px;max-width:160px;object-fit:contain;margin-bottom:8px;display:block}
  .dname{font-size:30px;line-height:1.1;margin:0 0 4px}
  .dtag{color:#6b6b6b;font-style:italic;margin:0 0 6px}
  .dmeta{font-size:13px;color:#6b6b6b;margin:2px 0}
  .dbio{max-width:620px;margin:14px 0 4px;line-height:1.5}
  .dactions{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0 4px}
  .btn{display:inline-flex;align-items:center;gap:6px;font-size:13px;border:1px solid #ddd;background:#fff;color:#222;
       padding:7px 12px;border-radius:999px;cursor:pointer;text-decoration:none}
  .btn:hover{background:#f6f6f6}
  .btn.pri{background:var(--accent-ink,#1c1c1c);color:#fff;border-color:transparent}
  .socials{display:flex;gap:10px;flex-wrap:wrap;margin:10px 0}
  .socials a{font-size:12px;letter-spacing:.03em;text-transform:uppercase;color:#444;text-decoration:none;border-bottom:1px solid #ddd;padding-bottom:1px}
  .socials a:hover{color:var(--accent-ink,#1c1c1c);border-color:currentColor}
  .share{position:relative}
  .sharemenu{position:absolute;top:110%;left:0;background:#fff;border:1px solid #e5e5e5;border-radius:12px;padding:6px;
    box-shadow:0 8px 24px rgba(0,0,0,.12);display:none;z-index:20;min-width:170px}
  .sharemenu.open{display:block}
  .sharemenu a,.sharemenu button{display:block;width:100%;text-align:left;background:none;border:0;padding:8px 10px;font-size:13px;
    color:#222;border-radius:8px;cursor:pointer;text-decoration:none}
  .sharemenu a:hover,.sharemenu button:hover{background:#f4f4f4}
</style>
</head>
<body>
<header class="top"><div class="wrap">
  <a class="brand" href="/">Pattern Design<span class="dot">.</span>Lab</a>
  <nav class="navlinks"><a href="/">Explore</a> <a href="/designer-login">Designer sign in</a></nav>
</div></header>
<div class="wrap" id="root"><p style="padding:40px 0;color:#6b6b6b">Loading…</p></div>
<footer><div class="wrap">Pattern Design Lab · owned, settlement-passed originals only</div></footer>
<script>
const slug = decodeURIComponent(location.pathname.split('/').pop());
function esc(s){ return (s==null?'':String(s)).replace(/[&<>"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c])); }
function fmtPrice(lo){ return lo==null?'Inquire':'$'+lo; }
let DESIGNS = [];

function socialUrl(type,h){
  if(!h) return null;
  h=String(h).trim(); if(/^https?:\/\//.test(h)&&type!=='website') return h;
  const bare=h.replace(/^@/,'');
  return ({
    website:h, instagram:'https://instagram.com/'+bare, pinterest:'https://pinterest.com/'+bare,
    tiktok:'https://tiktok.com/@'+bare, facebook:'https://facebook.com/'+bare, twitter:'https://twitter.com/'+bare,
    youtube:'https://youtube.com/@'+bare, linkedin:'https://linkedin.com/company/'+bare
  })[type]||null;
}
const SOCIAL_LABELS={website:'Website',instagram:'Instagram',pinterest:'Pinterest',tiktok:'TikTok',facebook:'Facebook',twitter:'X',youtube:'YouTube',linkedin:'LinkedIn'};

function sortDesigns(list, mode){
  const a=list.slice();
  const cmp={ newest:(x,y)=>new Date(y.created_at||0)-new Date(x.created_at||0),
    color:(x,y)=>String(x.dominant_hex||'').localeCompare(String(y.dominant_hex||'')),
    style:(x,y)=>String(x.style||'').localeCompare(String(y.style||'')),
    title_az:(x,y)=>String(x.title||'').localeCompare(String(y.title||'')),
    price_up:(x,y)=>(x.price_min??1e9)-(y.price_min??1e9),
    price_dn:(x,y)=>(y.price_min??-1)-(x.price_min??-1) }[mode]||(()=>0);
  return a.sort(cmp);
}
function renderGrid(){
  const mode=document.getElementById('sort').value;
  localStorage.setItem('pdl.sort',mode);
  const rows=sortDesigns(DESIGNS,mode);
  document.getElementById('grid').innerHTML=rows.map(x=>`
    <a class="card" href="/design/${x.slug}">
      <div class="thumb"><div class="tile" style="background-image:url('${x.img}')"></div>
        ${x.seamless?'<span class="badge">Seamless</span>':'<span class="badge">Mural</span>'}</div>
      <div class="cardbody"><h3>${esc(x.title)}</h3>
        <div class="meta"><span class="swatch" style="background:${x.dominant_hex||'#ccc'}"></span><span>${esc(x.style||'')}</span><span>·</span><span>${esc(x.colorway||'')}</span></div>
        <div class="price">${fmtPrice(x.price_min)}</div></div>
    </a>`).join('');
}
function toggleShare(){ document.getElementById('sharemenu').classList.toggle('open'); }
async function copyLink(){ try{ await navigator.clipboard.writeText(location.href); alert('Link copied'); }catch{ prompt('Copy link:',location.href);} }

(async()=>{
  const r=await fetch('/api/designers/'+encodeURIComponent(slug));
  if(!r.ok){ document.getElementById('root').innerHTML='<p style="padding:40px 0">Designer not found. <a href="/">Explore</a></p>'; return; }
  const {designer:d, designs, count}=await r.json();
  DESIGNS=designs||[];
  document.title=d.name+' · Pattern Design Lab';

  // owner check → show Edit
  let isOwner=false;
  try{ const me=await fetch('/api/designer/me'); if(me.ok){ const j=await me.json(); isOwner=j.designer&&j.designer.slug===slug; } }catch{}

  const loc=[d.city,d.state_region,d.country].filter(Boolean).join(', ');
  const socials=Object.keys(SOCIAL_LABELS).map(t=>{const u=socialUrl(t,d[t]);return u?`<a href="${esc(u)}" target="_blank" rel="noopener noreferrer">${SOCIAL_LABELS[t]}</a>`:''}).join('');
  const shareText=encodeURIComponent((d.name||'')+' on Pattern Design Lab');
  const shareUrl=encodeURIComponent(location.href);

  document.getElementById('root').innerHTML=`
    <div class="dprofile">
      <div class="dhead">
        <img class="dphoto" src="${esc(d.avatar||'')}" alt="${esc(d.name)}" onerror="this.style.background='${d.accent_hex||'#ddd'}';this.removeAttribute('src')">
        <div style="flex:1;min-width:260px">
          ${d.logo?`<img class="dlogo" src="${esc(d.logo)}" alt="${esc(d.name)} logo">`:''}
          <h1 class="dname">${esc(d.name)}</h1>
          ${d.tagline?`<p class="dtag">${esc(d.tagline)}</p>`:''}
          <p class="dmeta">${esc(loc)}${loc?' · ':''}${count} design${count===1?'':'s'}${d.founder_name?' · '+esc(d.founder_name):''}</p>
          ${socials?`<div class="socials">${socials}</div>`:''}
          <div class="dactions">
            <div class="share">
              <button class="btn" onclick="toggleShare()">Share ▾</button>
              <div class="sharemenu" id="sharemenu">
                <button onclick="copyLink()">Copy link</button>
                <a href="https://twitter.com/intent/tweet?url=${shareUrl}&text=${shareText}" target="_blank" rel="noopener noreferrer">Share on X</a>
                <a href="https://www.facebook.com/sharer/sharer.php?u=${shareUrl}" target="_blank" rel="noopener noreferrer">Share on Facebook</a>
                <a href="https://pinterest.com/pin/create/button/?url=${shareUrl}&description=${shareText}" target="_blank" rel="noopener noreferrer">Pin it</a>
                <a href="https://www.linkedin.com/sharing/share-offsite/?url=${shareUrl}" target="_blank" rel="noopener noreferrer">Share on LinkedIn</a>
              </div>
            </div>
            ${isOwner?`<a class="btn pri" href="/designer-dashboard">✎ Edit your page</a>`:''}
          </div>
          ${d.bio?`<p class="dbio">${esc(d.bio)}</p>`:''}
        </div>
      </div>
    </div>
    <div class="bar"><span class="count">${count} designs</span>
      <div><label for="sort">Sort</label>
        <select id="sort" onchange="renderGrid()">
          <option value="newest">Newest</option><option value="color">Color</option>
          <option value="style">Style</option><option value="title_az">Title A→Z</option>
          <option value="price_up">Price ↑</option><option value="price_dn">Price ↓</option>
        </select></div>
      <div><label for="density">Density</label>
        <input id="density" type="range" min="2" max="6" step="1" value="4" oninput="document.documentElement.style.setProperty('--cols',this.value);localStorage.setItem('pdl.density',this.value)"></div></div>
    <div class="grid" id="grid"></div>
    <div style="padding:24px 0"><a href="/" style="color:var(--accent-ink)">← Back to all patterns</a></div>`;

  const ss=localStorage.getItem('pdl.sort'); if(ss){const el=document.getElementById('sort'); if(el&&[...el.options].some(o=>o.value===ss)) el.value=ss;}
  const dd=localStorage.getItem('pdl.density'); if(dd){document.documentElement.style.setProperty('--cols',dd);const el=document.getElementById('density');if(el)el.value=dd;}
  renderGrid();
  document.addEventListener('click',e=>{ const s=document.getElementById('sharemenu'); if(s&&!e.target.closest('.share')) s.classList.remove('open'); });
})();
</script>
</body>
</html>