Save worktree changes

This commit is contained in:
2026-07-25 02:29:19 +01:00
parent 8d3b7d557b
commit db9d718cd8
170 changed files with 11719 additions and 3414 deletions
+27
View File
@@ -0,0 +1,27 @@
<div class="page-header">
<div>
<h2>Create playlist</h2>
<p>Name the playlist and start adding slides.</p>
</div>
</div>
<div class="card card-outline card-primary admin-form-card">
<div class="card-header">
<h3 class="card-title">Playlist details</h3>
</div>
<form id="playlist-edit-form" method="post" action="/playlists" data-async-save data-async-save-new-url="/playlists/new">
<div class="card-body">
<div class="mb-0">
<label for="playlist-name" class="form-label">Name</label>
<input id="playlist-name" name="name" class="form-control" placeholder="Lobby loop" required />
</div>
</div>
<div class="card-footer d-flex justify-content-end">
<div class="btn-group" role="group" aria-label="Playlist actions">
{{{saveActionButtons formId="playlist-edit-form" saveLabel="Save" showSaveAndClose=false showSaveAndNew=false}}}
<a class="btn btn-warning" href="/playlists" data-confirm-unsaved="You have unsaved changes. Leave this page?">Cancel</a>
</div>
</div>
</form>
</div>
+147
View File
@@ -0,0 +1,147 @@
<div class="page-header">
<div>
<h2>Edit playlist - {{playlist.name}}</h2>
<p>Adjust the playlist details, slide order, and scheduling.</p>
</div>
</div>
<div class="row g-4 mb-4">
<div class="col-12 col-lg-8">
<div class="card card-outline card-primary mb-0">
<div class="card-header">
<h3 class="card-title">Playlist details</h3>
</div>
<div class="card-body">
<form id="playlist-edit-form" method="post" action="/playlists/{{playlist.id}}" data-async-save data-async-save-close-url="/playlists" data-async-save-new-url="/playlists/new">
<div class="row g-3 align-items-end">
<div class="col-12 col-lg-8">
<label for="playlist-name" class="form-label">Name</label>
<input id="playlist-name" name="name" class="form-control" value="{{playlist.name}}" required />
</div>
</div>
</form>
<div class="row g-3 mt-1 align-items-start">
<div class="col-12">
<h4 class="h6 mb-2 text-uppercase text-muted">Add slide to playlist</h4>
{{#if hasSlides}}
{{#if hasSelectableSlides}}
<form id="playlist-add-slide-form">
<div class="row g-2 align-items-end playlist-add-slide-form">
<div class="col-12 col-md-8">
<select id="playlist-add-slide-select" class="form-select" required>
{{#each selectableSlides}}
<option value="{{id}}" data-canvas-signature="{{canvasSignature}}" {{#if isAssigned}}disabled{{/if}}>{{title}}</option>
{{/each}}
</select>
</div>
<div class="col-12 col-md-4 d-flex justify-content-md-end">
<button type="button" class="btn btn-primary w-100 w-md-auto" id="playlist-add-slide-button">Add slide</button>
</div>
</div>
</form>
<div id="playlist-add-slide-empty" class="empty mt-2 playlist-add-slide-empty is-hidden">No slides are currently available for this playlist.</div>
{{else}}
<div class="empty mb-0">
{{#if playlistCanvasMismatch}}
This playlist contains slides with different canvas sizes. Remove the mismatched items before adding more.
{{else if playlistCanvasSignature}}
No slides match this playlist canvas size.
{{else}}
No slides are currently available for this playlist.
{{/if}}
</div>
{{/if}}
{{else}}
<div class="empty mb-0">Create slides first.</div>
{{/if}}
</div>
</div>
</div>
<div class="card-footer d-flex justify-content-end">
<div class="btn-group playlist-actions-group" role="group" aria-label="Playlist actions">
{{{saveActionButtons formId="playlist-edit-form" saveLabel="Save" saveAndCloseLabel="Save and Close" saveAndNewLabel="Save and New" saveAndCloseValue="close" saveAndNewValue="new"}}}
<a class="btn btn-warning" href="/playlists" data-confirm-unsaved="You have unsaved changes. Leave this page?">Cancel</a>
<button type="submit" class="btn btn-danger" form="delete-playlist-form">Delete</button>
</div>
</div>
</div>
</div>
<div class="col-12 col-lg-4">
<div class="card card-outline card-light h-100 mb-0">
<div class="card-header">
<h4 class="card-title">Options</h4>
</div>
<div class="card-body py-3">
<div class="form-check form-switch mb-0">
<input class="form-check-input" type="checkbox" role="switch" id="playlist-fade-between-slides" name="fade_between_slides" value="1" form="playlist-edit-form" {{#if playlistFadeBetweenSlides}}checked{{/if}} />
<label class="form-check-label" for="playlist-fade-between-slides">Fade between slides</label>
</div>
</div>
</div>
</div>
</div>
<div class="row g-4 mb-4">
<div class="col-12">
<div class="card card-outline card-secondary mb-0">
<div class="card-header">
<h3 class="card-title">Playlist items</h3>
</div>
<div class="card-body table-responsive p-0">
<table class="table table-striped w-100 mb-0">
<thead><tr><th>Order</th><th>Slide</th><th>Schedule</th><th>Duration</th><th>Actions</th></tr></thead>
<tbody id="playlist-items-body" data-playlist-id="{{playlist.id}}">
{{#if playlistSlides.length}}
{{#each playlistSlides}}
<tr data-playlist-slide-row data-row-key="existing-{{id}}" data-slide-id="{{slide_id}}" data-canvas-signature="{{canvasSignature}}">
<td class="playlist-order-cell" data-label="Order">
<div class="playlist-order-cell-inner">
<button type="button" class="playlist-drag-handle btn btn-link p-0 text-body-secondary" data-playlist-drag-handle aria-label="Drag to reorder" title="Drag to reorder">
<span class="playlist-drag-handle-icon" aria-hidden="true">
<svg class="playlist-drag-handle-svg" viewBox="0 0 24 32" focusable="false" aria-hidden="true">
<polygon points="12,2 20,9 4,9"></polygon>
<rect x="4" y="14" width="16" height="4" rx="2"></rect>
<polygon points="4,23 20,23 12,30"></polygon>
</svg>
</span>
</button>
<span class="playlist-order-number">{{displayOrder}}</span>
</div>
</td>
<td data-label="Slide">
{{title}}
<input type="hidden" name="slide_id[]" value="{{slide_id}}" form="playlist-edit-form" />
</td>
<td data-label="Schedule">
<div class="playlist-schedule-summary">{{scheduleSummary}}</div>
<input type="hidden" name="schedule_mode[]" value="{{schedule_mode}}" form="playlist-edit-form" />
<input type="hidden" name="schedule_start_datetime[]" value="{{schedule_start_datetime}}" form="playlist-edit-form" />
<input type="hidden" name="schedule_end_datetime[]" value="{{schedule_end_datetime}}" form="playlist-edit-form" />
<input type="hidden" name="schedule_start_time[]" value="{{schedule_start_time}}" form="playlist-edit-form" />
<input type="hidden" name="schedule_end_time[]" value="{{schedule_end_time}}" form="playlist-edit-form" />
<input type="hidden" name="schedule_days_json[]" value="{{schedule_days_json}}" form="playlist-edit-form" />
</td>
<td data-label="Duration">
<input name="duration_seconds[]" type="number" min="1" value="{{duration_seconds}}" required form="playlist-edit-form" />
</td>
<td data-label="Actions">
<div class="actions playlist-item-actions">
<button type="button" class="btn btn-sm btn-primary" data-schedule-config="/playlists/{{../playlist.id}}/slides/{{id}}/config" data-schedule-config-row="existing-{{id}}">Schedule</button>
<button type="button" class="btn btn-sm btn-danger" data-playlist-remove-row>Remove</button>
</div>
</td>
</tr>
{{/each}}
{{else}}
<tr class="playlist-empty-row"><td colspan="5" class="empty">No slides assigned yet.</td></tr>
{{/if}}
</tbody>
</table>
</div>
</div>
</div>
</div>
<form id="delete-playlist-form" method="post" action="/playlists/{{playlist.id}}/delete" data-confirm-message="Delete this playlist?"></form>
+57
View File
@@ -0,0 +1,57 @@
<div class="page-header">
<div>
<h2>Playlists</h2>
<p>Organize slide rotations and manage scheduled playback.</p>
</div>
</div>
<div class="card card-outline card-primary">
<div class="card-header">
<h3 class="card-title">Existing playlists</h3>
{{#if (hasPermission currentUser 'playlists.create')}}
<div class="card-tools">
<a class="btn btn-primary btn-sm" href="/playlists/new">Create playlist</a>
</div>
{{/if}}
</div>
<div class="card-body table-responsive p-0">
<table class="table table-striped w-100 mb-0">
<thead>
<tr>
<th>Name</th>
<th>Slides</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{{#if playlists.length}}
{{#each playlists}}
<tr>
<td data-label="Name">{{name}}</td>
<td data-label="Slides"><span class="chip">{{slideCount}} slide{{#unless slideCountIsOne}}s{{/unless}}</span></td>
<td data-label="Actions">
{{#if (anyPermission ../currentUser 'playlists.update' 'playlists.delete')}}
<div class="actions">
{{#if (hasPermission ../currentUser 'playlists.update')}}
<a class="btn btn-sm btn-primary" href="/playlists/{{id}}/edit">Edit</a>
{{/if}}
{{#if (hasPermission ../currentUser 'playlists.delete')}}
<form class="inline-form" method="post" action="/playlists/{{id}}/delete" data-confirm-message="Delete this playlist?">
<button class="btn btn-sm btn-danger" type="submit">Delete</button>
</form>
{{/if}}
</div>
{{else}}
<span class="empty">-</span>
{{/if}}
</td>
</tr>
{{/each}}
{{else}}
<tr><td colspan="3" class="empty">No playlists yet.</td></tr>
{{/if}}
</tbody>
</table>
</div>
</div>
@@ -0,0 +1,69 @@
<div class="card card-outline card-primary mb-0 slide-schedule-card">
<div class="card-header">
<h3 class="card-title">Slide schedule</h3>
</div>
<form method="post" action="/playlists/{{playlist.id}}/slides/{{playlistSlide.id}}/config">
<div class="card-body">
<input type="hidden" name="playlist_slide_id" value="{{playlistSlide.id}}" />
<input type="hidden" name="row_key" value="{{rowKey}}" />
<div class="mb-3">
<label for="schedule-mode-select" class="form-label">Schedule mode</label>
<select name="schedule_mode" id="schedule-mode-select" class="form-select">
<option value="always" {{#if isAlwaysMode}}selected{{/if}}>Always visible</option>
<option value="dates" {{#if isDatesMode}}selected{{/if}}>Between two datetimes</option>
<option value="times" {{#if isTimesMode}}selected{{/if}}>Between times on selected days</option>
</select>
</div>
<div class="card card-outline card-primary mb-3 schedule-panel {{#unless isDatesMode}}is-hidden{{/unless}}" id="schedule-dates-panel">
<div class="card-header py-2">
<h4 class="h6 mb-0">Date range</h4>
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-12 col-md-6">
<label for="schedule-start-datetime" class="form-label">Start datetime</label>
<input id="schedule-start-datetime" type="datetime-local" name="schedule_start_datetime" class="form-control" value="{{startDateTimeValue}}" />
</div>
<div class="col-12 col-md-6">
<label for="schedule-end-datetime" class="form-label">End datetime</label>
<input id="schedule-end-datetime" type="datetime-local" name="schedule_end_datetime" class="form-control" value="{{endDateTimeValue}}" />
</div>
</div>
</div>
</div>
<div class="card card-outline card-secondary mb-0 schedule-panel {{#unless isTimesMode}}is-hidden{{/unless}}" id="schedule-times-panel">
<div class="card-header py-2">
<h4 class="h6 mb-0">Time range</h4>
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-12 col-md-6">
<label for="schedule-start-time" class="form-label">Start time</label>
<input id="schedule-start-time" type="time" name="schedule_start_time" class="form-control" value="{{startTimeValue}}" />
</div>
<div class="col-12 col-md-6">
<label for="schedule-end-time" class="form-label">End time</label>
<input id="schedule-end-time" type="time" name="schedule_end_time" class="form-control" value="{{endTimeValue}}" />
</div>
</div>
<div class="btn-group btn-group-sm w-100 schedule-day-grid mt-3" role="group" aria-label="Schedule days">
{{#each dayOptions}}
<input class="btn-check" type="checkbox" id="schedule-day-{{value}}" name="schedule_days" value="{{value}}" autocomplete="off" {{#if checked}}checked{{/if}} />
<label class="btn btn-outline-primary flex-fill schedule-day-button" for="schedule-day-{{value}}">{{label}}</label>
{{/each}}
</div>
</div>
</div>
</div>
<div class="card-footer d-flex justify-content-end">
<div class="btn-group" role="group" aria-label="Slide schedule actions">
<button type="submit" class="btn btn-primary">Save schedule</button>
<button type="button" class="btn btn-warning" data-confirm-unsaved="You have unsaved changes. Leave this page?" onclick="window.closeScheduleModal && window.closeScheduleModal();">Cancel</button>
</div>
</div>
</form>
</div>