← back to SDCC Stories

public/index.html

145 lines

<!DOCTYPE html>
<html lang="en">
<head>
<script>
// Credential-safe fetch guard (fleet drop-in) — if this page is opened with
// credentials in the URL (a saved bookmark / Chrome-remembered basic-auth:
// https://user:pass@host/…), the browser poisons document.baseURI, so any
// relative or root-relative fetch('/api/…') throws "Request cannot be constructed
// from a URL that includes credentials" and callers silently fall to an empty
// state. Resolve every non-absolute request URL against the credential-free
// location instead of baseURI. Placed first so it wraps window.fetch before any
// app script runs. Ref: creds-in-url-fetch-guard-fleet-pattern.
(function () {
  var _fetch = window.fetch.bind(window);
  var cleanBase = function () { return location.origin + location.pathname; };
  window.fetch = function (input, init) {
    try {
      if (typeof input === 'string' && !/^[a-z]+:\/\//i.test(input) && input.indexOf('//') !== 0) {
        input = new URL(input, cleanBase()).href;
      } else if (input instanceof Request && !/^[a-z]+:\/\//i.test(input.url)) {
        input = new Request(new URL(input.url, cleanBase()).href, input);
      }
    } catch (_) { /* fall through to native */ }
    return _fetch(input, init);
  };
})();
</script>
    <link rel="icon" type="image/svg+xml" href="/favicon.svg">

  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>SDCC Story Engine</title>
  <link rel="stylesheet" href="/css/styles.css">
</head>
<body>
  <header>
    <div class="header-left">
      <h1>SDCC Story Engine</h1>
      <span class="subtitle">Borrower Stories &rarr; Social Impact</span>
    </div>
    <div class="header-center">
      <div class="global-search-wrap">
        <input
          type="text"
          id="global-search-input"
          class="global-search-input"
          placeholder="Search candidates, stories, settings… (⌘K)"
          autocomplete="off"
          onfocus="openGlobalSearch()"
          oninput="onGlobalSearchInput(event)"
          onkeydown="onGlobalSearchKeyDown(event)"
        >
        <div id="global-search-results" class="global-search-results" hidden></div>
      </div>
    </div>
    <div class="header-right">
      <span id="ig-status" class="badge">Instagram: Checking...</span>
      <button class="btn btn-sm" onclick="connectInstagram()">Connect IG</button>
    </div>
  </header>

  <div class="stats-bar" id="stats-bar"></div>

  <nav class="tabs">
    <button class="tab active" data-tab="candidates" onclick="switchTab('candidates')">Candidates</button>
    <button class="tab" data-tab="stories" onclick="switchTab('stories')">Stories</button>
    <button class="tab" data-tab="calendar" onclick="switchTab('calendar')">Calendar</button>
    <button class="tab" data-tab="heatmap" onclick="switchTab('heatmap')">Heat Map</button>
    <button class="tab" data-tab="ideas" onclick="switchTab('ideas')">Content Ideas</button>
    <button class="tab" data-tab="settings" onclick="switchTab('settings')">Settings</button>
  </nav>

  <!-- Candidates Tab -->
  <div class="tab-content active" id="tab-candidates">
    <div class="controls">
      <div class="filter-group">
        <label>Tier:</label>
        <select id="filter-tier" onchange="loadCandidates()">
          <option value="">All</option>
          <option value="A">A — Press + Social + Lawmakers</option>
          <option value="B">B — Social + Lawmakers</option>
          <option value="C">C — Social Only</option>
          <option value="D">D — Needs Enrichment</option>
        </select>
      </div>
      <div class="filter-group">
        <input type="text" id="filter-search" placeholder="Search name, zip, story..." onkeyup="debounceSearch()">
      </div>
      <button class="btn btn-action" onclick="loadCandidates()">Search</button>
      <button class="btn btn-resource" onclick="refreshData()">Refresh from Sheet</button>
    </div>
    <div class="candidate-count" id="candidate-count"></div>
    <div class="candidate-list" id="candidate-list"></div>
    <div class="pagination" id="pagination"></div>
  </div>

  <!-- Stories Tab -->
  <div class="tab-content" id="tab-stories">
    <div class="controls">
      <select id="story-filter" onchange="loadStories()">
        <option value="">All Stories</option>
        <option value="draft">Drafts</option>
        <option value="scheduled">Scheduled</option>
        <option value="posted">Posted</option>
      </select>
    </div>
    <div class="story-list" id="story-list"></div>
  </div>

  <!-- Calendar Tab -->
  <div class="tab-content" id="tab-calendar">
    <div class="controls">
      <button class="btn btn-action" onclick="autoFillCalendar()">Auto-Fill 7 Days</button>
      <span class="hint">Picks top Tier A/B candidates, generates stories, schedules 2x daily (9am + 6pm PT)</span>
    </div>
    <div class="calendar-grid" id="calendar-grid"></div>
  </div>

  <!-- Heat Map Tab -->
  <div class="tab-content" id="tab-heatmap">
    <div class="heatmap-container" id="heatmap-container"></div>
  </div>

  <!-- Content Ideas Tab -->
  <div class="tab-content" id="tab-ideas">
    <div class="ideas-grid" id="ideas-grid"></div>
  </div>

  <!-- Settings Tab -->
  <div class="tab-content" id="tab-settings">
    <div class="settings-form" id="settings-form"></div>
  </div>

  <!-- Candidate Detail Modal -->
  <div class="modal" id="candidate-modal">
    <div class="modal-content">
      <button class="modal-close" onclick="closeModal()">&times;</button>
      <div id="candidate-detail"></div>
    </div>
  </div>

  <script src="/js/app.js"></script>
</body>
</html>