Release v2.4.2
This commit is contained in:
@@ -1,102 +0,0 @@
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h2>Add API source</h2>
|
||||
<p>Adjust the endpoint or refresh cadence for this API source.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-outline card-primary admin-form-card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">API source details</h3>
|
||||
</div>
|
||||
<form id="api-source-form" method="post" action="/data-sources/api-sources" data-async-save data-async-save-close-url="/data-sources/api-sources" data-async-save-new-url="/data-sources/api-sources/new">
|
||||
<div class="card-body">
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-12 col-lg-6">
|
||||
<label for="api-source-name" class="form-label">Name</label>
|
||||
<input id="api-source-name" name="name" class="form-control" value="{{apiSource.name}}" required />
|
||||
</div>
|
||||
<div class="col-12 col-lg-6">
|
||||
<label for="api-source-url" class="form-label">API URL</label>
|
||||
<input id="api-source-url" name="api_url" type="url" class="form-control" value="{{apiSource.apiUrl}}" placeholder="https://example.com/api.json" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<h4 class="api-source-section-heading">Access and parsing</h4>
|
||||
<div class="row g-3 align-items-end">
|
||||
<div class="col-12 col-md-4">
|
||||
<label for="api-source-auth-method" class="form-label">Auth method</label>
|
||||
<select id="api-source-auth-method" name="auth_method" class="form-select" data-api-source-auth-method>
|
||||
<option value="none" {{#if (eq apiSource.authMethod 'none')}}selected{{/if}}>None</option>
|
||||
<option value="basic" {{#if (eq apiSource.authMethod 'basic')}}selected{{/if}}>Basic</option>
|
||||
<option value="bearer" {{#if (eq apiSource.authMethod 'bearer')}}selected{{/if}}>Bearer token</option>
|
||||
<option value="api_key_header" {{#if (eq apiSource.authMethod 'api_key_header')}}selected{{/if}}>API key header</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-12 col-md-8">
|
||||
<label for="api-source-items-path" class="form-label">Items path</label>
|
||||
<input id="api-source-items-path" name="items_path" class="form-control" value="{{apiSource.itemsPath}}" placeholder="items, results.data, or another array path" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-2">
|
||||
<div class="col-12 col-md-8 offset-md-4">
|
||||
<div class="form-text">Optional. Use dot notation to point at the array this source should iterate through.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-4" data-api-source-auth-details-section>
|
||||
<h4 class="api-source-section-heading">Authentication details</h4>
|
||||
<div class="row g-3 mb-3" data-api-source-auth-panel="basic" hidden>
|
||||
<div class="col-12 col-md-6">
|
||||
<label for="api-source-auth-username" class="form-label">Username</label>
|
||||
<input id="api-source-auth-username" name="auth_username" class="form-control" value="{{apiSource.authUsername}}" autocomplete="username" />
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<label for="api-source-auth-password" class="form-label">Password</label>
|
||||
<input id="api-source-auth-password" name="auth_password" type="password" class="form-control" value="{{apiSource.authPassword}}" autocomplete="current-password" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-3 mb-3" data-api-source-auth-panel="bearer" hidden>
|
||||
<div class="col-12">
|
||||
<label for="api-source-auth-bearer-token" class="form-label">Bearer token</label>
|
||||
<input id="api-source-auth-bearer-token" name="auth_bearer_token" type="password" class="form-control" value="{{apiSource.authBearerToken}}" autocomplete="off" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-3 mb-3" data-api-source-auth-panel="api_key_header" hidden>
|
||||
<div class="col-12 col-md-5">
|
||||
<label for="api-source-auth-header-name" class="form-label">Header name</label>
|
||||
<input id="api-source-auth-header-name" name="auth_header_name" class="form-control" value="{{apiSource.authHeaderName}}" placeholder="X-API-Key" />
|
||||
</div>
|
||||
<div class="col-12 col-md-7">
|
||||
<label for="api-source-auth-header-value" class="form-label">Header value</label>
|
||||
<input id="api-source-auth-header-value" name="auth_header_value" class="form-control" value="{{apiSource.authHeaderValue}}" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="api-source-section-heading">Refresh</h4>
|
||||
<div class="row g-3">
|
||||
<div class="col-12 col-md-4">
|
||||
<label for="api-source-interval" class="form-label">Update interval</label>
|
||||
<input id="api-source-interval" name="update_interval_value" type="number" min="1" class="form-control" value="{{apiSource.updateIntervalValue}}" required />
|
||||
</div>
|
||||
<div class="col-12 col-md-4">
|
||||
<label for="api-source-interval-unit" class="form-label">Unit</label>
|
||||
<select id="api-source-interval-unit" name="update_interval_unit" class="form-select">
|
||||
<option value="seconds" {{#if (eq apiSource.updateIntervalUnit 'seconds')}}selected{{/if}}>Seconds</option>
|
||||
<option value="minutes" {{#if (eq apiSource.updateIntervalUnit 'minutes')}}selected{{/if}}>Minutes</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer d-flex justify-content-end">
|
||||
<div class="btn-group" role="group" aria-label="API source actions">
|
||||
{{{saveActionButtons formId="api-source-form" saveLabel="Save" saveAndCloseLabel="Save and Close" saveAndNewLabel="Save and New" saveAndCloseValue="close" saveAndNewValue="new"}}}
|
||||
<a class="btn btn-warning" href="/data-sources/api-sources" data-confirm-unsaved="You have unsaved changes. Leave this page?">Cancel</a>
|
||||
<button type="submit" class="btn btn-outline-danger" disabled>Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/assets/js/data-sources/api-source-form.js?v={{appVersion}}"></script>
|
||||
</form>
|
||||
</div>
|
||||
+26
-38
@@ -1,26 +1,24 @@
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h2>Edit API source - {{apiSource.name}}</h2>
|
||||
<p>Adjust the endpoint or refresh cadence and review the latest JSON response.</p>
|
||||
<h2>{{#if isEdit}}Edit API source - {{apiSource.name}}{{else}}Add API source{{/if}}</h2>
|
||||
<p>{{#if isEdit}}Adjust the endpoint or refresh cadence and review the latest JSON response.{{else}}Adjust the endpoint or refresh cadence for this API source.{{/if}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-outline card-primary admin-form-card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">API source details</h3>
|
||||
</div>
|
||||
<form id="api-source-form" method="post" action="/data-sources/api-sources/{{apiSource.id}}" data-async-save data-async-save-refresh-target="#api-source-response-panel" data-async-save-refresh-state-url="/data-sources/api-sources/{{apiSource.id}}/state" data-async-save-close-url="/data-sources/api-sources" data-async-save-new-url="/data-sources/api-sources/new">
|
||||
<form id="api-source-form" method="post" action="{{formAction}}" {{{formAttrs}}}>
|
||||
<div class="card card-outline card-primary admin-form-card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">API source details</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-4">
|
||||
<div class="row g-3">
|
||||
<div class="col-12 col-lg-6">
|
||||
<label for="api-source-name" class="form-label">Name</label>
|
||||
<input id="api-source-name" name="name" class="form-control" value="{{apiSource.name}}" required />
|
||||
</div>
|
||||
<div class="col-12 col-lg-6">
|
||||
<label for="api-source-url" class="form-label">API URL</label>
|
||||
<input id="api-source-url" name="api_url" type="url" class="form-control" value="{{apiSource.apiUrl}}" placeholder="https://example.com/api.json" required />
|
||||
</div>
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-12 col-lg-6">
|
||||
<label for="api-source-name" class="form-label">Name</label>
|
||||
<input id="api-source-name" name="name" class="form-control" value="{{apiSource.name}}" required />
|
||||
</div>
|
||||
<div class="col-12 col-lg-6">
|
||||
<label for="api-source-url" class="form-label">API URL</label>
|
||||
<input id="api-source-url" name="api_url" type="url" class="form-control" value="{{apiSource.apiUrl}}" placeholder="https://example.com/api.json" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
@@ -93,24 +91,15 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer d-flex justify-content-end">
|
||||
<div class="btn-group" role="group" aria-label="API source actions">
|
||||
{{{saveActionButtons formId="api-source-form" saveLabel="Save" saveAndCloseLabel="Save and Close" saveAndNewLabel="Save and New" saveAndCloseValue="close" saveAndNewValue="new"}}}
|
||||
<a class="btn btn-warning" href="/data-sources/api-sources" data-confirm-unsaved="You have unsaved changes. Leave this page?">Cancel</a>
|
||||
{{#if inUse}}
|
||||
<button type="submit" class="btn btn-outline-danger" form="delete-api-source-form" disabled>Delete</button>
|
||||
{{else}}
|
||||
<button type="submit" class="btn btn-danger" form="delete-api-source-form">Delete</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{{saveActionButtons formId="api-source-form" saveUrl=formAction cancelUrl=cancelUrl deleteUrl=deleteUrl deleteDisabled=deleteDisabled showSaveAndClose=showSaveSecondaryActions showSaveAndNew=showSaveSecondaryActions}}}
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="card card-outline card-secondary mt-4" id="api-source-response-panel">
|
||||
<div class="card card-outline card-secondary mt-3" id="api-source-response-panel">
|
||||
<div class="card-header d-flex align-items-center gap-2">
|
||||
<h3 class="card-title mb-0">Latest response</h3>
|
||||
{{#if apiSource.lastResponseJson}}
|
||||
{{#if lastResponseJson}}
|
||||
<div class="btn-group btn-group-sm ms-auto" role="group" aria-label="JSON tree actions">
|
||||
<button type="button" class="btn btn-outline-secondary" data-json-toggle-collapse-all>
|
||||
<i class="bi bi-arrows-collapse me-1"></i>
|
||||
@@ -124,8 +113,8 @@
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{{#if apiSource.lastPullError}}
|
||||
<div class="alert alert-warning">{{apiSource.lastPullError}}</div>
|
||||
{{#if lastPullError}}
|
||||
<div class="alert alert-warning">{{lastPullError}}</div>
|
||||
{{/if}}
|
||||
<dl class="row">
|
||||
<dt class="col-12 col-md-3">Last pulled</dt>
|
||||
@@ -143,10 +132,10 @@
|
||||
<dt class="col-12 col-md-3">Content type</dt>
|
||||
<dd class="col-12 col-md-9">{{#if apiSource.lastResponseContentType}}{{apiSource.lastResponseContentType}}{{else}}-{{/if}}</dd>
|
||||
</dl>
|
||||
{{#if apiSource.lastResponseJson}}
|
||||
{{#if lastResponseJson}}
|
||||
<div data-json-toggle-panel>
|
||||
<script type="application/json" data-json-toggle-source>{{json apiSource.lastResponseJson}}</script>
|
||||
<div class="mb-0 small bg-body-tertiary border rounded p-3 font-monospace" style="white-space: pre-wrap; word-break: break-word;" data-json-toggle-output>{{apiSource.lastResponseJson}}</div>
|
||||
<script type="application/json" data-json-toggle-source>{{json lastResponseJson}}</script>
|
||||
<div class="mb-0 small bg-body-tertiary border rounded p-3 font-monospace" style="white-space: pre-wrap; word-break: break-word;" data-json-toggle-output>{{lastResponseJson}}</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="alert alert-secondary mb-0">No JSON response is stored yet.</div>
|
||||
@@ -154,5 +143,4 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form id="delete-api-source-form" method="post" action="/data-sources/api-sources/{{apiSource.id}}/delete" data-confirm-message="Delete this API source?"></form>
|
||||
<script type="module" src="/assets/js/data-sources/api-source-form.js?v={{appVersion}}"></script>
|
||||
<script src="/assets/js/data-sources/api-source-form.js?v={{appVersion}}"></script>
|
||||
@@ -1,48 +0,0 @@
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h2>Add RSS feed</h2>
|
||||
<p>Capture a feed URL, refresh interval, and the number of items to store.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-outline card-primary admin-form-card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">RSS feed details</h3>
|
||||
</div>
|
||||
<form id="rss-feed-form" method="post" action="/data-sources/rss-feeds" data-async-save data-async-save-close-url="/data-sources/rss-feeds" data-async-save-new-url="/data-sources/rss-feeds/new">
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label for="rss-feed-name" class="form-label">Name</label>
|
||||
<input id="rss-feed-name" name="name" class="form-control" value="{{rssFeed.name}}" required />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="rss-feed-url" class="form-label">Feed URL</label>
|
||||
<input id="rss-feed-url" name="feed_url" type="url" class="form-control" value="{{rssFeed.feedUrl}}" placeholder="https://example.com/feed.xml" required />
|
||||
</div>
|
||||
<div class="row g-3">
|
||||
<div class="col-12 col-md-4">
|
||||
<label for="rss-feed-interval" class="form-label">Update interval</label>
|
||||
<input id="rss-feed-interval" name="update_interval_value" type="number" min="1" class="form-control" value="{{rssFeed.updateIntervalValue}}" required />
|
||||
</div>
|
||||
<div class="col-12 col-md-4">
|
||||
<label for="rss-feed-interval-unit" class="form-label">Unit</label>
|
||||
<select id="rss-feed-interval-unit" name="update_interval_unit" class="form-select">
|
||||
<option value="seconds" {{#if (eq rssFeed.updateIntervalUnit 'seconds')}}selected{{/if}}>Seconds</option>
|
||||
<option value="minutes" {{#if (eq rssFeed.updateIntervalUnit 'minutes')}}selected{{/if}}>Minutes</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-12 col-md-4">
|
||||
<label for="rss-feed-item-limit" class="form-label">Items pulled</label>
|
||||
<input id="rss-feed-item-limit" name="item_limit" type="number" min="1" class="form-control" value="{{rssFeed.itemLimit}}" required />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer d-flex justify-content-end">
|
||||
<div class="btn-group" role="group" aria-label="RSS feed actions">
|
||||
{{{saveActionButtons formId="rss-feed-form" saveLabel="Save" saveAndCloseLabel="Save and Close" saveAndNewLabel="Save and New" saveAndCloseValue="close" saveAndNewValue="new"}}}
|
||||
<a class="btn btn-warning" href="/data-sources/rss-feeds" data-confirm-unsaved="You have unsaved changes. Leave this page?">Cancel</a>
|
||||
<button type="submit" class="btn btn-outline-danger" disabled>Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -1,189 +0,0 @@
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h2>Edit RSS feed - {{rssFeed.name}}</h2>
|
||||
<p>Adjust the source URL, refresh interval, or item count.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-outline card-primary admin-form-card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">RSS feed details</h3>
|
||||
</div>
|
||||
<form id="rss-feed-form" method="post" action="/data-sources/rss-feeds/{{rssFeed.id}}" data-async-save data-async-save-refresh-target="#rss-feed-items-panel" data-async-save-refresh-state-url="/data-sources/rss-feeds/{{rssFeed.id}}/state" data-async-save-close-url="/data-sources/rss-feeds" data-async-save-new-url="/data-sources/rss-feeds/new">
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label for="rss-feed-name" class="form-label">Name</label>
|
||||
<input id="rss-feed-name" name="name" class="form-control" value="{{rssFeed.name}}" required />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="rss-feed-url" class="form-label">Feed URL</label>
|
||||
<input id="rss-feed-url" name="feed_url" type="url" class="form-control" value="{{rssFeed.feedUrl}}" placeholder="https://example.com/feed.xml" required />
|
||||
</div>
|
||||
<div class="row g-3">
|
||||
<div class="col-12 col-md-4">
|
||||
<label for="rss-feed-interval" class="form-label">Update interval</label>
|
||||
<input id="rss-feed-interval" name="update_interval_value" type="number" min="1" class="form-control" value="{{rssFeed.updateIntervalValue}}" required />
|
||||
</div>
|
||||
<div class="col-12 col-md-4">
|
||||
<label for="rss-feed-interval-unit" class="form-label">Unit</label>
|
||||
<select id="rss-feed-interval-unit" name="update_interval_unit" class="form-select">
|
||||
<option value="seconds" {{#if (eq rssFeed.updateIntervalUnit 'seconds')}}selected{{/if}}>Seconds</option>
|
||||
<option value="minutes" {{#if (eq rssFeed.updateIntervalUnit 'minutes')}}selected{{/if}}>Minutes</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-12 col-md-4">
|
||||
<label for="rss-feed-item-limit" class="form-label">Items pulled</label>
|
||||
<input id="rss-feed-item-limit" name="item_limit" type="number" min="1" class="form-control" value="{{rssFeed.itemLimit}}" required />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer d-flex justify-content-end">
|
||||
<div class="btn-group" role="group" aria-label="RSS feed actions">
|
||||
{{{saveActionButtons formId="rss-feed-form" saveLabel="Save" saveAndCloseLabel="Save and Close" saveAndNewLabel="Save and New" saveAndCloseValue="close" saveAndNewValue="new"}}}
|
||||
<a class="btn btn-warning" href="/data-sources/rss-feeds" data-confirm-unsaved="You have unsaved changes. Leave this page?">Cancel</a>
|
||||
{{#if inUse}}
|
||||
<button type="submit" class="btn btn-outline-danger" form="delete-rss-feed-form" disabled>Delete</button>
|
||||
{{else}}
|
||||
<button type="submit" class="btn btn-danger" form="delete-rss-feed-form">Delete</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="card card-outline card-secondary mt-4" id="rss-feed-items-panel">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Pulled items</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{{#if pullError}}
|
||||
<div class="alert alert-warning">{{pullError}}</div>
|
||||
{{/if}}
|
||||
{{#if pulledItems.length}}
|
||||
<div class="accordion rss-feed-items-accordion" id="rss-feed-items-accordion">
|
||||
{{#each pulledItems}}
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="rss-feed-item-heading-{{id}}-{{position}}">
|
||||
<button class="accordion-button {{#unless @first}}collapsed{{/unless}}" type="button" data-bs-toggle="collapse" data-bs-target="#rss-feed-item-collapse-{{id}}-{{position}}" aria-expanded="{{#if @first}}true{{else}}false{{/if}}" aria-controls="rss-feed-item-collapse-{{id}}-{{position}}">
|
||||
<span class="me-2">{{title}}</span>
|
||||
<span class="text-muted small">{{#if pubDate}}• {{pubDate}}{{/if}}</span>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="rss-feed-item-collapse-{{id}}-{{position}}" class="accordion-collapse collapse {{#if @first}}show{{/if}}" aria-labelledby="rss-feed-item-heading-{{id}}-{{position}}" data-bs-parent="#rss-feed-items-accordion">
|
||||
<div class="accordion-body">
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-12 col-md-3">Title</dt>
|
||||
<dd class="col-12 col-md-9">{{title}}</dd>
|
||||
|
||||
<dt class="col-12 col-md-3">Link</dt>
|
||||
<dd class="col-12 col-md-9">
|
||||
{{#if link}}
|
||||
<a href="{{link}}" target="_blank" rel="noopener noreferrer">{{link}}</a>
|
||||
{{else}}
|
||||
<span class="empty">-</span>
|
||||
{{/if}}
|
||||
</dd>
|
||||
|
||||
<dt class="col-12 col-md-3">Published</dt>
|
||||
<dd class="col-12 col-md-9">{{#if pubDate}}{{pubDate}}{{else}}-{{/if}}</dd>
|
||||
|
||||
<dt class="col-12 col-md-3">Author</dt>
|
||||
<dd class="col-12 col-md-9">{{#if itemJson.author}}{{itemJson.author}}{{else}}-{{/if}}</dd>
|
||||
|
||||
<dt class="col-12 col-md-3">GUID</dt>
|
||||
<dd class="col-12 col-md-9">{{#if itemJson.guid}}{{itemJson.guid}}{{else}}-{{/if}}</dd>
|
||||
|
||||
<dt class="col-12 col-md-3">Comments</dt>
|
||||
<dd class="col-12 col-md-9">
|
||||
{{#if itemJson.comments}}
|
||||
<a href="{{itemJson.comments}}" target="_blank" rel="noopener noreferrer">{{itemJson.comments}}</a>
|
||||
{{else}}
|
||||
<span class="empty">-</span>
|
||||
{{/if}}
|
||||
</dd>
|
||||
|
||||
<dt class="col-12 col-md-3">Description</dt>
|
||||
<dd class="col-12 col-md-9">{{#if description}}{{description}}{{else}}<span class="empty">-</span>{{/if}}</dd>
|
||||
|
||||
<dt class="col-12 col-md-3">Categories</dt>
|
||||
<dd class="col-12 col-md-9">
|
||||
{{#if itemJson.categories.length}}
|
||||
<ul class="list-group list-group-flush border rounded">
|
||||
{{#each itemJson.categories}}
|
||||
<li class="list-group-item d-flex justify-content-between align-items-start">
|
||||
<span>{{value}}</span>
|
||||
{{#if domain}}<span class="text-muted small ms-3">{{domain}}</span>{{/if}}
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{else}}
|
||||
<span class="empty">-</span>
|
||||
{{/if}}
|
||||
</dd>
|
||||
|
||||
<dt class="col-12 col-md-3">Enclosure</dt>
|
||||
<dd class="col-12 col-md-9">
|
||||
{{#if itemJson.enclosure}}
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-12 col-lg-4">URL</dt>
|
||||
<dd class="col-12 col-lg-8">
|
||||
{{#if itemJson.enclosure.url}}
|
||||
<a href="{{itemJson.enclosure.url}}" target="_blank" rel="noopener noreferrer">{{itemJson.enclosure.url}}</a>
|
||||
{{else}}
|
||||
<span class="empty">-</span>
|
||||
{{/if}}
|
||||
</dd>
|
||||
<dt class="col-12 col-lg-4">Length</dt>
|
||||
<dd class="col-12 col-lg-8">{{#if itemJson.enclosure.length}}{{itemJson.enclosure.length}}{{else}}-{{/if}}</dd>
|
||||
<dt class="col-12 col-lg-4">Type</dt>
|
||||
<dd class="col-12 col-lg-8">{{#if itemJson.enclosure.type}}{{itemJson.enclosure.type}}{{else}}-{{/if}}</dd>
|
||||
</dl>
|
||||
{{else}}
|
||||
<span class="empty">-</span>
|
||||
{{/if}}
|
||||
</dd>
|
||||
|
||||
<dt class="col-12 col-md-3">Source</dt>
|
||||
<dd class="col-12 col-md-9 mb-0">
|
||||
{{#if itemJson.source}}
|
||||
<dl class="row mb-0">
|
||||
<dt class="col-12 col-lg-4">Title</dt>
|
||||
<dd class="col-12 col-lg-8">{{#if itemJson.source.title}}{{itemJson.source.title}}{{else}}-{{/if}}</dd>
|
||||
<dt class="col-12 col-lg-4">URL</dt>
|
||||
<dd class="col-12 col-lg-8">
|
||||
{{#if itemJson.source.url}}
|
||||
<a href="{{itemJson.source.url}}" target="_blank" rel="noopener noreferrer">{{itemJson.source.url}}</a>
|
||||
{{else}}
|
||||
<span class="empty">-</span>
|
||||
{{/if}}
|
||||
</dd>
|
||||
</dl>
|
||||
{{else}}
|
||||
<span class="empty">-</span>
|
||||
{{/if}}
|
||||
</dd>
|
||||
|
||||
<dt class="col-12 col-md-3">Raw item XML</dt>
|
||||
<dd class="col-12 col-md-9 mb-0">
|
||||
{{#if itemJson.rawXml}}
|
||||
<details>
|
||||
<summary class="small text-muted">Show raw XML</summary>
|
||||
<pre class="mb-0 mt-2 small bg-body-tertiary border rounded p-3 text-wrap">{{itemJson.rawXml}}</pre>
|
||||
</details>
|
||||
{{else}}
|
||||
<span class="empty">-</span>
|
||||
{{/if}}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="alert alert-secondary mb-0">No feed items could be loaded.</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form id="delete-rss-feed-form" method="post" action="/data-sources/rss-feeds/{{rssFeed.id}}/delete" data-confirm-message="Delete this RSS feed?"></form>
|
||||
@@ -0,0 +1,141 @@
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h2>{{#if isEdit}}Edit RSS feed - {{rssFeed.name}}{{else}}Add RSS feed{{/if}}</h2>
|
||||
<p>{{#if isEdit}}Adjust the source URL, refresh interval, or item count.{{else}}Capture a feed URL, refresh interval, and the number of items to store.{{/if}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form id="rss-feed-form" method="post" action="{{formAction}}" {{{formAttrs}}}>
|
||||
<div class="card card-outline card-primary admin-form-card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">RSS feed details</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label for="rss-feed-name" class="form-label">Name</label>
|
||||
<input id="rss-feed-name" name="name" class="form-control" value="{{rssFeed.name}}" required />
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="rss-feed-url" class="form-label">Feed URL</label>
|
||||
<input id="rss-feed-url" name="feed_url" type="url" class="form-control" value="{{rssFeed.feedUrl}}" placeholder="https://example.com/feed.xml" required />
|
||||
</div>
|
||||
<div class="row g-3">
|
||||
<div class="col-12 col-md-4">
|
||||
<label for="rss-feed-interval" class="form-label">Update interval</label>
|
||||
<input id="rss-feed-interval" name="update_interval_value" type="number" min="1" class="form-control" value="{{rssFeed.updateIntervalValue}}" required />
|
||||
</div>
|
||||
<div class="col-12 col-md-4">
|
||||
<label for="rss-feed-interval-unit" class="form-label">Unit</label>
|
||||
<select id="rss-feed-interval-unit" name="update_interval_unit" class="form-select">
|
||||
<option value="seconds" {{#if (eq rssFeed.updateIntervalUnit 'seconds')}}selected{{/if}}>Seconds</option>
|
||||
<option value="minutes" {{#if (eq rssFeed.updateIntervalUnit 'minutes')}}selected{{/if}}>Minutes</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-12 col-md-4">
|
||||
<label for="rss-feed-item-limit" class="form-label">Items pulled</label>
|
||||
<input id="rss-feed-item-limit" name="item_limit" type="number" min="1" class="form-control" value="{{rssFeed.itemLimit}}" required />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer d-flex justify-content-end">
|
||||
{{{saveActionButtons formId="rss-feed-form" saveUrl=formAction cancelUrl=cancelUrl deleteUrl=deleteUrl deleteDisabled=deleteDisabled showSaveAndClose=showSaveSecondaryActions showSaveAndNew=showSaveSecondaryActions}}}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="card card-outline card-secondary admin-form-card mt-4">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Latest items</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="accordion" id="rss-feed-items-accordion">
|
||||
{{#if pullError}}
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="rss-feed-items-heading-error">
|
||||
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#rss-feed-items-collapse-error" aria-expanded="true" aria-controls="rss-feed-items-collapse-error">
|
||||
Preview pulled feed items
|
||||
</button>
|
||||
</h2>
|
||||
<div id="rss-feed-items-collapse-error" class="accordion-collapse collapse show" aria-labelledby="rss-feed-items-heading-error" data-bs-parent="#rss-feed-items-accordion">
|
||||
<div class="accordion-body">
|
||||
<div class="alert alert-danger mb-0" role="alert">{{pullError}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{else if pulledItems.length}}
|
||||
{{#each pulledItems}}
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="rss-feed-item-heading-{{@index}}">
|
||||
<button class="accordion-button {{#unless @first}}collapsed{{/unless}}" type="button" data-bs-toggle="collapse" data-bs-target="#rss-feed-item-collapse-{{@index}}" aria-expanded="{{#if @first}}true{{else}}false{{/if}}" aria-controls="rss-feed-item-collapse-{{@index}}">
|
||||
{{#if title}}{{title}}{{else}}Item {{@index}}{{/if}}
|
||||
</button>
|
||||
</h2>
|
||||
<div id="rss-feed-item-collapse-{{@index}}" class="accordion-collapse collapse {{#if @first}}show{{/if}}" aria-labelledby="rss-feed-item-heading-{{@index}}" data-bs-parent="#rss-feed-items-accordion">
|
||||
<div class="accordion-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-striped align-middle mb-0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row" class="text-nowrap" style="width: 10rem;">Title</th>
|
||||
<td class="text-break">{{#if title}}{{title}}{{else}}-{{/if}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row" class="text-nowrap">Link</th>
|
||||
<td class="text-break">
|
||||
{{#if link}}
|
||||
<a href="{{link}}" target="_blank" rel="noreferrer">{{link}}</a>
|
||||
{{else}}
|
||||
-
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row" class="text-nowrap">Published</th>
|
||||
<td class="text-break">{{#if pubDate}}{{pubDate}}{{else}}-{{/if}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row" class="text-nowrap">Description</th>
|
||||
<td class="text-break">{{#if description}}{{description}}{{else}}-{{/if}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row" class="text-nowrap">Author</th>
|
||||
<td class="text-break">{{#if author}}{{author}}{{else}}-{{/if}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row" class="text-nowrap">Guid</th>
|
||||
<td class="text-break">{{#if guid}}{{guid}}{{else}}-{{/if}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row" class="text-nowrap">Comments</th>
|
||||
<td class="text-break">
|
||||
{{#if comments}}
|
||||
<a href="{{comments}}" target="_blank" rel="noreferrer">{{comments}}</a>
|
||||
{{else}}
|
||||
-
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
{{else}}
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="rss-feed-items-heading-empty">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#rss-feed-items-collapse-empty" aria-expanded="false" aria-controls="rss-feed-items-collapse-empty">
|
||||
Preview pulled feed items
|
||||
</button>
|
||||
</h2>
|
||||
<div id="rss-feed-items-collapse-empty" class="accordion-collapse collapse" aria-labelledby="rss-feed-items-heading-empty" data-bs-parent="#rss-feed-items-accordion">
|
||||
<div class="accordion-body">
|
||||
<div class="text-muted">No feed items have been pulled yet.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
+19
-31
@@ -1,31 +1,31 @@
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h2>{{#if isEdit}}Edit schedule group{{else}}Add schedule group{{/if}}</h2>
|
||||
<h2>{{#if isEdit}}Edit timetable group{{else}}Add timetable group{{/if}}</h2>
|
||||
<p>Define a group of events with their start and end times.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-outline card-primary admin-form-card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Schedule group details</h3>
|
||||
<h3 class="card-title">Timetable group details</h3>
|
||||
</div>
|
||||
<form id="schedule-group-form" method="post" action="{{#if isEdit}}/data-sources/schedules/{{scheduleGroup.id}}{{else}}/data-sources/schedules{{/if}}" data-async-save data-async-save-close-url="/data-sources/schedules" data-async-save-new-url="/data-sources/schedules/new">
|
||||
<form id="timetable-group-form" method="post" action="{{#if isEdit}}/data-sources/timetables/{{timetableGroup.id}}{{else}}/data-sources/timetables{{/if}}" data-async-save data-async-save-close-url="/data-sources/timetables"{{#unless isEdit}} data-async-save-new-redirect="response-url"{{/unless}} data-async-save-new-url="/data-sources/timetables/new">
|
||||
<div class="card-body d-grid gap-4">
|
||||
<div class="row g-3">
|
||||
<div class="col-12 col-lg-4">
|
||||
<label for="schedule-group-name" class="form-label">Name</label>
|
||||
<input id="schedule-group-name" name="name" class="form-control" value="{{scheduleGroup.name}}" required />
|
||||
<label for="timetable-group-name" class="form-label">Name</label>
|
||||
<input id="timetable-group-name" name="name" class="form-control" value="{{timetableGroup.name}}" required />
|
||||
</div>
|
||||
<div class="col-12 col-lg-8">
|
||||
<label for="schedule-group-short-description" class="form-label">Short description</label>
|
||||
<input id="schedule-group-short-description" name="short_description" class="form-control" value="{{scheduleGroup.shortDescription}}" placeholder="Morning events" />
|
||||
<label for="timetable-group-short-description" class="form-label">Short description</label>
|
||||
<input id="timetable-group-short-description" name="short_description" class="form-control" value="{{timetableGroup.shortDescription}}" placeholder="Morning events" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="d-flex align-items-center justify-content-between gap-3 mb-2">
|
||||
<h4 class="schedule-section-heading mb-0">Entries</h4>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" data-add-schedule-entry>Add entry</button>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" data-add-timetable-entry>Add entry</button>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered align-middle schedule-entries-table mb-0">
|
||||
@@ -38,9 +38,9 @@
|
||||
<th style="width: 6rem;">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-schedule-entries-body>
|
||||
{{#each scheduleEntries}}
|
||||
<tr data-schedule-entry-row>
|
||||
<tbody data-timetable-entries-body>
|
||||
{{#each timetableEntries}}
|
||||
<tr data-timetable-entry-row>
|
||||
<td>
|
||||
<input type="hidden" name="entry_id[]" value="{{id}}" />
|
||||
<input type="text" name="entry_title[]" class="form-control" value="{{title}}" required />
|
||||
@@ -55,36 +55,24 @@
|
||||
<input type="datetime-local" name="entry_end_datetime[]" class="form-control" value="{{endValue}}" />
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-outline-danger btn-sm" data-remove-schedule-entry>Remove</button>
|
||||
<button type="button" class="btn btn-outline-danger btn-sm" data-remove-timetable-entry>Remove</button>
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p class="form-text mb-0">Add one or more rows. A schedule region can render upcoming or current entries from this group.</p>
|
||||
<p class="form-text mb-0">Add one or more rows. A timetable region can render upcoming or current entries from this group.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer d-flex justify-content-end">
|
||||
<div class="btn-group" role="group" aria-label="Schedule group actions">
|
||||
{{{saveActionButtons formId="schedule-group-form" saveLabel="Save" saveAndCloseLabel="Save and Close" saveAndNewLabel="Save and New" saveAndCloseValue="close" saveAndNewValue="new"}}}
|
||||
<a class="btn btn-warning" href="/data-sources/schedules" data-confirm-unsaved="You have unsaved changes. Leave this page?">Cancel</a>
|
||||
{{#if isEdit}}
|
||||
{{#if inUse}}
|
||||
<button type="submit" class="btn btn-outline-danger" form="delete-schedule-group-form" disabled>Delete</button>
|
||||
{{else}}
|
||||
<button type="submit" class="btn btn-danger" form="delete-schedule-group-form">Delete</button>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<button type="submit" class="btn btn-outline-danger" disabled>Delete</button>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{{saveActionButtons formId="timetable-group-form" saveUrl=formAction cancelUrl=cancelUrl deleteUrl=deleteUrl deleteDisabled=deleteDisabled showSaveAndClose=showSaveSecondaryActions showSaveAndNew=showSaveSecondaryActions}}}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<template id="schedule-entry-row-template">
|
||||
<tr data-schedule-entry-row>
|
||||
<template id="timetable-entry-row-template">
|
||||
<tr data-timetable-entry-row>
|
||||
<td>
|
||||
<input type="hidden" name="entry_id[]" value="" />
|
||||
<input type="text" name="entry_title[]" class="form-control" value="" required />
|
||||
@@ -99,13 +87,13 @@
|
||||
<input type="datetime-local" name="entry_end_datetime[]" class="form-control" value="" />
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-outline-danger btn-sm" data-remove-schedule-entry>Remove</button>
|
||||
<button type="button" class="btn btn-outline-danger btn-sm" data-remove-timetable-entry>Remove</button>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
{{#if isEdit}}
|
||||
<form id="delete-schedule-group-form" method="post" action="/data-sources/schedules/{{scheduleGroup.id}}/delete" data-confirm-message="Delete this schedule group?"></form>
|
||||
<form id="delete-timetable-group-form" method="post" action="/data-sources/timetables/{{timetableGroup.id}}/delete" data-confirm-message="Delete this timetable group?"></form>
|
||||
{{/if}}
|
||||
|
||||
<script type="module" src="/assets/js/data-sources/schedule-group-form.js?v={{assetVersion}}"></script>
|
||||
<script type="module" src="/assets/js/data-sources/timetable-group-form.js?v={{assetVersion}}"></script>
|
||||
+14
-14
@@ -1,7 +1,7 @@
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h2>Schedules</h2>
|
||||
<p>Store grouped events with start and end times so a schedule region can show what is coming up next.</p>
|
||||
<h2>Timetables</h2>
|
||||
<p>Store grouped events with start and end times so a timetable region can show what is coming up next.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
<div class="card-tools d-flex flex-nowrap align-items-center gap-2">
|
||||
<div class="input-group input-group-sm flex-shrink-1" style="width: min(14rem, 100%);">
|
||||
<span class="input-group-text" aria-hidden="true"><i class="bi bi-search"></i></span>
|
||||
<input type="search" class="form-control" placeholder="Search schedules" aria-label="Search schedules" data-table-search />
|
||||
<input type="search" class="form-control" placeholder="Search timetables" aria-label="Search timetables" data-table-search />
|
||||
</div>
|
||||
{{#if (hasPermission currentUser 'schedules.create')}}
|
||||
<a class="btn btn-primary btn-sm" href="/data-sources/schedules/new">Add schedule group</a>
|
||||
{{#if (hasPermission currentUser 'timetables.create')}}
|
||||
<a class="btn btn-primary btn-sm" href="/data-sources/timetables/new">Add timetable group</a>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
@@ -30,8 +30,8 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#if scheduleGroups.length}}
|
||||
{{#each scheduleGroups}}
|
||||
{{#if timetableGroups.length}}
|
||||
{{#each timetableGroups}}
|
||||
<tr data-table-search-row>
|
||||
<td data-label="Name">{{name}}</td>
|
||||
<td data-label="Description" class="text-break">{{short_description}}</td>
|
||||
@@ -44,13 +44,13 @@
|
||||
{{/if}}
|
||||
</td>
|
||||
<td data-label="Actions">
|
||||
{{#if (anyPermission ../currentUser 'schedules.update' 'schedules.delete')}}
|
||||
{{#if (anyPermission ../currentUser 'timetables.update' 'timetables.delete')}}
|
||||
<div class="actions">
|
||||
{{#if (hasPermission ../currentUser 'schedules.update')}}
|
||||
<a class="btn btn-sm btn-primary" href="/data-sources/schedules/{{id}}/edit">Edit</a>
|
||||
{{#if (hasPermission ../currentUser 'timetables.update')}}
|
||||
<a class="btn btn-sm btn-primary" href="/data-sources/timetables/{{id}}/edit">Edit</a>
|
||||
{{/if}}
|
||||
{{#if (hasPermission ../currentUser 'schedules.delete')}}
|
||||
<form class="inline-form" method="post" action="/data-sources/schedules/{{id}}/delete" data-confirm-message="Delete this schedule group?">
|
||||
{{#if (hasPermission ../currentUser 'timetables.delete')}}
|
||||
<form class="inline-form" method="post" action="/data-sources/timetables/{{id}}/delete" data-confirm-message="Delete this timetable group?">
|
||||
{{#if inUse}}
|
||||
<button class="btn btn-sm btn-outline-danger" type="submit" disabled>Delete</button>
|
||||
{{else}}
|
||||
@@ -66,10 +66,10 @@
|
||||
</tr>
|
||||
{{/each}}
|
||||
{{else}}
|
||||
<tr data-table-search-empty-default><td colspan="5" class="empty">No schedule groups yet.</td></tr>
|
||||
<tr data-table-search-empty-default><td colspan="5" class="empty">No timetable groups yet.</td></tr>
|
||||
{{/if}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{> table-pagination pagination=pagination basePath="/data-sources/schedules" alwaysShow=true}}
|
||||
{{> table-pagination pagination=pagination basePath="/data-sources/timetables" alwaysShow=true}}
|
||||
</div>
|
||||
Reference in New Issue
Block a user