← back to Dw Photo Capture

public/kid-simple/v7-scan-and-go.html

325 lines

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>v7 — Scan &amp; Go</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Spectral:wght@500;600;700&family=Manrope:wght@500;600;700;800&display=swap" rel="stylesheet">
<style>
  :root{
    --teal:#0F3D3E; --teal-2:#164C4D; --copper:#C7784F; --copper-dark:#9B5A38;
    --parchment:#F6EFE4; --ink:#2E2A26; --muted:#8A7F6E; --sage:#5C8F6B;
    --line:#E7DDCB;
  }
  *{box-sizing:border-box;}
  html,body{margin:0;padding:0;background:#d9d3c8;font-family:'Manrope',sans-serif;color:var(--ink);}
  body{display:flex; align-items:flex-start; justify-content:center; min-height:100vh; padding:20px 0;}
  .phone{
    width:390px; min-height:780px; background:var(--parchment); border-radius:36px; overflow:hidden;
    box-shadow:0 24px 60px rgba(0,0,0,.30); display:flex; flex-direction:column; position:relative;
  }
  .screen{ flex:1; display:flex; flex-direction:column; }
  h1,h2{ font-family:'Spectral',serif; margin:0; }
  button{ font-family:'Manrope',sans-serif; cursor:pointer; }

  /* ---- viewfinder (dark scan screens) ---- */
  .viewfinder{
    flex:1; min-height:780px; background:radial-gradient(120% 90% at 50% 0%, #1a5152 0%, var(--teal) 55%, #0A2C2D 100%);
    display:flex; flex-direction:column; align-items:center; color:#F6EFE4; position:relative; overflow:hidden;
    padding:44px 24px 30px;
  }
  .dw-mark{ position:absolute; top:16px; left:20px; font-size:12px; letter-spacing:2px; color:rgba(246,239,228,.55); font-weight:700; }
  .hint-top{
    font-family:'Spectral',serif; font-weight:600; font-size:22px; line-height:1.3; text-align:center;
    margin:14px 0 22px; color:#FBF7F0;
  }
  .scan-frame{
    width:220px; height:220px; position:relative; margin:6px auto 20px; flex-shrink:0;
  }
  .scan-frame .corner{ position:absolute; width:36px; height:36px; border-color:var(--copper); border-style:solid; }
  .corner.tl{ top:0; left:0; border-width:5px 0 0 5px; border-top-left-radius:10px; }
  .corner.tr{ top:0; right:0; border-width:5px 5px 0 0; border-top-right-radius:10px; }
  .corner.bl{ bottom:0; left:0; border-width:0 0 5px 5px; border-bottom-left-radius:10px; }
  .corner.br{ bottom:0; right:0; border-width:0 5px 5px 0; border-bottom-right-radius:10px; }
  .scan-frame .tag-icon{ position:absolute; inset:0; display:flex; align-items:center; justify-content:center; font-size:64px; opacity:.9; }
  .scanline{
    position:absolute; left:8px; right:8px; height:3px; background:linear-gradient(90deg, transparent, #FFD9B0, transparent);
    box-shadow:0 0 12px 2px rgba(255,178,110,.7); animation:sweep 1.8s ease-in-out infinite;
  }
  @keyframes sweep{ 0%{top:6px;} 50%{top:206px;} 100%{top:6px;} }
  .scan-status{ min-height:22px; font-size:15px; font-weight:700; color:#FFD9B0; text-align:center; margin-bottom:14px; }

  .toggle-row{ display:flex; gap:10px; width:100%; margin-bottom:18px; }
  .toggle-btn{
    flex:1; min-height:56px; border-radius:16px; border:2px solid rgba(246,239,228,.28); background:rgba(246,239,228,.08);
    color:#F6EFE4; font-size:15px; font-weight:700; display:flex; align-items:center; justify-content:center; gap:6px;
  }
  .toggle-btn.active{ background:var(--copper); border-color:var(--copper); color:#2A1608; }
  .toggle-btn:focus-visible{ outline:3px solid #6fd3d6; outline-offset:2px; }

  .scanbtn{
    width:100%; min-height:68px; border-radius:22px; background:var(--copper); border:none; color:#2A1608;
    font-size:20px; font-weight:800; letter-spacing:.3px; box-shadow:0 10px 24px rgba(199,120,79,.35);
  }
  .scanbtn:disabled{ opacity:.7; }
  .scanbtn:focus-visible{ outline:3px solid #6fd3d6; outline-offset:2px; }

  .vf-link{
    margin-top:16px; background:none; border:none; color:#D9CFC0; font-size:16px; text-decoration:underline;
    min-height:56px; width:100%;
  }
  .vf-link:focus-visible{ outline:3px solid #6fd3d6; outline-offset:2px; }

  .shutter-wrap{ flex:1; display:flex; align-items:flex-end; justify-content:center; padding-bottom:26px; width:100%; }
  .shutter{
    width:92px; height:92px; border-radius:50%; background:var(--copper); border:6px solid #F6EFE4;
    font-size:36px; display:flex; align-items:center; justify-content:center; box-shadow:0 10px 26px rgba(0,0,0,.35);
  }
  .shutter:focus-visible{ outline:3px solid #6fd3d6; outline-offset:2px; }

  /* ---- card / content screens (warm parchment) ---- */
  .card-screen{ flex:1; display:flex; flex-direction:column; padding:34px 26px 28px; background:var(--parchment); }
  .topbar{ display:flex; align-items:center; justify-content:space-between; margin-bottom:6px; }
  .homefab{
    min-width:52px; min-height:52px; border-radius:14px; background:#fff; border:2px solid var(--line);
    color:var(--ink); font-size:14px; font-weight:700; display:flex; align-items:center; justify-content:center; gap:4px;
    padding:0 12px;
  }
  .homefab:focus-visible{ outline:3px solid #2B6CB0; outline-offset:2px; }
  .kicker{ font-size:13px; font-weight:800; letter-spacing:1.5px; text-transform:uppercase; color:var(--copper-dark); margin:2px 0 8px; }
  .headline{ font-size:28px; font-weight:600; line-height:1.22; color:var(--ink); margin:0 0 22px; }

  .item-card{
    background:#fff; border:2px solid var(--line); border-radius:22px; padding:20px; display:flex; flex-direction:column;
    align-items:center; gap:10px; margin-bottom:22px;
  }
  .swatch{ width:100%; height:150px; border-radius:16px; }
  .sw1{background:repeating-linear-gradient(45deg,#5C8F6B 0 18px,#4c7a5a 18px 36px);}
  .sw2{background:repeating-linear-gradient(-45deg,#7C93B0 0 16px,#617a99 16px 32px);}
  .sw3{background:repeating-linear-gradient(90deg,#C7784F 0 14px,#9B5A38 14px 28px);}
  .itemname{ font-size:20px; font-weight:800; margin:0; text-align:center; }
  .itemmeta{ font-size:15px; color:var(--muted); margin:0; text-align:center; }

  .stack{ display:flex; flex-direction:column; gap:14px; }
  .btn{
    width:100%; min-height:60px; border-radius:18px; font-size:18px; font-weight:800; padding:10px 16px;
    border:2px solid var(--line); background:#fff; color:var(--ink);
  }
  .btn-primary{ background:var(--copper); border-color:var(--copper); color:#2A1608; }
  .btn-secondary{ background:var(--teal); border-color:var(--teal); color:#F6EFE4; }
  .btn:focus-visible{ outline:3px solid #2B6CB0; outline-offset:2px; }
  .link{
    background:none; border:none; color:var(--muted); font-size:16px; text-decoration:underline;
    min-height:56px; width:100%; margin-top:6px;
  }
  .link:focus-visible{ outline:3px solid #2B6CB0; outline-offset:2px; }

  .suggest-row{
    display:flex; align-items:center; gap:14px; background:#fff; border:2px solid var(--line); border-radius:18px;
    padding:12px 14px; min-height:64px; width:100%; text-align:left; margin-bottom:12px;
  }
  .suggest-row .sw-mini{ width:52px; height:52px; border-radius:12px; flex-shrink:0; }
  .suggest-row .txt{ display:flex; flex-direction:column; gap:2px; }
  .suggest-row .txt b{ font-size:16px; }
  .suggest-row .txt span{ font-size:13px; color:var(--muted); }

  .brandlist{ display:flex; flex-direction:column; gap:12px; overflow-y:auto; }
  .brand-row{
    width:100%; min-height:60px; border-radius:16px; border:2px solid var(--line); background:#fff; color:var(--ink);
    font-size:17px; font-weight:700; text-align:left; padding:0 18px;
  }

  .center{ display:flex; flex-direction:column; align-items:center; justify-content:center; flex:1; gap:14px; text-align:center; }
  .bigcheck{ font-size:56px; }
</style>
</head>
<body>
<div class="phone">
  <div class="screen" id="screen" role="main"></div>
</div>

<script>
const MOCK_ITEMS = [
  {name:'Palm Damask', color:'Sage', code:'DW-10423', sw:'sw1'},
  {name:'Trellis Weave', color:'Indigo', code:'DW-88214', sw:'sw2'},
  {name:'Linen Bloom', color:'Blush', code:'DW-55071', sw:'sw3'},
];
const BRANDS = ['York Wallcoverings','Brewster Home','Kravet','Cole & Son','Thibaut','Ronald Redding'];

const state = { screen:'home', simMode:'known', item:null };

function go(screen){ state.screen = screen; render(); }
function randomItem(){ return MOCK_ITEMS[Math.floor(Math.random()*MOCK_ITEMS.length)]; }
function homeFab(){ return `<div class="topbar"><button class="homefab" onclick="go('home')">⌂ Home</button><span></span></div>`; }

function doScan(){
  const btn = document.getElementById('scanBtn');
  const st = document.getElementById('scanStatus');
  if(btn) btn.disabled = true;
  if(st) st.textContent = 'Scanning…';
  setTimeout(()=>{
    if(state.simMode==='known'){ state.item = randomItem(); go('scan-match'); }
    else { go('scan-nomatch'); }
  }, 850);
}

function render(){
  const s = document.getElementById('screen');
  let html = '';

  if(state.screen==='home'){
    html += `<div class="viewfinder">
      <div class="dw-mark">DESIGNER WALLCOVERINGS</div>
      <div class="hint-top">Point your camera<br>at a tag or box</div>
      <div class="scan-frame">
        <div class="corner tl"></div><div class="corner tr"></div>
        <div class="corner bl"></div><div class="corner br"></div>
        <div class="tag-icon">${state.simMode==='known' ? '🏷️' : '🆕'}</div>
        <div class="scanline"></div>
      </div>
      <div class="scan-status" id="scanStatus">&nbsp;</div>
      <div class="toggle-row">
        <button class="toggle-btn ${state.simMode==='known'?'active':''}" onclick="state.simMode='known'; render();">🏷️ Known Tag</button>
        <button class="toggle-btn ${state.simMode!=='known'?'active':''}" onclick="state.simMode='new'; render();">🆕 New Item</button>
      </div>
      <button class="scanbtn" id="scanBtn" onclick="doScan()">Tap to Scan</button>
      <button class="vf-link" onclick="go('find-search')">Or type a code instead</button>
    </div>`;
  }

  else if(state.screen==='scan-match'){
    html += `<div class="card-screen">${homeFab()}
      <div class="kicker">Found It</div>
      <h1 class="headline">What do you want to do?</h1>
      <div class="item-card">
        <div class="swatch ${state.item.sw}"></div>
        <p class="itemname">${state.item.name} — ${state.item.color}</p>
        <p class="itemmeta">code ${state.item.code}</p>
      </div>
      <div class="stack">
        <button class="btn btn-primary" onclick="go('update-camera')">Update It</button>
        <button class="btn btn-secondary" onclick="go('sample-printing')">Sample Just Arrived</button>
        <button class="link" onclick="go('home')">Not this one — scan again</button>
      </div>
    </div>`;
  }

  else if(state.screen==='scan-nomatch'){
    html += `<div class="card-screen">${homeFab()}
      <div class="center" style="flex:none; margin:30px 0 26px;">
        <div class="bigcheck">🔎</div>
        <h1 class="headline" style="margin-bottom:0;">Hmm, we don't<br>recognize this one.</h1>
      </div>
      <div class="stack">
        <button class="btn btn-primary" onclick="go('add-brand')">Add It as New</button>
        <button class="link" onclick="go('home')">Scan Again</button>
      </div>
    </div>`;
  }

  else if(state.screen==='add-camera'){
    html += cameraScreen('Take a photo of it.', "add-brand");
  }
  else if(state.screen==='add-brand'){
    html += `<div class="card-screen">${homeFab()}
      <div class="kicker">Almost Done</div>
      <h1 class="headline">Which brand is it?</h1>
      <div class="brandlist">${BRANDS.map(b=>`<button class="brand-row" onclick="go('add-done')">${b}</button>`).join('')}</div>
    </div>`;
  }
  else if(state.screen==='add-done'){
    html += `<div class="card-screen"><div class="center">
      <div class="bigcheck">🎉</div>
      <h1 class="headline" style="margin-bottom:0;">Saved as a new item!</h1>
      <div class="stack" style="width:100%; margin-top:18px;">
        <button class="btn btn-primary" onclick="go('home')">Scan Something Else</button>
      </div>
    </div></div>`;
  }

  else if(state.screen==='update-camera'){
    html += cameraScreen('Take the new photo.', "update-done");
  }
  else if(state.screen==='update-done'){
    html += `<div class="card-screen"><div class="center">
      <div class="bigcheck">✅</div>
      <h1 class="headline" style="margin-bottom:0;">Updated!</h1>
      <div class="stack" style="width:100%; margin-top:18px;">
        <button class="btn btn-primary" onclick="go('home')">Scan Something Else</button>
      </div>
    </div></div>`;
  }

  else if(state.screen==='sample-printing'){
    html += `<div class="card-screen"><div class="center">
      <div class="bigcheck">🖨️</div>
      <h1 class="headline" style="margin-bottom:0;">Printing your sticker…</h1>
    </div></div>`;
    setTimeout(()=>{ if(state.screen==='sample-printing') go('sample-done'); }, 1100);
  }
  else if(state.screen==='sample-done'){
    html += `<div class="card-screen"><div class="center">
      <div class="bigcheck">✅</div>
      <h1 class="headline" style="margin-bottom:0;">Done! Sample marked received.</h1>
      <div class="stack" style="width:100%; margin-top:18px;">
        <button class="btn btn-primary" onclick="go('home')">Scan Something Else</button>
      </div>
    </div></div>`;
  }

  else if(state.screen==='find-search'){
    html += `<div class="card-screen">${homeFab()}
      <div class="kicker">Find An Item</div>
      <h1 class="headline">Which one are you<br>looking for?</h1>
      ${MOCK_ITEMS.map(it=>`
        <button class="suggest-row" onclick='findPick("${it.code}")'>
          <div class="sw-mini ${it.sw}"></div>
          <div class="txt"><b>${it.name}</b><span>${it.color} · code ${it.code}</span></div>
        </button>`).join('')}
      <button class="vf-link" style="color:var(--muted); margin-top:4px;" onclick="go('home')">Scan instead</button>
    </div>`;
  }
  else if(state.screen==='find-detail'){
    html += `<div class="card-screen">${homeFab()}
      <div class="kicker">Here It Is</div>
      <div class="item-card">
        <div class="swatch ${state.item.sw}"></div>
        <p class="itemname">${state.item.name} — ${state.item.color}</p>
        <p class="itemmeta">code ${state.item.code}</p>
      </div>
      <div class="stack">
        <button class="btn btn-primary" onclick="go('update-camera')">Update This</button>
        <button class="btn btn-secondary" onclick="go('find-search')">Search Again</button>
      </div>
    </div>`;
  }

  s.innerHTML = html;
}

function cameraScreen(hint, nextScreen){
  return `<div class="viewfinder" style="padding-top:56px;">
    <div class="dw-mark">DESIGNER WALLCOVERINGS</div>
    <div class="hint-top">${hint}</div>
    <div class="shutter-wrap">
      <button class="shutter" aria-label="Take photo" onclick="shoot('${nextScreen}')">📸</button>
    </div>
  </div>`;
}
function shoot(nextScreen){
  const btn = document.querySelector('.shutter');
  if(btn){ btn.disabled = true; btn.style.opacity = .6; }
  setTimeout(()=> go(nextScreen), 650);
}
function findPick(code){
  state.item = MOCK_ITEMS.find(i=>i.code===code);
  go('find-detail');
}

render();
</script>
</body>
</html>