228 lines
10 KiB
Plaintext
228 lines
10 KiB
Plaintext
<%- include("../../../src/web/views/partials/layout-top", { title }) %>
|
||
<link rel="stylesheet" href="/plugins/throne_wishlist/assets/admin.css?v=<%= assetVersion %>" />
|
||
|
||
<% const destinationMap = new Map(destinations.map((item) => [item.platform, item])); %>
|
||
<% const templateMap = new Map(templates.map((item) => [item.event_type + ":" + item.platform, item])); %>
|
||
<% const statusMap = new Map(platformStatus.map((item) => [item.id, item])); %>
|
||
|
||
<header class="throne-header">
|
||
<div>
|
||
<h1>Throne Wishlist</h1>
|
||
<p>Verified wishlist events and cross-platform notifications.</p>
|
||
</div>
|
||
<button type="button" class="button subtle" data-debug-open>Debug</button>
|
||
</header>
|
||
|
||
<section class="card">
|
||
<div class="section-header">
|
||
<div>
|
||
<h2>Diagnostics</h2>
|
||
<p class="hint">Core availability and public delivery readiness.</p>
|
||
</div>
|
||
</div>
|
||
<div class="diagnostic-grid">
|
||
<div><span>Webhook framework</span><strong><%= webhookAvailable ? "Available" : "Unavailable" %></strong></div>
|
||
<div><span>Public base URL</span><strong><%= publicBaseUrl %></strong></div>
|
||
<% platformStatus.forEach((platform) => { %>
|
||
<div><span><%= platform.label %></span><strong><%= platform.sendAvailable ? "Ready" : "Unavailable" %></strong><small><%= platform.diagnostic %></small></div>
|
||
<% }) %>
|
||
</div>
|
||
<% if (publicBaseUrlWarning) { %>
|
||
<div class="callout"><%= publicBaseUrlWarning %></div>
|
||
<% } %>
|
||
<% if (!webhookAvailable) { %>
|
||
<div class="callout">Install and restart with the core webhook framework patch before creating endpoints.</div>
|
||
<% } %>
|
||
</section>
|
||
|
||
<section class="card">
|
||
<div class="section-header">
|
||
<div>
|
||
<h2>Endpoints</h2>
|
||
<p class="hint">Create multiple subscriber URLs and paste them into Throne.</p>
|
||
</div>
|
||
</div>
|
||
<form method="post" action="/plugins/throne_wishlist/endpoints/create" class="inline-form">
|
||
<div class="field">
|
||
<label for="throne-identifier">Identifier</label>
|
||
<input id="throne-identifier" name="identifier" required placeholder="cozy-carnage" />
|
||
</div>
|
||
<button type="submit" class="button" <%= webhookAvailable ? "" : "disabled" %>>Create endpoint</button>
|
||
</form>
|
||
|
||
<% if (!endpoints.length) { %>
|
||
<p>No Throne endpoints configured.</p>
|
||
<% } else { %>
|
||
<div class="table-wrap">
|
||
<table class="table throne-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Identifier</th>
|
||
<th>UUID</th>
|
||
<th>Link</th>
|
||
<th>Last payload</th>
|
||
<th>Actions</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<% endpoints.forEach((endpoint) => { %>
|
||
<tr>
|
||
<td><strong><%= endpoint.identifier %></strong></td>
|
||
<td><code><%= endpoint.uuid %></code></td>
|
||
<td>
|
||
<div class="copy-row">
|
||
<input value="<%= endpoint.publicUrl %>" readonly data-copy-source="<%= endpoint.id %>" aria-label="Webhook URL for <%= endpoint.identifier %>" />
|
||
<button type="button" class="icon-button" data-copy-button="<%= endpoint.id %>" title="Copy webhook URL" aria-label="Copy webhook URL">
|
||
<span aria-hidden="true">⧉</span>
|
||
</button>
|
||
</div>
|
||
<small class="copy-status" data-copy-status="<%= endpoint.id %>" aria-live="polite"></small>
|
||
</td>
|
||
<td>
|
||
<% if (endpoint.last_payload_preview) { %>
|
||
<% let preview = {}; try { preview = JSON.parse(endpoint.last_payload_preview); } catch {} %>
|
||
<strong><%= preview.event_type || "unknown" %></strong>
|
||
<small><%= preview.event_id || "No event id" %></small>
|
||
<small><%= preview.authentic ? "Authentic" : "Rejected" %> · <%= formatTimestamp(endpoint.last_payload_at) %></small>
|
||
<% } else { %>
|
||
<span>Never</span>
|
||
<% } %>
|
||
</td>
|
||
<td>
|
||
<div class="action-row">
|
||
<button type="button" class="button subtle" data-confirm-open data-confirm-title="Renew endpoint?" data-confirm-text="The current Throne URL will stop working immediately." data-confirm-action="/plugins/throne_wishlist/endpoints/<%= endpoint.id %>/renew">Renew</button>
|
||
<button type="button" class="button subtle danger" data-confirm-open data-confirm-title="Remove endpoint?" data-confirm-text="This endpoint will stop accepting Throne payloads." data-confirm-action="/plugins/throne_wishlist/endpoints/<%= endpoint.id %>/remove">Remove</button>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
<% }) %>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<% } %>
|
||
</section>
|
||
|
||
<section class="card">
|
||
<div class="section-header">
|
||
<div>
|
||
<h2>Platform Destinations</h2>
|
||
<p class="hint">Only destinations with a working Lumi send capability can be enabled.</p>
|
||
</div>
|
||
</div>
|
||
<form method="post" action="/plugins/throne_wishlist/destinations" class="destination-grid">
|
||
<% const availablePlatforms = platformStatus.filter((platform) => platform.sendAvailable); %>
|
||
<% if (!availablePlatforms.length) { %>
|
||
<p>No active platform sender is currently available.</p>
|
||
<% } %>
|
||
<% availablePlatforms.forEach((platform) => { const destination = destinationMap.get(platform.id); %>
|
||
<fieldset class="destination-panel">
|
||
<legend><%= platform.label %></legend>
|
||
<label class="switch">
|
||
<input type="checkbox" class="switch-input" name="enabled_platforms" value="<%= platform.id %>" <%= destination?.enabled ? "checked" : "" %> />
|
||
<span class="switch-track" aria-hidden="true"></span>
|
||
<span class="switch-text"><%= destination?.enabled ? "Enabled" : "Disabled" %></span>
|
||
</label>
|
||
<% if (platform.id === "discord") { %>
|
||
<div class="field">
|
||
<label for="discord-destination">Text channel</label>
|
||
<select id="discord-destination" name="discord_destination_id">
|
||
<option value="">Select a regular text channel</option>
|
||
<% discordChannels.forEach((channel) => { %>
|
||
<option value="<%= channel.id %>" <%= destination?.destination_id === channel.id ? "selected" : "" %>><%= channel.label %></option>
|
||
<% }) %>
|
||
</select>
|
||
</div>
|
||
<% } %>
|
||
<small><%= platform.diagnostic %></small>
|
||
</fieldset>
|
||
<% }) %>
|
||
<div class="field full">
|
||
<button type="submit" class="button">Save destinations</button>
|
||
</div>
|
||
</form>
|
||
</section>
|
||
|
||
<section class="card">
|
||
<div class="section-header">
|
||
<div>
|
||
<h2>Event Messages</h2>
|
||
<p class="hint">One message per event and platform. Unknown placeholders remain unchanged.</p>
|
||
</div>
|
||
</div>
|
||
<details class="placeholder-list">
|
||
<summary>Available placeholders</summary>
|
||
<div>
|
||
<% placeholders.forEach((placeholder) => { %><code>{<%= placeholder %>}</code> <% }) %>
|
||
</div>
|
||
</details>
|
||
<div class="event-sections">
|
||
<% eventTypes.forEach((eventType) => { %>
|
||
<section class="event-section">
|
||
<h3><%= eventType %></h3>
|
||
<div class="template-grid">
|
||
<% if (!activePlatforms.length) { %>
|
||
<p>No active platform templates are available.</p>
|
||
<% } %>
|
||
<% activePlatforms.forEach((platform) => { const template = templateMap.get(eventType + ":" + platform); const status = statusMap.get(platform); %>
|
||
<form method="post" action="/plugins/throne_wishlist/templates" class="template-panel">
|
||
<input type="hidden" name="event_type" value="<%= eventType %>" />
|
||
<input type="hidden" name="platform" value="<%= platform %>" />
|
||
<div class="template-heading">
|
||
<strong><%= status?.label || platform %></strong>
|
||
<label class="switch">
|
||
<input type="checkbox" class="switch-input" name="enabled" <%= template?.enabled ? "checked" : "" %> />
|
||
<span class="switch-track" aria-hidden="true"></span>
|
||
<span class="switch-text"><%= template?.enabled ? "On" : "Off" %></span>
|
||
</label>
|
||
</div>
|
||
<label for="template-<%= eventType %>-<%= platform %>">Message</label>
|
||
<textarea id="template-<%= eventType %>-<%= platform %>" name="template" rows="4"><%= template?.template || "" %></textarea>
|
||
<button type="submit" class="button subtle">Save <%= status?.label || platform %></button>
|
||
</form>
|
||
<% }) %>
|
||
</div>
|
||
</section>
|
||
<% }) %>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="modal-backdrop" data-confirm-modal aria-hidden="true">
|
||
<div class="modal" role="dialog" aria-modal="true" aria-labelledby="confirm-title">
|
||
<div class="modal-header">
|
||
<h3 id="confirm-title" data-confirm-heading>Confirm action</h3>
|
||
<button type="button" class="icon-button" data-confirm-close aria-label="Close confirmation">×</button>
|
||
</div>
|
||
<p data-confirm-description></p>
|
||
<form method="post" data-confirm-form>
|
||
<div class="modal-actions">
|
||
<button type="button" class="button subtle" data-confirm-close>Cancel</button>
|
||
<button type="submit" class="button danger" data-confirm-submit disabled>Confirm in 3</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="modal-backdrop" data-debug-modal aria-hidden="true">
|
||
<div class="modal debug-modal" role="dialog" aria-modal="true" aria-labelledby="debug-title">
|
||
<div class="modal-header">
|
||
<div>
|
||
<h3 id="debug-title">Live Throne Payloads</h3>
|
||
<p class="hint" data-debug-status>Starting debug session…</p>
|
||
</div>
|
||
<button type="button" class="icon-button" data-debug-close aria-label="Close debug viewer">×</button>
|
||
</div>
|
||
<div class="debug-toolbar">
|
||
<button type="button" class="icon-button" data-debug-prev aria-label="Previous payload">←</button>
|
||
<span data-debug-page>0 / 0</span>
|
||
<input type="range" min="1" max="1" value="1" data-debug-slider aria-label="Payload page" />
|
||
<button type="button" class="icon-button" data-debug-next aria-label="Next payload">→</button>
|
||
</div>
|
||
<div class="debug-summary" data-debug-summary>No payloads received during this session.</div>
|
||
<pre class="debug-json" data-debug-json>{}</pre>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="/plugins/throne_wishlist/assets/admin.js?v=<%= assetVersion %>"></script>
|
||
<script src="/plugins/throne_wishlist/assets/debug-modal.js?v=<%= assetVersion %>"></script>
|
||
<%- include("../../../src/web/views/partials/layout-bottom") %>
|