← back to Ai Workforce
public/index.html
105 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>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>AI Workforce</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><circle cx='16' cy='16' r='13' fill='%230b0e14' stroke='%2337d67a' stroke-width='2'/><circle cx='16' cy='16' r='4' fill='%2337d67a'/></svg>">
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<header class="topbar">
<div class="brand">
<span class="dot"></span>
<div>
<h1>AI WORKFORCE</h1>
<p class="tag">Your fleet as full-time employees — machines are offices, agents are staff.</p>
</div>
</div>
<div class="topbar-right">
<span id="stamp" class="stamp">loading…</span>
<button id="refresh" class="btn">↻ Refresh</button>
</div>
</header>
<section class="ribbon" id="ribbon"></section>
<section class="panel">
<h2 class="ph">Offices</h2>
<div class="offices" id="offices"></div>
</section>
<div class="two-col">
<section class="panel">
<h2 class="ph">Today's output <span class="muted" id="wins-src"></span></h2>
<div class="wins" id="wins"></div>
</section>
<section class="panel">
<h2 class="ph">Payroll today <span class="muted">(paid-API spend)</span></h2>
<div class="payroll" id="payroll"></div>
</section>
</div>
<section class="panel">
<div class="roster-head">
<h2 class="ph">Staff roster <span class="muted" id="roster-count"></span></h2>
<div class="controls">
<input id="search" class="inp" type="search" placeholder="Search staff…" autocomplete="off">
<select id="dept" class="inp"></select>
<select id="status" class="inp">
<option value="">All status</option>
<option value="working">Working</option>
<option value="idle">Idle / scheduled</option>
<option value="error">Error</option>
<option value="stopped">Stopped</option>
<option value="roster">On roster</option>
</select>
<select id="sort" class="inp">
<option value="status">Sort: Status</option>
<option value="name">Sort: Name A→Z</option>
<option value="dept">Sort: Department</option>
<option value="hired-new">Sort: Newest hired</option>
<option value="hired-old">Sort: Oldest hired</option>
</select>
<label class="density" title="Card density">
<span>▦</span>
<input id="density" type="range" min="2" max="8" value="4">
</label>
</div>
</div>
<div class="grid" id="grid"></div>
</section>
<footer class="foot">
<span>Local prototype · read-only · <code>$0 (local)</code></span>
<span class="muted">Deploy / DNS / public exposure are gated — not taken here.</span>
</footer>
<script src="/app.js"></script>
</body>
</html>