Release v2.4.2

This commit is contained in:
2026-08-03 12:45:27 +01:00
parent 2b9cabdab2
commit e95928f31c
141 changed files with 7694 additions and 5078 deletions
@@ -0,0 +1,140 @@
<div class="page-header">
<div>
<h2>{{#if isEdit}}Edit announcement{{#if announcement.short_label}} - {{announcement.short_label}}{{/if}}{{else}}Add announcement{{/if}}</h2>
<p>{{#if isEdit}}Update the message, screens, color, or expiry behavior.{{else}}Give the announcement a short description, then define the full message and target screens.{{/if}}</p>
</div>
</div>
<form id="announcement-form" method="post" action="{{formAction}}" {{{formAttrs}}}>
<div class="row g-4 mb-4">
<div class="col-12 col-xl-7">
<div class="card card-outline card-primary admin-form-card h-100 mb-0">
<div class="card-header">
<h3 class="card-title">Content</h3>
</div>
<div class="card-body">
<div class="mb-4">
<label for="announcement-short-label" class="form-label">{{#if isEdit}}Descriptor{{else}}Short Description{{/if}}</label>
<input id="announcement-short-label" name="short_label" class="form-control" value="{{announcement.short_label}}" maxlength="255" placeholder="{{#if isEdit}}Power warning{{else}}Announcement Short Name{{/if}}" />
<div class="form-text">A short name for admin lists and page titles.</div>
</div>
<div class="mb-0">
<label for="announcement-message" class="form-label">Message</label>
<textarea id="announcement-message" name="message" class="form-control" rows="6" required placeholder="{{#if isEdit}}{{else}}Maintenance starts in 10 minutes.{{/if}}">{{announcement.message}}</textarea>
<div class="form-text">Use the full text that should appear on screens.</div>
</div>
<div class="row g-3 mb-0">
<div class="col-12 col-md-6 col-xl-12">
<label for="announcement-type" class="form-label">Type</label>
<select id="announcement-type" name="announcement_type" class="form-select">
{{#each announcementTypes}}
<option value="{{value}}" {{#if (eq ../announcement.announcement_type value)}}selected{{/if}}>{{label}}</option>
{{/each}}
</select>
</div>
<div class="col-12 col-md-6 col-xl-12">
<label for="announcement-color" class="form-label">Color</label>
<div class="row g-2 align-items-stretch">
<div class="col">
<select id="announcement-color" name="color_key" class="form-select">
{{#each announcementColors}}
<option value="{{value}}" {{#if (eq ../announcement.color_key value)}}selected{{/if}}>{{label}}</option>
{{/each}}
</select>
</div>
<div class="col-auto">
<div class="announcement-color-preview" data-announcement-color-preview aria-hidden="true"></div>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-xl-12">
<div class="form-label">Icon</div>
<div class="announcement-icon-picker" data-announcement-icon-picker-shell>
<select id="announcement-icon" name="icon_key" class="d-none" tabindex="-1" aria-hidden="true">
{{#each announcementIcons}}
<option value="{{value}}" {{#if (eq ../announcement.icon_key value)}}selected{{/if}}>{{label}}</option>
{{/each}}
</select>
<button type="button" class="announcement-icon-picker__toggle" data-announcement-icon-picker-toggle aria-expanded="false" aria-controls="announcement-icon-picker-menu">
<span class="announcement-icon-picker__toggle-icon" data-announcement-icon-picker-icon aria-hidden="true"></span>
<span class="announcement-icon-picker__toggle-label" data-announcement-icon-picker-label>Select an icon</span>
<i class="bi bi-chevron-down announcement-icon-picker__caret" aria-hidden="true"></i>
</button>
<div id="announcement-icon-picker-menu" class="announcement-icon-picker__menu" data-announcement-icon-picker hidden>
<div class="announcement-icon-picker__header">
<span>Choose an icon</span>
<button type="button" class="btn btn-sm btn-link text-decoration-none p-0" data-announcement-icon-picker-close>Close</button>
</div>
<div class="announcement-icon-picker__grid">
{{#each announcementIcons}}
<button type="button" class="announcement-icon-picker__option" data-announcement-icon-option data-icon-key="{{value}}" aria-pressed="false" title="{{label}}">
<i class="bi bi-{{value}}" aria-hidden="true"></i>
<span class="visually-hidden">{{label}}</span>
</button>
{{/each}}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-12 col-xl-5">
<div class="card card-outline card-primary admin-form-card mb-3">
<div class="card-footer d-flex flex-wrap align-items-center">
{{#if isEdit}}
<button type="submit" class="btn {{announcement.actionClassName}}" form="{{actionFormId}}"><i class="bi {{announcement.actionIcon}} me-1" aria-hidden="true"></i>{{announcement.actionLabel}}</button>
{{/if}}
<div class="ms-auto d-flex flex-wrap align-items-center gap-2">
{{{saveActionButtons formId="announcement-form" saveUrl=formAction cancelUrl=cancelUrl deleteUrl=deleteUrl deleteDisabled=deleteDisabled deleteTitle=announcement.deleteBlockedMessage showDelete=showDeleteAction showSaveAndClose=showSaveSecondaryActions showSaveAndNew=showSaveSecondaryActions}}}
</div>
</div>
</div>
<div class="card card-outline card-secondary admin-form-card mb-4">
<div class="card-header">
<h3 class="card-title">Screens</h3>
</div>
<div class="card-body">
<div class="row g-3 mb-0">
<div class="col-12 col-md-4 col-xl-12">
<label for="announcement-duration-mode" class="form-label">Display mode</label>
<select id="announcement-duration-mode" name="duration_mode" class="form-select">
<option value="duration" {{#if (eq announcement.durationMode 'duration')}}selected{{/if}}>For a duration</option>
<option value="until_disabled" {{#if (eq announcement.durationMode 'until_disabled')}}selected{{/if}}>Until disabled</option>
</select>
</div>
<div class="col-12" data-announcement-duration-value-fields {{#unless (eq announcement.durationMode 'duration')}}hidden{{/unless}}>
<div class="row g-3">
<div class="col-12 col-md-4 col-xl-6">
<label for="announcement-duration-value" class="form-label">Duration value</label>
<input id="announcement-duration-value" name="duration_value" type="number" min="1" class="form-control" value="{{announcement.durationValue}}" />
</div>
<div class="col-12 col-md-4 col-xl-6">
<label for="announcement-duration-unit" class="form-label">Unit</label>
<select id="announcement-duration-unit" name="duration_unit" class="form-select">
<option value="seconds" {{#if (eq announcement.durationUnit 'seconds')}}selected{{/if}}>Seconds</option>
<option value="minutes" {{#if (eq announcement.durationUnit 'minutes')}}selected{{/if}}>Minutes</option>
</select>
</div>
</div>
</div>
</div>
<label for="announcement-screens" class="form-label">Target screens</label>
<select id="announcement-screens" name="screen_ids[]" class="form-select" multiple size="8" required>
{{#each announcementScreens}}
<option value="{{id}}" {{#if isSelected}}selected{{/if}}>{{name}}{{#if slug}} ({{slug}}){{/if}}</option>
{{/each}}
</select>
<div class="form-text">Select one or more screens for this announcement.</div>
</div>
</div>
</div>
</div>
<script type="module" src="/assets/js/announcements/announcement-form.js?v={{appVersion}}"></script>
</form>
{{#if isEdit}}
<form id="announcement-action-form" method="post" action="{{announcement.actionPath}}" class="d-none" data-confirm-message="{{announcement.actionConfirmMessage}}" data-async-command></form>
<form id="delete-announcement-form" method="post" action="/announcements/{{announcement.id}}/delete" class="d-none" data-confirm-message="Delete this announcement?"></form>
{{/if}}