collabcentral/html/sendMessage.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

100 lines
4.3 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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.jsdelivr.net/npm/easymde/dist/easymde.min.css">
<link rel="stylesheet" href="js/virtual-select.min.css">
<script src="js/virtual-select.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/easymde/dist/easymde.min.js"></script>
</head>
<body data-page="send" data-title-suffix="Send Message">
<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="formPanel" class="container hidden">
<div class="card">
<h1 class="pageTitle">Compose a message</h1>
<p class="pageSubtitle">Pick your recipients, write the message, and send now or schedule it for later.</p>
<form id="sendMessage" enctype="multipart/form-data">
<div class="field">
<label for="my-text-area">Message</label>
<textarea id="my-text-area"></textarea>
</div>
<div class="field">
<label for="uploadImage">Attach image (optional)</label>
<input type="file" id="uploadImage" accept="image/*" name="uploadImage" onchange="showImagePreview(event);">
<img id="file-ip-1-preview" alt="">
</div>
<div class="field">
<label for="favGroups">Favorite groups</label>
<select id="favGroups" multiple name="favGroups" placeholder="Favorite groups"
data-silent-initial-value-set="false"></select>
</div>
<div class="field">
<label for="newGroups">Additional groups</label>
<select id="newGroups" multiple name="newGroups" placeholder="Search all groups"
data-silent-initial-value-set="false"></select>
</div>
<div class="field">
<label for="uploadCSV">Upload CSV of recipients (optional)</label>
<input type="file" id="uploadCSV" name="uploadCSV" accept="text/csv">
<span class="hint">CSV of Webex person IDs or emails to add to the recipient list.</span>
</div>
<div class="field">
<label for="scheduledFor">Schedule for (leave blank to send immediately)</label>
<input type="datetime-local" id="scheduledFor" name="scheduledFor">
</div>
<div class="formActions">
<button id="submit" type="submit" class="btn btn-primary">Review &amp; send</button>
</div>
</form>
</div>
</div>
<section class="modal hidden" aria-modal="true" role="dialog">
<button class="modal__close" onclick="continueEditing()" aria-label="Close">×</button>
<h3>Ready to send?</h3>
<p id="modalMessage">This message will be sent.</p>
<div class="modal__actions">
<button class="btn btn-secondary" onclick="continueEditing()">Keep editing</button>
<button class="btn btn-primary" onclick="submit()">Send</button>
</div>
</section>
<div class="overlay hidden"></div>
<script src="js/app.js"></script>
<script src="sendMessage.js"></script>
</body>
</html>