collabcentral/html/sendMessage.html
Joseph B. McQueen 224e853368 Add a cancel button to the compose page
Users can now abandon a draft they don't want to send. Two entry
points, one shared server action.

Backend
- New action on the existing POST /CollabCentral/:app/jobs/:action
  route: /jobs/cancel. Deletes jobs.building[<personId>::<appName>]
  if present, saves jobs.json, returns 200 with { cancelled: true }.
- Idempotent: if there was no building entry (client fires this on
  every cancel click regardless of state), the endpoint no-ops with
  { cancelled: false } and never touches jobs.json. That keeps disk
  writes tied to real state changes instead of every click.
- Wrapped saveConfig in try/catch so a disk-full or permission
  failure returns a clean 500 instead of crashing the request.

Frontend
- New "Cancel" button in the compose form's action row, sits next
  to "Review & send" as a secondary action.
- cancelMessage() checks whether the form has anything in it (text,
  image, CSV, groups, schedule) and only prompts window.confirm() if
  there's actually a draft to lose. Blank-form clicks skip the
  prompt so the button behaves as expected on first load.
- New "Discard" action on the review modal for "wait, actually no"
  decisions after clicking Review. Positioned on the far left of the
  modal footer with margin-right: auto so it's visually separated
  from the affirmative "Keep editing" / "Send" pair — a stray click
  on the way to Send lands on Keep editing, not Discard.
- Extended clearForm() to also clear both VirtualSelect group
  pickers via setValue([]) (previously only text/files/schedule got
  reset, leaving stale group selections after a cancel or send).
- New .btn-danger style: muted red text on a transparent background,
  filled-red hover state. Used for the modal Discard button and
  available for future destructive UI.

Not covered (follow-up if wanted): the preview DM the bot sends to
the sender during /jobs/edit isn't tracked in the building job, so
Cancel doesn't retract that Webex message. Adding server-side
tracking of the preview message id would let cancel call
DELETE /v1/messages/<id> to clean it up.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-01 20:32:50 -04:00

107 lines
5 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">
<div class="fieldLabelRow">
<label for="favGroups">Favorite groups</label>
<span id="favGroupsStatus" class="fieldStatus" role="status" aria-live="polite"></span>
</div>
<select id="favGroups" multiple name="favGroups" placeholder="Favorite groups"
data-silent-initial-value-set="false"></select>
<div id="favGroupsManager" class="chipStrip hidden" aria-label="Manage favorite groups"></div>
<span class="hint">Selecting a group under <em>Additional groups</em> saves it to your favorites. Click the × on a chip to remove that favorite.</span>
</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="cancel" type="button" class="btn btn-secondary" onclick="cancelMessage()">Cancel</button>
<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-danger" onclick="discardMessage()">Discard</button>
<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>