Release 2.7.4
Publish Docker Image / build-and-push (./build/Dockerfile, git.lzstealth.com/lzstealth/pulse-signage-web, web) (push) Successful in 1m15s
Publish Docker Image / build-and-push (./build/Dockerfile.player, git.lzstealth.com/lzstealth/pulse-signage-player, player) (push) Successful in 32s
Publish Docker Image / build-and-push (./build/Dockerfile, git.lzstealth.com/lzstealth/pulse-signage-web, web) (push) Successful in 1m15s
Publish Docker Image / build-and-push (./build/Dockerfile.player, git.lzstealth.com/lzstealth/pulse-signage-player, player) (push) Successful in 32s
This commit is contained in:
1 parent
f0177e6628
commit
1f1ad5d61f
16 files changed
+198
-11
No files matched your search
@@ -2,6 +2,16 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
## 2.7.4 - 2026-08-15
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- A new About page.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Refreshed the vendored AdminLTE assets to 4.3.1.
|
||||||
|
|
||||||
## 2.7.3 - 2026-08-15
|
## 2.7.3 - 2026-08-15
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pulse-signage-player",
|
"name": "pulse-signage-player",
|
||||||
"version": "2.7.3",
|
"version": "2.7.4",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "Pulse Signage player application bundle",
|
"description": "Pulse Signage player application bundle",
|
||||||
"main": "src/common.js",
|
"main": "src/common.js",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pulse-signage-web",
|
"name": "pulse-signage-web",
|
||||||
"version": "2.7.3",
|
"version": "2.7.4",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "Pulse Signage web and bridge application bundle",
|
"description": "Pulse Signage web and bridge application bundle",
|
||||||
"main": "src/common.js",
|
"main": "src/common.js",
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "pulse-signage",
|
"name": "pulse-signage",
|
||||||
"version": "2.7.3",
|
"version": "2.7.4",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "pulse-signage",
|
"name": "pulse-signage",
|
||||||
"version": "2.7.3",
|
"version": "2.7.4",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sparticuz/chromium": "^137.0.0",
|
"@sparticuz/chromium": "^137.0.0",
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pulse-signage",
|
"name": "pulse-signage",
|
||||||
"version": "2.7.3",
|
"version": "2.7.4",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "Pulse Signage application with MySQL and media storage",
|
"description": "Pulse Signage application with MySQL and media storage",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ function routePath(...segments) {
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
renderLoginPage: require(routePath('auth', 'login')),
|
renderLoginPage: require(routePath('auth', 'login')),
|
||||||
renderAccountPage: require(routePath('account', 'password')),
|
renderAccountPage: require(routePath('account', 'password')),
|
||||||
|
renderAboutPage: require(routePath('settings', 'about', 'index')),
|
||||||
renderUsersPage: require(routePath('settings', 'users', 'list')),
|
renderUsersPage: require(routePath('settings', 'users', 'list')),
|
||||||
renderUsersAddPage: require(routePath('settings', 'users', 'add')),
|
renderUsersAddPage: require(routePath('settings', 'users', 'add')),
|
||||||
renderUsersEditPage: require(routePath('settings', 'users', 'edit')),
|
renderUsersEditPage: require(routePath('settings', 'users', 'edit')),
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
@charset "UTF-8";/*!
|
@charset "UTF-8";/*!
|
||||||
* AdminLTE v4.1.0
|
* AdminLTE v4.3.1
|
||||||
* Author: Colorlib
|
* Author: Colorlib
|
||||||
* Website: AdminLTE.io <https://adminlte.io>
|
* Website: AdminLTE.io <https://adminlte.io>
|
||||||
* License: Open source - MIT <https://opensource.org/licenses/MIT>
|
* License: Open source - MIT <https://opensource.org/licenses/MIT>
|
||||||
*/:root,[data-bs-theme=light]{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-black:#000;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-primary-rgb:13,110,253;--bs-secondary-rgb:108,117,125;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-primary-text-emphasis:rgb(5.2, 44, 101.2);--bs-secondary-text-emphasis:rgb(43.2, 46.8, 50);--bs-success-text-emphasis:rgb(10, 54, 33.6);--bs-info-text-emphasis:rgb(5.2, 80.8, 96);--bs-warning-text-emphasis:rgb(102, 77.2, 2.8);--bs-danger-text-emphasis:rgb(88, 21.2, 27.6);--bs-light-text-emphasis:#495057;--bs-dark-text-emphasis:#495057;--bs-primary-bg-subtle:rgb(206.6, 226, 254.6);--bs-secondary-bg-subtle:rgb(225.6, 227.4, 229);--bs-success-bg-subtle:rgb(209, 231, 220.8);--bs-info-bg-subtle:rgb(206.6, 244.4, 252);--bs-warning-bg-subtle:rgb(255, 242.6, 205.4);--bs-danger-bg-subtle:rgb(248, 214.6, 217.8);--bs-light-bg-subtle:rgb(251.5, 252, 252.5);--bs-dark-bg-subtle:#ced4da;--bs-primary-border-subtle:rgb(158.2, 197, 254.2);--bs-secondary-border-subtle:rgb(196.2, 199.8, 203);--bs-success-border-subtle:rgb(163, 207, 186.6);--bs-info-border-subtle:rgb(158.2, 233.8, 249);--bs-warning-border-subtle:rgb(255, 230.2, 155.8);--bs-danger-border-subtle:rgb(241, 174.2, 180.6);--bs-light-border-subtle:#e9ecef;--bs-dark-border-subtle:#adb5bd;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-font-sans-serif:"Source Sans 3",system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans","Liberation Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-color-rgb:33,37,41;--bs-body-bg:#fff;--bs-body-bg-rgb:255,255,255;--bs-emphasis-color:#000;--bs-emphasis-color-rgb:0,0,0;--bs-secondary-color:rgba(33, 37, 41, 0.75);--bs-secondary-color-rgb:33,37,41;--bs-secondary-bg:#e9ecef;--bs-secondary-bg-rgb:233,236,239;--bs-tertiary-color:rgba(33, 37, 41, 0.5);--bs-tertiary-color-rgb:33,37,41;--bs-tertiary-bg:#f8f9fa;--bs-tertiary-bg-rgb:248,249,250;--bs-heading-color:inherit;--bs-link-color:#0d6efd;--bs-link-color-rgb:13,110,253;--bs-link-decoration:underline;--bs-link-hover-color:rgb(10.4, 88, 202.4);--bs-link-hover-color-rgb:10,88,202;--bs-code-color:#d63384;--bs-highlight-color:#212529;--bs-highlight-bg:rgb(255, 242.6, 205.4);--bs-border-width:1px;--bs-border-style:solid;--bs-border-color:#dee2e6;--bs-border-color-translucent:rgba(0, 0, 0, 0.175);--bs-border-radius:0.375rem;--bs-border-radius-sm:0.25rem;--bs-border-radius-lg:0.5rem;--bs-border-radius-xl:1rem;--bs-border-radius-xxl:2rem;--bs-border-radius-2xl:var(--bs-border-radius-xxl);--bs-border-radius-pill:50rem;--bs-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-box-shadow-sm:0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-box-shadow-lg:0 1rem 3rem rgba(0, 0, 0, 0.175);--bs-box-shadow-inset:inset 0 1px 2px rgba(0, 0, 0, 0.075);--bs-focus-ring-width:0.25rem;--bs-focus-ring-opacity:0.25;--bs-focus-ring-color:rgba(13, 110, 253, 0.25);--bs-form-valid-color:#198754;--bs-form-valid-border-color:#198754;--bs-form-invalid-color:#dc3545;--bs-form-invalid-border-color:#dc3545}[data-bs-theme=dark]{color-scheme:dark;--bs-body-color:#dee2e6;--bs-body-color-rgb:222,226,230;--bs-body-bg:#212529;--bs-body-bg-rgb:33,37,41;--bs-emphasis-color:#fff;--bs-emphasis-color-rgb:255,255,255;--bs-secondary-color:rgba(222, 226, 230, 0.75);--bs-secondary-color-rgb:222,226,230;--bs-secondary-bg:#343a40;--bs-secondary-bg-rgb:52,58,64;--bs-tertiary-color:rgba(222, 226, 230, 0.5);--bs-tertiary-color-rgb:222,226,230;--bs-tertiary-bg:rgb(42.5, 47.5, 52.5);--bs-tertiary-bg-rgb:43,48,53;--bs-primary-text-emphasis:rgb(109.8, 168, 253.8);--bs-secondary-text-emphasis:rgb(166.8, 172.2, 177);--bs-success-text-emphasis:rgb(117, 183, 152.4);--bs-info-text-emphasis:rgb(109.8, 223.2, 246);--bs-warning-text-emphasis:rgb(255, 217.8, 106.2);--bs-danger-text-emphasis:rgb(234, 133.8, 143.4);--bs-light-text-emphasis:#f8f9fa;--bs-dark-text-emphasis:#dee2e6;--bs-primary-bg-subtle:rgb(2.6, 22, 50.6);--bs-secondary-bg-subtle:rgb(21.6, 23.4, 25);--bs-success-bg-subtle:rgb(5, 27, 16.8);--bs-Line truncated
|
*/:root,[data-bs-theme=light]{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-black:#000;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-primary-rgb:13,110,253;--bs-secondary-rgb:108,117,125;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-primary-text-emphasis:rgb(2.0392156863%, 17.2549019608%, 39.6862745098%);--bs-secondary-text-emphasis:rgb(16.9411764706%, 18.3529411765%, 19.6078431373%);--bs-success-text-emphasis:rgb(3.9215686275%, 21.1764705882%, 13.1764705882%);--bs-info-text-emphasis:rgb(2.0392156863%, 31.6862745098%, 37.6470588235%);--bs-warning-text-emphasis:rgb(40%, 30.2745098039%, 1.0980392157%);--bs-danger-text-emphasis:rgb(34.5098039216%, 8.3137254902%, 10.8235294118%);--bs-light-text-emphasis:#495057;--bs-dark-text-emphasis:#495057;--bs-primary-bg-subtle:rgb(81.0196078431%, 88.6274509804%, 99.8431372549%);--bs-secondary-bg-subtle:rgb(88.4705882353%, 89.1764705882%, 89.8039215686%);--bs-success-bg-subtle:rgb(81.9607843137%, 90.5882352941%, 86.5882352941%);--bs-info-bg-subtle:rgb(81.0196078431%, 95.8431372549%, 98.8235294118%);--bs-warning-bg-subtle:rgb(100%, 95.137254902%, 80.5490196078%);--bs-danger-bg-subtle:rgb(97.2549019608%, 84.1568627451%, 85.4117647059%);--bs-light-bg-subtle:rgb(98.6274509804%, 98.8235294118%, 99.0196078431%);--bs-dark-bg-subtle:#ced4da;--bs-primary-border-subtle:rgb(62.0392156863%, 77.2549019608%, 99.6862745098%);--bs-secondary-border-subtle:rgb(76.9411764706%, 78.3529411765%, 79.6078431373%);--bs-success-border-subtle:rgb(63.9215686275%, 81.1764705882%, 73.1764705882%);--bs-info-border-subtle:rgb(62.0392156863%, 91.6862745098%, 97.6470588235%);--bs-warning-border-subtle:rgb(100%, 90.2745098039%, 61.0980392157%);--bs-danger-border-subtle:rgb(94.5098039216%, 68.3137254902%, 70.8235294118%);--bs-light-border-subtle:#e9ecef;--bs-dark-border-subtle:#adb5bd;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-font-sans-serif:"Source Sans 3",system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans","Liberation Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-color-rgb:33,37,41;--bs-body-bg:#fff;--bs-body-bg-rgb:255,255,255;--bs-emphasis-color:#000;--bs-emphasis-color-rgb:0,0,0;--bs-secondary-color:rgba(33, 37, 41, 0.75);--bs-secondary-color-rgb:33,37,41;--bs-secondary-bg:#e9ecef;--bs-secondary-bg-rgb:233,236,239;--bs-tertiary-color:rgba(33, 37, 41, 0.5);--bs-tertiary-color-rgb:33,37,41;--bs-tertiary-bg:#f8f9fa;--bs-tertiary-bg-rgb:248,249,250;--bs-heading-color:inherit;--bs-link-color:#0d6efd;--bs-link-color-rgb:13,110,253;--bs-link-decoration:underline;--bs-link-hover-color:rgb(4.0784313725%, 34.5098039216%, 79.3725490196%);--bs-link-hover-color-rgb:10,88,202;--bs-code-color:#d63384;--bs-highlight-color:#212529;--bs-highlight-bg:rgb(100%, 95.137254902%, 80.5490196078%);--bs-border-width:1px;--bs-border-style:solid;--bs-border-color:#dee2e6;--bs-border-color-translucent:rgba(0, 0, 0, 0.175);--bs-border-radius:0.375rem;--bs-border-radius-sm:0.25rem;--bs-border-radius-lg:0.5rem;--bs-border-radius-xl:1rem;--bs-border-radius-xxl:2rem;--bs-border-radius-2xl:var(--bs-border-radius-xxl);--bs-border-radius-pill:50rem;--bs-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-box-shadow-sm:0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-box-shadow-lg:0 1rem 3rem rgba(0, 0, 0, 0.175);--bs-box-shadow-inset:inset 0 1px 2px rgba(0, 0, 0, 0.075);--bs-focus-ring-width:0.25rem;--bs-focus-ring-opacity:0.25;--bs-focus-ring-color:rgba(13, 110, 253, 0.25);--bs-form-valid-color:#198754;--bs-form-valid-border-color:#198754;--bs-form-invalid-color:#dc3545;--bs-form-invalid-border-color:#dc3545}[data-bs-theme=dark]{color-scheme:dark;--bs-body-color:#dee2e6;--bs-body-color-rgb:222,226,230;--bs-body-bg:#212529;--bs-body-bg-rgb:33,37,41;--bs-emphasis-color:#fff;--bs-emphasis-color-rgb:255,255,255;--bs-secondary-color:rgba(222, 226, 230, 0.75);--bs-secondary-color-rgb:222,226,230;--bs-secondary-bg:#343a40;--bs-secondary-bg-rgb:52,58,64;--bs-tertiary-color:rgba(222, 226, 230, 0.5);--bs-tertiaLine truncated
|
||||||
/*# sourceMappingURL=adminlte.min.css.map */
|
/*# sourceMappingURL=adminlte.min.css.map */
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["../../src/scss/adminlte.scss","../../node_modules/bootstrap/scss/_root.scss","dist/css/adminlte.css","../../node_modules/bootstrap/scss/vendor/_rfs.scss","../../node_modules/bootstrap/scss/mixins/_color-mode.scss","../../node_modules/bootstrap/scss/_reboot.scss","../../node_modules/bootstrap/scss/mixins/_border-radius.scss","../../node_modules/bootstrap/scss/_type.scss","../../node_modules/bootstrap/scss/mixins/_lists.scss","../../node_modules/bootstrap/scss/_images.scss","../../node_modules/bootstrap/scss/mixins/_image.scss","../../node_modules/bootstrap/scss/mixins/_box-shadow.scss","../../node_modules/bootstrap/scss/_containers.scss","../../node_modules/bootstrap/scss/mixins/_container.scss","../../node_modules/bootstrap/scss/mixins/_breakpoints.scss","../../node_modules/bootstrap/scss/_grid.scss","../../node_modules/bootstrap/scss/mixins/_grid.scss","../../node_modules/bootstrap/scss/_tables.scss","../../node_modules/bootstrap/scss/mixins/_table-variants.scss","../../node_modules/bootstrap/scss/forms/_labels.scss","../../node_modules/bootstrap/scss/forms/_form-text.scss","../../node_modules/bootstrap/scss/forms/_form-control.scss","../../node_modules/bootstrap/scss/mixins/_transition.scss","../../node_modules/bootstrap/scss/mixins/_gradients.scss","../../node_modules/bootstrap/scss/forms/_form-select.scss","../../node_modules/bootstrap/scss/forms/_form-check.scss","../../node_modules/bootstrap/scss/forms/_form-range.scss","../../node_modules/bootstrap/scss/forms/_floating-labels.scss","../../node_modules/bootstrap/scss/forms/_input-group.scss","../../node_modules/bootstrap/scss/mixins/_forms.scss","../../node_modules/bootstrap/scss/_buttons.scss","../../node_modules/bootstrap/scss/mixins/_buttons.scss","../../node_modules/bootstrap/scss/_transitions.scss","../../node_modules/bootstrap/scss/_dropdown.scss","../../node_modules/bootstrap/scss/mixins/_caret.scss","../../node_modules/bootstrap/scss/_button-group.scss","../../node_modules/bootstrap/scss/_nav.scss","../../node_modules/bootstrap/scss/_navbar.scss","../../node_modules/bootstrap/scss/_card.scss","../../node_modules/bootstrap/scss/_accordion.scss","../../node_modules/bootstrap/scss/_breadcrumb.scss","../../node_modules/bootstrap/scss/_pagination.scss","../../node_modules/bootstrap/scss/mixins/_pagination.scss","../../node_modules/bootstrap/scss/_badge.scss","../../node_modules/bootstrap/scss/_alert.scss","../../node_modules/bootstrap/scss/_progress.scss","../../node_modules/bootstrap/scss/_list-group.scss","../../node_modules/bootstrap/scss/_close.scss","../../node_modules/bootstrap/scss/_toasts.scss","../../node_modules/bootstrap/scss/_modal.scss","../../node_modules/bootstrap/scss/mixins/_backdrop.scss","../../node_modules/bootstrap/scss/_tooltip.scss","../../node_modules/bootstrap/scss/mixins/_reset-text.scss","../../node_modules/bootstrap/scss/_popover.scss","../../node_modules/bootstrap/scss/_carousel.scss","../../node_modules/bootstrap/scss/mixins/_clearfix.scss","../../node_modules/bootstrap/scss/_spinners.scss","../../node_modules/bootstrap/scss/_offcanvas.scss","../../node_modules/bootstrap/scss/_placeholders.scss","../../node_modules/bootstrap/scss/helpers/_color-bg.scss","../../node_modules/bootstrap/scss/helpers/_colored-links.scss","../../node_modules/bootstrap/scss/helpers/_focus-ring.scss","../../node_modules/bootstrap/scss/helpers/_icon-link.scss","../../node_modules/bootstrap/scss/helpers/_ratio.scss","../../node_modules/bootstrap/scss/helpers/_position.scss","../../node_modules/bootstrap/scss/helpers/_stacks.scss","../../node_modules/bootstrap/scss/helpers/_visually-hidden.scss","../../node_modules/bootstrap/scss/mixins/_visually-hidden.scss","../../node_modules/bootstrap/scss/helpers/_stretched-link.scss","../../node_modules/bootstrap/scss/helpers/_text-truncation.scss","../../node_modules/bootstrap/scss/mixins/_text-truncate.scss","../../node_modules/bootstrap/scss/helpers/_vr.scss","../../node_modules/bootstrap/scss/mixins/_utilities.scss","../../node_modules/bootstrap/scss/utilities/_api.scss","../../src/scss/mixins/_animations.scss","../../src/scss/_root.scss","../../src/scss/_app-wrapper.scss","../../src/scss/_app-content.scss","../../src/scss/_app-header.scss","../../src/scss/_app-sidebar.scss","../../src/scss/mixins/_scrollbar.scss","../../src/scss/_app-main.scss","../../src/scss/_app-footer.scss","../../src/scss/_dropdown.scss","../../src/scss/_callouts.scss","../../src/scss/_compact-mode.scss","../../src/scss/_progress-bars.scss","../../src/scss/_cards.scss","../../src/scss/_table.scss","../../src/scss/_small-box.scss","../../src/scss/_info-box.scss","../../src/scss/_timeline.scss","../../src/scss/_direct-chat.scss","../../src/scss/_toasts.scss","../../src/scss/_ribbon.scss","../../src/scss/_widgets.scss","../../src/scss/pages/_login_and_register.scss","../../src/scss/pages/_lockscreen.scss","../../src/scss/_miscellaneous.scss","../../src/scss/_accessibility.scss"],"names":[],"mappings":"iBAAA;Line truncated
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
/*!
|
/*!
|
||||||
* AdminLTE v4.1.0 (https://adminlte.io)
|
* AdminLTE v4.3.1 (https://adminlte.io)
|
||||||
* Copyright 2014-2026 Colorlib <https://colorlib.com>
|
* Copyright 2014-2026 Colorlib <https://colorlib.com>
|
||||||
* Licensed under MIT (https://github.com/ColorlibHQ/AdminLTE/blob/master/LICENSE)
|
* Licensed under MIT (https://github.com/ColorlibHQ/AdminLTE/blob/master/LICENSE)
|
||||||
*/
|
*/
|
||||||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).adminlte={})}(this,function(e){"use strict";const t=[],n={controller:new AbortController,hasInitialized:!1},i=()=>n.controller.signal,s=()=>{if(!n.hasInitialized){n.hasInitialized=!0;for(const e of t)e()}},a=e=>{t.push(e),n.hasInitialized&&e()};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",s,{once:!0}):s(),document.addEventListener("turbo:before-render",()=>{n.controller.abort(),n.controller=new AbortController,n.hasInitialized=!1}),document.addEventListener("turbo:load",s);const o=new WeakMap,r=e=>{const t=o.get(e)??[];for(const e of t)globalThis.clearTimeout(e);o.delete(e)},c=e=>{for(const t of["height","padding-top","padding-bottom","margin-top","margin-bottom","overflow","transition-duration","transition-property"])e.style.removeProperty(t)},l=(e,t=500)=>{if(r(e),t<=1)return e.style.display="none",void c(e);e.style.transitionProperty="height, margin, padding",e.style.transitionDuration=`${t}ms`,e.style.boxSizing="border-box",e.style.height=`${e.offsetHeight}px`,e.style.overflow="hidden";const n=globalThis.setTimeout(()=>{e.style.height="0",e.style.paddingTop="0",e.style.paddingBottom="0",e.style.marginTop="0",e.style.marginBottom="0"},1),i=globalThis.setTimeout(()=>{e.style.display="none",c(e),o.delete(e)},t);o.set(e,[n,i])},d=(e,t=500)=>{r(e),c(e),e.style.removeProperty("display");let{display:n}=globalThis.getComputedStyle(e);if("none"===n&&(n="block"),e.style.display=n,t<=1)return;const i=e.offsetHeight;e.style.overflow="hidden",e.style.height="0",e.style.paddingTop="0",e.style.paddingBottom="0",e.style.marginTop="0",e.style.marginBottom="0";const s=globalThis.setTimeout(()=>{e.style.boxSizing="border-box",e.style.transitionProperty="height, margin, padding",e.style.transitionDuration=`${t}ms`,e.style.height=`${i}px`,e.style.removeProperty("padding-top"),e.style.removeProperty("padding-bottom"),e.style.removeProperty("margin-top"),e.style.removeProperty("margin-bottom")},1),a=globalThis.setTimeout(()=>{c(e),o.delete(e)},t);o.set(e,[s,a])},u="hold-transition";class h{_element;_holdTransitionTimer;constructor(e){this._element=e,this._holdTransitionTimer=void 0}holdTransition(e=100){this._holdTransitionTimer&&clearTimeout(this._holdTransitionTimer),document.body.classList.add(u),this._holdTransitionTimer=setTimeout(()=>{document.body.classList.remove(u)},e)}}a(()=>{const e=new h(document.body);window.addEventListener("resize",()=>e.holdTransition(200),{signal:i()}),setTimeout(()=>{document.body.classList.add("app-loaded")},400)});const m=new WeakMap;class p{static get NAME(){throw new Error("Component subclasses must override the static NAME getter.")}static get DATA_KEY(){return`lte.${this.NAME}`}static _getInstance(e){return e?m.get(e)?.get(this.DATA_KEY)??null:null}_element;constructor(e){this._element=e;const t=m.get(e)??new Map;m.set(e,t),t.set(this.constructor.DATA_KEY,this)}dispose(){const e=m.get(this._element);e?.delete(this.constructor.DATA_KEY),0===e?.size&&m.delete(this._element)}}const g=(e,t,n={})=>{const i=new CustomEvent(t,{bubbles:!0,cancelable:n.cancelable??!1,detail:n.detail});return e.dispatchEvent(i),i},b="card-widget",f=`.lte.${b}`,v=`collapse${f}`,y=`expand${f}`,_=`remove${f}`,S=`collapsed${f}`,E=`expanded${f}`,L=`removed${f}`,A=`maximized${f}`,T=`minimized${f}`,k="card",x="collapsed-card",w="collapsing-card",M="expanding-card",q="was-collapsed",$="maximized-card",I='[data-lte-toggle="card-remove"]',C='[data-lte-toggle="card-collapse"]',N='[data-lte-toggle="card-maximize"]',P=`.${k}`,z=".card-body",D=".card-footer",R={animationSpeed:500,collapseTrigger:C,removeTrigger:I,maximizeTrigger:N};class B extends p{static get NAME(){return b}static getInstance(e){return this._getInstance(e)}static getOrCreateInstance(e,t={}){return this.getInstance(e)??new this(e,t)}_parent;_config;constructor(e,t={}){super(e),this._parent=e.closest(P),e.classList.contains(k)&&(this._parent=e),this._config={...R,...t}}collapse(){this._parent&&(g(this._parent,v,{cancelable:!0}).defaultPrevented||(this._parent.classList.add(w),this._parent.classList.remove(M),this._parent.querySelectorAll(`:scope > ${z}, :scope > ${D}`).forEach(e=>{e instanceof HTMLElement&&l(e,this._config.animationSpeed)}),setTimeout(()=>{this._parent?.classList.contains(w)&&(this._parent.classList.add(x),this._parent.classList.remove(w),g(this._parent,S))},this._config.animationSpeed)))}expand(){this._parent&&(g(this._parent,y,{cancelable:!0}).defaultPrevented||(this._parent.classList.add(M),this._parent.classList.remove(w,x),this._parent.querySelectorAll(`:scope > ${z}, :scope > ${D}`).forEach(e=>{e instanceof HTMLElement&&d(e,this._config.animationSpeed)}),setTimeout(()=>{this._parent?.classList.contains(M)&&(this._parent.classList.remove(M),g(this._parent,E))},this._config.animationSpeed)))}remove(){if(!this._pareLine truncated
|
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).adminlte={})}(this,function(e){"use strict";const t=[],n={controller:new AbortController,hasInitialized:!1,isReplaying:!1},s=()=>n.controller.signal,i=()=>{if(!n.hasInitialized){n.hasInitialized=!0,n.isReplaying=!0;try{for(const e of t)e()}finally{n.isReplaying=!1}}},a=e=>{t.push(e),n.hasInitialized&&e()},o=()=>{n.controller.abort(),n.controller=new AbortController,n.hasInitialized=!1},r=()=>{n.isReplaying||(o(),i())};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",r,{once:!0}):i(),document.addEventListener("turbo:before-render",o),document.addEventListener("turbo:load",i);const c=new WeakMap,l=e=>{const t=c.get(e)??[];for(const e of t)globalThis.clearTimeout(e);c.delete(e)},d=e=>{for(const t of["height","padding-top","padding-bottom","margin-top","margin-bottom","overflow","transition-duration","transition-property"])e.style.removeProperty(t)},u=(e,t=500)=>{if(l(e),t<=1)return e.style.display="none",void d(e);e.style.transitionProperty="height, margin, padding",e.style.transitionDuration=`${t}ms`,e.style.boxSizing="border-box",e.style.height=`${e.offsetHeight}px`,e.style.overflow="hidden";const n=globalThis.setTimeout(()=>{e.style.height="0",e.style.paddingTop="0",e.style.paddingBottom="0",e.style.marginTop="0",e.style.marginBottom="0"},1),s=globalThis.setTimeout(()=>{e.style.display="none",d(e),c.delete(e)},t);c.set(e,[n,s])},h=(e,t=500)=>{l(e),d(e),e.style.removeProperty("display");let{display:n}=globalThis.getComputedStyle(e);if("none"===n&&(n="block"),e.style.display=n,t<=1)return;const s=e.offsetHeight;e.style.overflow="hidden",e.style.height="0",e.style.paddingTop="0",e.style.paddingBottom="0",e.style.marginTop="0",e.style.marginBottom="0";const i=globalThis.setTimeout(()=>{e.style.boxSizing="border-box",e.style.transitionProperty="height, margin, padding",e.style.transitionDuration=`${t}ms`,e.style.height=`${s}px`,e.style.removeProperty("padding-top"),e.style.removeProperty("padding-bottom"),e.style.removeProperty("margin-top"),e.style.removeProperty("margin-bottom")},1),a=globalThis.setTimeout(()=>{d(e),c.delete(e)},t);c.set(e,[i,a])},m="hold-transition";class p{_element;_holdTransitionTimer;constructor(e){this._element=e,this._holdTransitionTimer=void 0}holdTransition(e=100){this._holdTransitionTimer&&clearTimeout(this._holdTransitionTimer),document.body.classList.add(m),this._holdTransitionTimer=setTimeout(()=>{document.body.classList.remove(m)},e)}}a(()=>{const e=new p(document.body);window.addEventListener("resize",()=>e.holdTransition(200),{signal:s()}),setTimeout(()=>{document.body.classList.add("app-loaded")},400)});const g=new WeakMap;class f{static get NAME(){throw new Error("Component subclasses must override the static NAME getter.")}static get DATA_KEY(){return`lte.${this.NAME}`}static _getInstance(e){return e?g.get(e)?.get(this.DATA_KEY)??null:null}_element;constructor(e){this._element=e;const t=g.get(e)??new Map;g.set(e,t),t.set(this.constructor.DATA_KEY,this)}dispose(){const e=g.get(this._element);e?.delete(this.constructor.DATA_KEY),0===e?.size&&g.delete(this._element)}}const b=(e,t,n={})=>{const s=new CustomEvent(t,{bubbles:!0,cancelable:n.cancelable??!1,detail:n.detail});return e.dispatchEvent(s),s},y="card-widget",v=`.lte.${y}`,_=`collapse${v}`,S=`expand${v}`,E=`remove${v}`,L=`collapsed${v}`,A=`expanded${v}`,T=`removed${v}`,k=`maximized${v}`,w=`minimized${v}`,x="card",q="collapsed-card",M="collapsing-card",$="expanding-card",I="was-collapsed",C="maximized-card",N='[data-lte-toggle="card-remove"]',P='[data-lte-toggle="card-collapse"]',R='[data-lte-toggle="card-maximize"]',z=`.${x}`,B=".card-body",D=".card-footer",O={animationSpeed:500,collapseTrigger:P,removeTrigger:N,maximizeTrigger:R};class F extends f{static get NAME(){return y}static getInstance(e){return this._getInstance(e)}static getOrCreateInstance(e,t={}){return this.getInstance(e)??new this(e,t)}_parent;_config;constructor(e,t={}){super(e),this._parent=e.closest(z),e.classList.contains(x)&&(this._parent=e),this._config={...O,...t}}collapse(){this._parent&&(b(this._parent,_,{cancelable:!0}).defaultPrevented||(this._parent.classList.add(M),this._parent.classList.remove($),this._parent.querySelectorAll(`:scope > ${B}, :scope > ${D}`).forEach(e=>{e instanceof HTMLElement&&u(e,this._config.animationSpeed)}),setTimeout(()=>{this._parent?.classList.contains(M)&&(this._parent.classList.add(q),this._parent.classList.remove(M),b(this._parent,L))},this._config.animationSpeed)))}expand(){this._parent&&(b(this._parent,S,{cancelable:!0}).defaultPrevented||(this._parent.classList.add($),this._parent.classList.remove(M,q),this._parent.querySelectorAll(`:scope > ${B}, :scope > ${D}`).forEach(e=>{e instanceof HTMLElement&&h(e,this._config.animationSpeed)}),setTimeout(()=>{this._parent?.classList.contains($)&&(this._paLine truncated
|
||||||
//# sourceMappingURL=adminlte.min.js.map
|
//# sourceMappingURL=adminlte.min.js.map
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["lifecycleCallbacks","lifecycleState","controller","AbortController","hasInitialized","isReplaying","getLifecycleSignal","signal","runLifecycleCallbacks","callback","onDOMContentLoaded","push","teardown","abort","initialize","document","readyState","addEventListener","once","slideTimers","WeakMap","cancelSlide","target","timers","get","timer","globalThis","clearTimeout","delete","clearSlideStyles","property","style","removeProperty","slideUp","duration","display","transitionProperty","transitionDuration","boxSizing","height","offsetHeight","overflow","stepTimer","setTimeout","paddingTop","paddingBottom","marginTop","marginBottom","cleanupTimer","set","slideDown","getComputedStyle","CLASS_NAME_HOLD_TRANSITIONS","Layout","_element","_holdTransitionTimer","constructor","element","this","undefined","holdTransition","time","body","classList","add","remove","layout","window","componentRegistry","BaseComponent","NAME","Error","DATA_KEY","_getInstance","instances","Map","dispose","size","dispatchCustomEvent","name","options","event","CustomEvent","bubbles","cancelable","detail","dispatchEvent","EVENT_KEY","EVENT_COLLAPSE","EVENT_EXPAND","EVENT_REMOVE","EVENT_COLLAPSED","EVENT_EXPANDED","EVENT_REMOVED","EVENT_MAXIMIZED","EVENT_MINIMIZED","CLASS_NAME_CARD","CLASS_NAME_COLLAPSED","CLASS_NAME_COLLAPSING","CLASS_NAME_EXPANDING","CLASS_NAME_WAS_COLLAPSED","CLASS_NAME_MAXIMIZED","SELECTOR_DATA_REMOVE","SELECTOR_DATA_COLLAPSE","SELECTOR_DATA_MAXIMIZE","SELECTOR_CARD","SELECTOR_CARD_BODY","SELECTOR_CARD_FOOTER","Default","animationSpeed","collapseTrigger","removeTrigger","maximizeTrigger","CardWidget","getInstance","getOrCreateInstance","config","_parent","_config","super","closest","contains","collapse","defaultPrevented","querySelectorAll","forEach","el","HTMLElement","expand","parent","toggle","maximize","width","offsetWidth","transition","htmlTag","querySelector","minimize","toggleMaximize","Element","trigger","preventDefault","card","widget","EVENT_LOAD_DATA_API","CLASS_NAME_MENU_OPEN","SELECTOR_NAV_ITEM","SELECTOR_NAV_LINK","SELECTOR_TREEVIEW_MENU","SELECTOR_DATA_TOGGLE","accordion","setAriaExpanded","navItem","expanded","link","setAttribute","String","Treeview","open","openMenuList","parentElement","openMenu","childElement","close","toggleRoot","targetItem","targetLink","getAttribute","accordionAttr","dataset","animationSpeedAttr","Number","menuItem","Event","root","item","CLASS_NAME_DIRECT_CHAT_OPEN","DirectChat","chatPane","SELECTOR_FULLSCREEN_TOGGLE","syncFullScreenState","iconMaximize","iconMinimize","isFullScreen","Boolean","fullscreenElement","eventName","button","FullScreen","inFullScreen","documentElement","requestFullscreen","catch","outFullscreen","exitFullscreen","toggleFullScreen","fullscreenEnabled","EVENT_OPEN","EVENT_OPENED","CLASS_NAME_SIDEBAR_OVERLAY","CLASS_NAME_SIDEBAR_COLLAPSE","CLASS_NAME_SIDEBAR_OPEN","SELECTOR_APP_SIDEBAR","STORAGE_KEY_SIDEBAR_STATE","Defaults","sidebarBreakpoint","enablePersistence","PushMenu","isCollapsed","isExplicitlyOpen","isMiniMode","isMobileSize","innerWidth","saveSidebarState","setupSidebarBreakPoint","sidebarExpand","content","getPropertyValue","breakpointValue","replace","isNaN","updateStateByResponsiveLogic","state","localStorage","setItem","loadSidebarState","storedState","getItem","clearSidebarState","removeItem","init","sidebar","sidebarBreakpointAttr","enablePersistenceAttr","pushMenu","matchMedia","appWrapper","sidebarOverlay","createElement","className","append","overlaySignal","overlayTouchMoved","passive","STORAGE_KEY","ATTRIBUTE_THEME","ATTRIBUTE_TOGGLE","SELECTOR_TOGGLE","THEMES","Set","isValidTheme","value","has","isDisabled","MARKUP_THEME","hasAttribute","declared","readMarkupTheme","ColorMode","getStoredTheme","stored","getMarkupTheme","getPreferredTheme","_prefersDark","resolveTheme","theme","setTheme","_applyTheme","_showActiveTheme","resolved","colorScheme","matches","isActive","icon","lteThemeIcon","colorMode","preferred","EVENT_FILTERED","SELECTOR_NAV_HEADER","submenuOf","setOpen","submenu","SidebarSearch","_menu","_emptyState","_snapshot","lteTarget","scope","search","term","menu","query","trim","toLowerCase","clear","_takeSnapshot","items","matched","label","textContent","includes","index","length","hidden","descendant","visible","header","snapshot","HTMLInputElement","key","input","AccessibilityManager","liveRegion","focusHistory","announcements","skipLinks","focusManagement","keyboardNavigation","reducedMotion","createLiveRegion","addSkipLinks","initFocusManagement","initKeyboardNavigation","respectReducedMotion","initErrorAnnouncements","initTableAccessibility","initFormAccessibility","existingRegion","getElementById","id","ensureSkipTargets","skipLinksContainer","skipToMain","href","skipToNav","insertBefore","firstChild","targets","fallbackSelector","handleEscapeKey","initModalFocusManagement","initDropdownFocusManagement","toggleButton","click","handleMenuNavigation","currentElement","menuItems","Array","from","filter"Line truncated
|
||||||
@@ -15,6 +15,7 @@ const registerRssFeedRoutes = require('./data-sources/rss-feeds/routes');
|
|||||||
const registerTimetableRoutes = require('./data-sources/timetables/routes');
|
const registerTimetableRoutes = require('./data-sources/timetables/routes');
|
||||||
const registerSettingsRoutes = require('./settings/background-tasks');
|
const registerSettingsRoutes = require('./settings/background-tasks');
|
||||||
const registerFontRoutes = require('./settings/fonts');
|
const registerFontRoutes = require('./settings/fonts');
|
||||||
|
const registerAboutRoutes = require('./settings/about/routes');
|
||||||
const registerInternalSyncRoutes = require('./internal/sync');
|
const registerInternalSyncRoutes = require('./internal/sync');
|
||||||
const registerScreensRoutes = require('./signage/screens/routes');
|
const registerScreensRoutes = require('./signage/screens/routes');
|
||||||
const { PERMISSIONS, normalizePermissionKeys } = require('#src/rbac');
|
const { PERMISSIONS, normalizePermissionKeys } = require('#src/rbac');
|
||||||
@@ -24,6 +25,7 @@ function registerRoutes(app, deps) {
|
|||||||
registerAuthAndAccountRoutes(app, deps);
|
registerAuthAndAccountRoutes(app, deps);
|
||||||
registerSignageRoutes(app, deps);
|
registerSignageRoutes(app, deps);
|
||||||
registerSettingsAndContentRoutes(app, deps);
|
registerSettingsAndContentRoutes(app, deps);
|
||||||
|
registerAboutRoutes(app, deps);
|
||||||
registerInternalSyncRoutes(app, {
|
registerInternalSyncRoutes(app, {
|
||||||
backgroundTaskQueue: deps.backgroundTaskQueue,
|
backgroundTaskQueue: deps.backgroundTaskQueue,
|
||||||
uploadSyncService: deps.uploadSyncService,
|
uploadSyncService: deps.uploadSyncService,
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// Settings page renderer for the About page.
|
||||||
|
|
||||||
|
const { renderView } = require('../../../view');
|
||||||
|
|
||||||
|
function buildLibraryList() {
|
||||||
|
return [
|
||||||
|
{ name: 'AdminLTE', version: '4.3.1', usage: 'Admin dashboard layout and components', license: 'MIT', url: 'https://adminlte.io/' },
|
||||||
|
{ name: 'Animate.css', version: '4.1.1', usage: 'Reusable animation classes', license: 'MIT', url: 'https://animate.style/' },
|
||||||
|
{ name: 'Bootstrap Icons', version: '1.11.3', usage: 'Icon set used throughout the UI', license: 'MIT', url: 'https://icons.getbootstrap.com/' },
|
||||||
|
{ name: 'Cropper.js', version: '1.6.2', usage: 'Image cropping in the slide editor', license: 'MIT', url: 'https://github.com/fengyuanchen/cropperjs' },
|
||||||
|
{ name: 'Express', version: '4.21.2', usage: 'Web server and routing', license: 'MIT', url: 'https://expressjs.com/' },
|
||||||
|
{ name: 'Handlebars', version: '4.7.8', usage: 'Server-side view rendering', license: 'MIT', url: 'https://handlebarsjs.com/' },
|
||||||
|
{ name: 'hls.js', version: '1.5.15', usage: 'HTTP Live Streaming playback', license: 'Apache-2.0', url: 'https://github.com/video-dev/hls.js' },
|
||||||
|
{ name: 'Multer', version: '2.2.0', usage: 'Multipart file uploads', license: 'MIT', url: 'https://github.com/expressjs/multer' },
|
||||||
|
{ name: 'MySQL2', version: '3.14.3', usage: 'Database access layer', license: 'MIT', url: 'https://github.com/sidorares/node-mysql2' },
|
||||||
|
{ name: 'Sharp', version: '0.35.3', usage: 'Image processing and transforms', license: 'Apache-2.0', url: 'https://sharp.pixelplumbing.com/' },
|
||||||
|
{ name: 'TinyMCE', version: '7.7.0', usage: 'Rich text editing for content fields', license: 'GPL-2.0-or-later', url: 'https://www.tiny.cloud/' },
|
||||||
|
{ name: 'ws', version: '8.21.0', usage: 'WebSocket transport', license: 'MIT', url: 'https://github.com/websockets/ws' }
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = function renderAboutPage(currentUser) {
|
||||||
|
return renderView('settings/about/index', {
|
||||||
|
title: 'About',
|
||||||
|
active: 'about',
|
||||||
|
currentUser: currentUser || null,
|
||||||
|
repositoryUrl: 'https://git.lzstealth.com/LZStealth/pulse-signage.git',
|
||||||
|
libraries: buildLibraryList(),
|
||||||
|
scripts: []
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// Settings route registration for the About page.
|
||||||
|
|
||||||
|
module.exports = function registerAboutRoutes(app, deps) {
|
||||||
|
const pages = deps.pages;
|
||||||
|
|
||||||
|
if (!pages) {
|
||||||
|
throw new Error('registerAboutRoutes requires pages.');
|
||||||
|
}
|
||||||
|
|
||||||
|
app.get('/settings/about', function (req, res) {
|
||||||
|
res.send(pages.renderAboutPage(req.currentUser));
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
<div class="page-header">
|
||||||
|
<div>
|
||||||
|
<h2>About Pulse Signage</h2>
|
||||||
|
<p>Version details and the main open-source libraries bundled with the app.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-12 col-xl-4">
|
||||||
|
<div class="card card-outline card-primary h-100">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="d-flex flex-wrap align-items-center justify-content-between gap-2 mb-3">
|
||||||
|
<div>
|
||||||
|
<div class="text-uppercase text-muted small fw-semibold">Application</div>
|
||||||
|
<div class="fs-3 fw-semibold">Pulse Signage</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-end">
|
||||||
|
<div class="text-muted small text-uppercase">Version</div>
|
||||||
|
<div class="fs-4 fw-semibold">v{{appVersion}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="text-muted mb-3">Pulse is a self-hosted signage platform for managing screens, playlists, slides, and data sources from one web interface.</p>
|
||||||
|
|
||||||
|
<div class="d-flex flex-wrap gap-2">
|
||||||
|
<a class="btn btn-primary" href="/dashboard"><i class="bi bi-speedometer2 me-1" aria-hidden="true"></i>Open dashboard</a>
|
||||||
|
<a class="btn btn-outline-secondary" href="{{repositoryUrl}}" target="_blank" rel="noreferrer"><i class="bi bi-github me-1" aria-hidden="true"></i>Repository</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-xl-8">
|
||||||
|
<div class="card card-outline card-primary h-100 overflow-hidden">
|
||||||
|
<div class="card-header">
|
||||||
|
<h3 class="card-title">Third-party libraries</h3>
|
||||||
|
</div>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped align-middle mb-0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Library</th>
|
||||||
|
<th>Version</th>
|
||||||
|
<th>Use</th>
|
||||||
|
<th>License</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{#each libraries}}
|
||||||
|
<tr>
|
||||||
|
<td class="fw-semibold">{{name}}</td>
|
||||||
|
<td>{{version}}</td>
|
||||||
|
<td>{{usage}}</td>
|
||||||
|
<td>
|
||||||
|
{{#if url}}
|
||||||
|
<a href="{{url}}" target="_blank" rel="noreferrer">{{license}}</a>
|
||||||
|
{{else}}
|
||||||
|
{{license}}
|
||||||
|
{{/if}}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -326,8 +326,9 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="sidebar-version mt-auto pt-2 text-secondary small">
|
<div class="sidebar-version mt-auto pt-2 text-secondary small">
|
||||||
<div class="sidebar-version__inner border-top pt-2">
|
<div class="sidebar-version__inner border-top pt-2 d-flex align-items-center justify-content-between gap-3">
|
||||||
v{{appVersion}}
|
<span>v{{appVersion}}</span>
|
||||||
|
<a href="/settings/about" class="text-secondary text-decoration-none text-nowrap">Pulse Signage</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
const test = require('node:test');
|
||||||
|
const assert = require('node:assert/strict');
|
||||||
|
|
||||||
|
require('../src/common');
|
||||||
|
|
||||||
|
const pages = require('../src/web/pages');
|
||||||
|
const registerAboutRoutes = require('../src/web/routes/settings/about/routes');
|
||||||
|
|
||||||
|
test('about page renders the footer link beside the version and lists bundled libraries', () => {
|
||||||
|
const html = pages.renderAboutPage({ id: 1 });
|
||||||
|
|
||||||
|
const orderedNames = ['AdminLTE', 'Animate.css', 'Bootstrap Icons', 'Cropper.js', 'Express', 'Handlebars', 'hls.js', 'Multer', 'MySQL2', 'Sharp', 'TinyMCE', 'ws'];
|
||||||
|
let previousIndex = -1;
|
||||||
|
|
||||||
|
orderedNames.forEach(function (name) {
|
||||||
|
const currentIndex = html.indexOf(name);
|
||||||
|
assert.ok(currentIndex >= 0, `missing ${name}`);
|
||||||
|
assert.ok(currentIndex > previousIndex, `${name} is out of order`);
|
||||||
|
previousIndex = currentIndex;
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.match(html, /v2\.7\.4/);
|
||||||
|
assert.match(html, /About Pulse/);
|
||||||
|
assert.match(html, /href="\/settings\/about"/);
|
||||||
|
assert.match(html, /Cropper\.js/);
|
||||||
|
assert.match(html, /TinyMCE/);
|
||||||
|
assert.match(html, /7\.7\.0/);
|
||||||
|
assert.match(html, /card card-outline card-primary h-100 overflow-hidden/);
|
||||||
|
assert.doesNotMatch(html, /rounded-3 overflow-hidden border/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('about route sends the about page', async () => {
|
||||||
|
const handlers = {};
|
||||||
|
const app = {
|
||||||
|
get(path, ...routeHandlers) {
|
||||||
|
handlers[path] = routeHandlers;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
registerAboutRoutes(app, {
|
||||||
|
pages: {
|
||||||
|
renderAboutPage(currentUser) {
|
||||||
|
return currentUser && currentUser.id === 7 ? 'about-page' : 'unexpected';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = {
|
||||||
|
body: null,
|
||||||
|
send(value) {
|
||||||
|
this.body = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
await handlers['/settings/about'][0]({ currentUser: { id: 7 } }, response, function () {});
|
||||||
|
|
||||||
|
assert.equal(response.body, 'about-page');
|
||||||
|
});
|
||||||
Reference in new issue
Block a user