<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>CitiStayz — Breezeway Dashboard</title>

<style>

  * { box-sizing: border-box; margin: 0; padding: 0; }

  body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #f5f5f0; color: #1a1a1a; min-height: 100vh; }

  .topbar { background: #130e3d; color: #fff; padding: 0 1.5rem; height: 56px; display: flex; align-items: center; justify-content: space-between; position: sticky; top: 0; z-index: 100; }

  .topbar-logo { font-size: 16px; font-weight: 600; letter-spacing: -0.01em; }

  .topbar-logo span { color: #f0c040; }

  .topbar-right { display: flex; align-items: center; gap: 12px; }

  .status-pill { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; padding: 4px 12px; border-radius: 20px; border: 1px solid rgba(255,255,255,0.2); color: rgba(255,255,255,0.7); }

  .status-pill.ok { background: rgba(34,197,94,0.2); color: #86efac; border-color: rgba(34,197,94,0.3); }

  .status-pill.err { background: rgba(239,68,68,0.2); color: #fca5a5; border-color: rgba(239,68,68,0.3); }

  .dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }

  .refresh-btn { background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.2); color: #fff; padding: 6px 14px; border-radius: 8px; cursor: pointer; font-size: 13px; }

  .refresh-btn:hover { background: rgba(255,255,255,0.2); }

  .container { max-width: 1200px; margin: 0 auto; padding: 1.5rem; }

  .metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; margin-bottom: 1.5rem; }

  .metric { background: #fff; border-radius: 12px; padding: 1.25rem; border: 1px solid #e8e8e4; }

  .metric-label { font-size: 12px; color: #888; margin-bottom: 6px; text-transform: uppercase; letter-spacing: 0.05em; }

  .metric-value { font-size: 32px; font-weight: 600; color: #130e3d; }

  .metric-sub { font-size: 12px; color: #aaa; margin-top: 3px; }

  .tabs { display: flex; gap: 4px; margin-bottom: 1.25rem; background: #fff; border-radius: 10px; padding: 4px; border: 1px solid #e8e8e4; width: fit-content; }

  .tab { padding: 7px 18px; border-radius: 7px; border: none; background: transparent; font-size: 13px; font-weight: 500; color: #888; cursor: pointer; }

  .tab.on { background: #130e3d; color: #fff; }

  .search { width: 100%; padding: 10px 14px; border: 1px solid #e8e8e4; border-radius: 10px; font-size: 14px; background: #fff; margin-bottom: 1rem; outline: none; }

  .search:focus { border-color: #130e3d; }

  .section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.75rem; }

  .section-title { font-size: 13px; font-weight: 600; color: #555; text-transform: uppercase; letter-spacing: 0.05em; }

  .section-count { font-size: 13px; color: #aaa; }

  .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 12px; }

  .card { background: #fff; border: 1px solid #e8e8e4; border-radius: 12px; padding: 1rem 1.25rem; cursor: pointer; transition: border-color 0.15s, box-shadow 0.15s; }

  .card:hover { border-color: #130e3d; box-shadow: 0 2px 12px rgba(19,14,61,0.08); }

  .card-name { font-size: 14px; font-weight: 600; color: #1a1a1a; margin-bottom: 3px; }

  .card-addr { font-size: 13px; color: #888; margin-bottom: 8px; }

  .card-footer { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

  .badge { display: inline-block; font-size: 11px; font-weight: 500; padding: 3px 10px; border-radius: 20px; }

  .badge-active { background: #f0fdf4; color: #16a34a; border: 1px solid #bbf7d0; }

  .badge-inactive { background: #f5f5f5; color: #888; border: 1px solid #e8e8e4; }

  .badge-open { background: #fffbeb; color: #d97706; border: 1px solid #fde68a; }

  .badge-done { background: #f0fdf4; color: #16a34a; border: 1px solid #bbf7d0; }

  .badge-clean { background: #eff6ff; color: #2563eb; border: 1px solid #bfdbfe; }

  .badge-maint { background: #fef2f2; color: #dc2626; border: 1px solid #fecaca; }

  .task-list { display: flex; flex-direction: column; gap: 8px; }

  .task-card { background: #fff; border: 1px solid #e8e8e4; border-radius: 12px; padding: 0.875rem 1.25rem; display: flex; align-items: flex-start; gap: 12px; }

  .task-dot { width: 8px; height: 8px; border-radius: 50%; margin-top: 5px; flex-shrink: 0; }

  .dot-open { background: #f59e0b; }

  .dot-done { background: #22c55e; }

  .task-body { flex: 1; min-width: 0; }

  .task-name { font-size: 14px; font-weight: 500; color: #1a1a1a; margin-bottom: 3px; }

  .task-meta { font-size: 12px; color: #aaa; display: flex; gap: 10px; flex-wrap: wrap; }

  .loader { text-align: center; padding: 3rem; color: #aaa; font-size: 14px; }

  .spinner { display: inline-block; width: 24px; height: 24px; border: 2px solid #e8e8e4; border-top-color: #130e3d; border-radius: 50%; animation: spin 0.8s linear infinite; margin-bottom: 12px; }

  @keyframes spin { to { transform: rotate(360deg); } }

  .error-box { background: #fef2f2; border: 1px solid #fecaca; border-radius: 12px; padding: 1.25rem; color: #dc2626; margin-bottom: 1rem; }

  .error-box strong { display: block; margin-bottom: 4px; }

  .empty { text-align: center; padding: 2rem; color: #aaa; font-size: 14px; background: #fff; border-radius: 12px; border: 1px solid #e8e8e4; }

  .filter-row { display: flex; gap: 6px; margin-bottom: 1rem; flex-wrap: wrap; }

  .filter-btn { font-size: 12px; padding: 5px 12px; border-radius: 8px; border: 1px solid #e8e8e4; background: #fff; color: #888; cursor: pointer; }

  .filter-btn.on { background: #130e3d; color: #fff; border-color: #130e3d; }

  @media (max-width: 600px) {

    .container { padding: 1rem; }

    .metrics { grid-template-columns: repeat(2, 1fr); }

    .grid { grid-template-columns: 1fr; }

  }

</style>

</head>

<body>

<div class="topbar">

  <div class="topbar-logo">Citi<span>Stayz</span> — Breezeway</div>

  <div class="topbar-right">

    <span class="status-pill" id="status-pill"><span class="dot"></span> Connecting…</span>

    <button class="refresh-btn" onclick="boot()">↻ Refresh</button>

  </div>

</div>

<div class="container">

  <div id="metrics-row" class="metrics" style="display:none">

    <div class="metric"><div class="metric-label">Properties</div><div class="metric-value" id="m-props">—</div><div class="metric-sub">total</div></div>

    <div class="metric"><div class="metric-label">Active</div><div class="metric-value" id="m-active">—</div><div class="metric-sub">in portfolio</div></div>

    <div class="metric"><div class="metric-label">Tasks</div><div class="metric-value" id="m-tasks">—</div><div class="metric-sub">total loaded</div></div>

    <div class="metric"><div class="metric-label">Open tasks</div><div class="metric-value" id="m-open">—</div><div class="metric-sub">need attention</div></div>

  </div>

  <div id="main"><div class="loader"><div class="spinner"></div><br>Loading your portfolio…</div></div>

</div>

<script>

const PROXY = 'https://breezeway-proxy.ssingh-d7e.workers.dev';

let allProps = [], allTasks = [], activeTab = 'props', taskFilter = 'all';


async function get(path) {

  const r = await fetch(PROXY + path);

  if (!r.ok) throw new Error('HTTP ' + r.status);

  return r.json();

}


function fdate(d) {

  if (!d) return '';

  try { return new Date(d).toLocaleDateString('en-AU', { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit' }); } catch(e) { return ''; }

}


function isDone(t) {

  const s = (t.status || '').toLowerCase();

  return s.includes('complet') || s.includes('closed') || s.includes('approv') || s.includes('done');

}


function taskType(t) {

  const type = (t.task_type || t.type || t.template_name || t.name || '').toLowerCase();

  if (type.includes('clean') || type.includes('departure')) return 'clean';

  if (type.includes('maint') || type.includes('repair')) return 'maint';

  if (type.includes('inspect') || type.includes('check')) return 'inspect';

  return 'other';

}


function typeBadge(t) {

  const type = taskType(t);

  if (type === 'clean') return '<span class="badge badge-clean">Clean</span>';

  if (type === 'maint') return '<span class="badge badge-maint">Maintenance</span>';

  if (type === 'inspect') return '<span class="badge badge-open">Inspection</span>';

  return '';

}


function renderProps(list) {

  if (!list.length) return '<div class="empty">No properties found</div>';

  return '<div class="grid">' + list.map(p => {

    const addr = [p.address1, p.address2, p.city].filter(Boolean).join(', ');

    const s = p.status === 'active' ? 'badge-active' : 'badge-inactive';

    return `<div class="card">

      <div class="card-name">${p.name || 'Unnamed'}</div>

      <div class="card-addr">${addr}</div>

      <div class="card-footer"><span class="badge ${s}">${p.status || 'active'}</span></div>

    </div>`;

  }).join('') + '</div>';

}


function renderTasks(list) {

  if (!list.length) return '<div class="empty">No tasks found</div>';

  return '<div class="task-list">' + list.map(t => {

    const done = isDone(t);

    const prop = t.property_name || t.home_name || '';

    const due = fdate(t.scheduled_start_time || t.due_date || t.start_time);

    const assignee = t.assignee_name || (t.assignees && t.assignees[0] && t.assignees[0].name) || '';

    return `<div class="task-card">

      <div class="task-dot ${done ? 'dot-done' : 'dot-open'}"></div>

      <div class="task-body">

        <div class="task-name">${t.name || t.title || t.task_type || 'Task'}</div>

        <div class="task-meta">

          ${prop ? '<span>📍 ' + prop + '</span>' : ''}

          ${due ? '<span>🕐 ' + due + '</span>' : ''}

          ${assignee ? '<span>👤 ' + assignee + '</span>' : ''}

        </div>

        <div style="margin-top:6px;display:flex;gap:6px;flex-wrap:wrap">

          ${typeBadge(t)}

          <span class="badge ${done ? 'badge-done' : 'badge-open'}">${t.status || 'open'}</span>

        </div>

      </div>

    </div>`;

  }).join('') + '</div>';

}


function updateMetrics() {

  document.getElementById('metrics-row').style.display = 'grid';

  document.getElementById('m-props').textContent = allProps.length;

  document.getElementById('m-active').textContent = allProps.filter(p => p.status === 'active').length;

  document.getElementById('m-tasks').textContent = allTasks.length;

  document.getElementById('m-open').textContent = allTasks.filter(t => !isDone(t)).length;

}


function renderMain() {

  const main = document.getElementById('main');

  if (activeTab === 'props') {

    main.innerHTML = `

      <div class="tabs">

        <button class="tab on" onclick="switchTab('props')">Properties</button>

        <button class="tab" onclick="switchTab('tasks')">Tasks</button>

      </div>

      <input class="search" type="text" placeholder="Search properties…" oninput="filterProps(this.value)">

      <div class="section-header">

        <span class="section-title">All properties</span>

        <span class="section-count" id="prop-count">${allProps.length} properties</span>

      </div>

      <div id="prop-list">${renderProps(allProps)}</div>

    `;

  } else {

    const filtered = taskFilter === 'all' ? allTasks :

      taskFilter === 'open' ? allTasks.filter(t => !isDone(t)) :

      allTasks.filter(t => taskType(t) === taskFilter);

    main.innerHTML = `

      <div class="tabs">

        <button class="tab" onclick="switchTab('props')">Properties</button>

        <button class="tab on" onclick="switchTab('tasks')">Tasks</button>

      </div>

      <div class="filter-row">

        <button class="filter-btn ${taskFilter==='all'?'on':''}" onclick="setFilter('all')">All</button>

        <button class="filter-btn ${taskFilter==='open'?'on':''}" onclick="setFilter('open')">Open only</button>

        <button class="filter-btn ${taskFilter==='clean'?'on':''}" onclick="setFilter('clean')">Cleans</button>

        <button class="filter-btn ${taskFilter==='maint'?'on':''}" onclick="setFilter('maint')">Maintenance</button>

        <button class="filter-btn ${taskFilter==='inspect'?'on':''}" onclick="setFilter('inspect')">Inspections</button>

      </div>

      <div class="section-header">

        <span class="section-title">Tasks</span>

        <span class="section-count">${filtered.length} tasks</span>

      </div>

      ${renderTasks(filtered)}

    `;

  }

}


function switchTab(t) { activeTab = t; renderMain(); }

function setFilter(f) { taskFilter = f; renderMain(); }


function filterProps(q) {

  const f = allProps.filter(p =>

    (p.name||'').toLowerCase().includes(q.toLowerCase()) ||

    (p.address1||'').toLowerCase().includes(q.toLowerCase()) ||

    (p.city||'').toLowerCase().includes(q.toLowerCase())

  );

  document.getElementById('prop-list').innerHTML = renderProps(f);

  document.getElementById('prop-count').textContent = f.length + ' properties';

}


async function boot() {

  const pill = document.getElementById('status-pill');

  pill.className = 'status-pill';

  pill.innerHTML = '<span class="dot"></span> Connecting…';

  document.getElementById('main').innerHTML = '<div class="loader"><div class="spinner"></div><br>Loading your portfolio…</div>';

  document.getElementById('metrics-row').style.display = 'none';

  try {

    const [pd, td] = await Promise.all([

      get('/public/inventory/v1/property?limit=100'),

      get('/public/inventory/v1/task/?limit=100').catch(() => ({ results: [] }))

    ]);

    allProps = pd.results || pd || [];

    const raw = td.results || td.tasks || td || [];

    allTasks = Array.isArray(raw) ? raw : [];

    pill.className = 'status-pill ok';

    pill.innerHTML = '<span class="dot"></span> Live — ' + new Date().toLocaleTimeString('en-AU', { hour: '2-digit', minute: '2-digit' });

    updateMetrics();

    renderMain();

  } catch(e) {

    pill.className = 'status-pill err';

    pill.innerHTML = '<span class="dot"></span> Error';

    document.getElementById('main').innerHTML = `

      <div class="error-box">

        <strong>Could not connect to Breezeway</strong>

        ${e.message}<br><br>

        If you see "429" — wait 60 seconds and click Refresh.<br>

        If you see "Failed to fetch" — check the proxy is deployed.

      </div>

    `;

  }

}


boot();

</script>

</body>

</html>