pediatric-ai-scribe-v3/public/components/extensions.html
Daniel d859c8c5a9 feat(extensions): personal Pagers & Extensions directory with soft-delete
New top-level tab positioned after Physical Exam Guide. Per-user
directory of hospital phone extensions and pagers — grouped by location
then type, searchable, soft-deleted.

Data:
- New table user_phone_extensions (id, user_id, location, name, number,
  type CHECK (extension|pager), notes, trashed_at, timestamps).
  Partial indexes on active vs trashed rows for fast filtering.
- Not PHI — hospital internal phone directory. Plaintext.

API (all user-scoped, all params validated):
- GET    /api/extensions?trash=1&q=text  — list active or trash, optional search
- POST   /api/extensions                  — create
- PUT    /api/extensions/:id              — update (requires all three core fields)
- DELETE /api/extensions/:id              — soft-delete (sets trashed_at)
- POST   /api/extensions/:id/restore      — un-trash
- DELETE /api/extensions/:id/purge        — hard-delete (only if trashed)

All :id params parsed + validated (positive integer) before query.
All queries parameterized, every WHERE includes user_id scoping.

UI (public/js/extensions.js + components/extensions.html):
- Search bar with 200ms debounce, server-side LIKE on location/name/number/notes
- Add button expands inline form — location (with datalist of existing
  locations for autocomplete), name/dept, number, type, optional notes
- Each entry renders as a card: big monospace number, dept, type badge,
  edit + delete inline
- Grouped by location → type (Extensions / Pagers subheaders)
- Trash view: toggle shows trashed items with Restore + Purge actions
- Trash count badge on the Trash button updates after every delete/restore
- Delete requires confirm() dialog, then soft-delete (easy to undo)
- Purge from trash requires a second confirm() ("cannot be undone")
- Esc closes the form; form resets between Add and Edit
2026-04-22 18:54:26 +02:00

60 lines
3.6 KiB
HTML

<div class="module-header">
<h2><i class="fas fa-phone-volume"></i> Pagers &amp; Extensions</h2>
<p>Your personal directory of hospital phone extensions and pagers — searchable, edit-friendly, soft-delete</p>
</div>
<div class="card ext-toolbar">
<div style="display:flex;gap:10px;align-items:center;padding:12px 16px;flex-wrap:wrap;">
<div style="flex:1;min-width:220px;position:relative;">
<i class="fas fa-search" style="position:absolute;left:10px;top:50%;transform:translateY(-50%);color:var(--g400);font-size:13px;"></i>
<input type="search" id="ext-search" placeholder="Search by location, name, number..." autocomplete="off"
style="width:100%;padding:8px 10px 8px 32px;font-size:14px;border:1px solid var(--g300);border-radius:8px;">
</div>
<button id="ext-add-btn" class="btn-sm btn-primary"><i class="fas fa-plus"></i> Add</button>
<button id="ext-trash-btn" class="btn-sm btn-ghost"><i class="fas fa-trash-can"></i> Trash <span id="ext-trash-count" style="color:var(--g500);font-size:11px;"></span></button>
</div>
<div id="ext-form-wrap" class="hidden" style="border-top:1px solid var(--g100);padding:14px 16px;background:var(--g50);">
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;">
<div class="demo-field">
<label>Location</label>
<input type="text" id="ext-location" list="ext-location-list" placeholder="e.g., Main Hospital" maxlength="120">
<datalist id="ext-location-list"></datalist>
</div>
<div class="demo-field">
<label>Name / Department</label>
<input type="text" id="ext-name" placeholder="e.g., Nursery, 5B, On-call" maxlength="120">
</div>
<div class="demo-field">
<label>Number</label>
<input type="text" id="ext-number" placeholder="e.g., 5866" maxlength="40">
</div>
<div class="demo-field">
<label>Type</label>
<select id="ext-type">
<option value="extension">Extension</option>
<option value="pager">Pager</option>
</select>
</div>
</div>
<div style="margin-top:8px;">
<div class="demo-field">
<label>Notes (optional)</label>
<input type="text" id="ext-notes" placeholder="e.g., After hours: 5867" maxlength="500">
</div>
</div>
<div style="margin-top:10px;display:flex;gap:6px;">
<button id="ext-save-btn" class="btn-sm btn-primary"><i class="fas fa-floppy-disk"></i> Save</button>
<button id="ext-cancel-btn" class="btn-sm btn-ghost">Cancel</button>
<span id="ext-form-status" style="font-size:12px;margin-left:6px;color:var(--g500);align-self:center;"></span>
</div>
</div>
</div>
<div id="ext-mode-banner" class="hidden" style="margin:10px 0;padding:8px 12px;background:#fef3c7;border-left:4px solid #f59e0b;border-radius:6px;font-size:13px;color:#78350f;">
<i class="fas fa-trash-can"></i> Viewing trash. <button id="ext-back-active" class="btn-sm btn-ghost" style="margin-left:8px;">Back to active</button>
</div>
<div id="ext-list" style="margin-top:10px;">
<p style="text-align:center;color:#9ca3af;padding:40px;">Loading...</p>
</div>