collabcentral/html/monitorJobs.html
Joseph B. McQueen 2984e8e851 Phase 9: rebuild frontend on a shared layout + fix Monitor Jobs race
The nav link race:
- Every page's header used `<a id="jobLink" href=''>` and the real
  destination was only assigned later, after /info returned. An empty
  href resolves to the current document URL, so clicking "Monitor Jobs"
  on sendMessage before /info completed silently reloaded sendMessage.
- Fixed structurally: nav links now use static relative hrefs baked
  into the HTML ("./sendMessage.html", "./monitorJobs.html"), so the
  destination is correct the moment the DOM parses.

Shared UI:
- New html/css/app.css: design tokens (palette, radius, shadow, font),
  sticky compact top header (bot avatar + label on the left, nav pills
  in the middle, current user on the right, active-page highlight via
  aria-current), card containers, form styling with focus rings,
  DataTables theme overrides, status pills, modal, and responsive
  breakpoints.
- New html/js/app.js: shared browser bootstrap. Parses appName from
  the URL, redirects to OAuth if the id cookie is missing, fetches
  /info once, populates the header, applies aria-current to the
  active nav link, and invokes a per-page onReady callback with
  { info, appName }. Also exports getCookie, escapeHtml, and formatDate
  helpers so each page stops shipping its own copy.

Per-page rewrites:
- sendMessage.html/.js: form now lives in a card, image preview only
  shows when a file is attached, EasyMDE + VirtualSelect styled to
  match the theme, submit is a primary button, confirmation modal
  redesigned. All bootstrap code deleted (delegated to app.js).
- monitorJobs.html/.js: three cards (Running / Scheduled / Completed)
  with themed DataTables. Completed table sorts by start time desc,
  paginates, and searches; message column truncates HTML previews to
  ~120 chars. Empty-state text per table. `jobId` and running-row
  "view" links go to jobDetail via safe relative URLs.
- jobDetail.html/.js: same shared header + card layout; summary grid,
  message preview, and recipient table styled to match the new
  palette.

Sanity checks:
- All 43 helper tests still pass.
- Server boots cleanly on port 3001.
- Curl of sendMessage/monitorJobs/jobDetail all return 200 with the
  shared header markup.
- /CollabCentral/:app/css/app.css and /CollabCentral/:app/js/app.js
  both serve 200 (shared static mount is per-bot as expected).
- No local href in any page is empty; every nav target resolves at
  parse time.
- /info and requireBot 404 gate unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-01 19:36:36 -04:00

94 lines
3.5 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>CollabCentral</title>
<link id="appFavicon" rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="css/app.css">
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/jquery.dataTables.css">
<script src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.js"></script>
</head>
<body data-page="monitor" data-title-suffix="Monitor Jobs">
<header class="appHeader">
<div class="appHeader__inner">
<div class="appHeader__brand">
<div id="appAvatar" class="appHeader__avatar"></div>
<div id="appLabel" class="appHeader__label">Loading…</div>
</div>
<nav class="appHeader__nav">
<a href="./sendMessage.html" data-nav="send">Send Message</a>
<a href="./monitorJobs.html" data-nav="monitor">Monitor Jobs</a>
</nav>
<div class="appHeader__spacer"></div>
<div id="appUser" class="appHeader__user"></div>
</div>
</header>
<div id="unauthorizedPanel" class="panel hidden">
<h2>You don't have access to this bot</h2>
<p>Your account isn't on the authorized list for this bot. If you believe this is a mistake, contact the bot's administrator.</p>
</div>
<div id="jobsPanel" class="container hidden">
<div class="card">
<h2 class="pageTitle" style="font-size: 1.05rem;">Running</h2>
<p class="pageSubtitle">Jobs currently sending.</p>
<table id="runningJobs" class="dataTable">
<thead>
<tr>
<th>Submitter</th>
<th>App</th>
<th>Start time</th>
<th>Total recipients</th>
<th>Completed</th>
</tr>
</thead>
</table>
</div>
<div class="card">
<h2 class="pageTitle" style="font-size: 1.05rem;">Scheduled</h2>
<p class="pageSubtitle">Jobs waiting to fire.</p>
<table id="scheduledJobs" class="dataTable">
<thead>
<tr>
<th>Submitter</th>
<th>App</th>
<th>Message</th>
<th>Recipients</th>
<th>Scheduled for</th>
</tr>
</thead>
</table>
</div>
<div class="card">
<h2 class="pageTitle" style="font-size: 1.05rem;">Completed</h2>
<p class="pageSubtitle">Jobs retained for 30 days. Click a job ID to open its detail view.</p>
<table id="completedJobs" class="dataTable">
<thead>
<tr>
<th>Job</th>
<th>Submitter</th>
<th>App</th>
<th>Started</th>
<th>Completed</th>
<th>Total time</th>
<th>Delivered</th>
<th>Success rate</th>
</tr>
</thead>
</table>
</div>
</div>
<script src="js/app.js"></script>
<script src="monitorJobs.js"></script>
</body>
</html>