Display Module 13-05-2026
This commit is contained in:
parent
6a65354f4c
commit
9262132325
41 changed files with 496 additions and 334 deletions
|
|
@ -411,6 +411,56 @@
|
|||
.status-message { font-weight: 300; opacity: 0.7; }
|
||||
.status-error { color: #ef4444; font-weight: 500; }
|
||||
.status-sub { font-size: 1.4vh; opacity: 0.4; margin-top: 1vh; }
|
||||
|
||||
.display-overview {
|
||||
position: fixed; inset: 0; z-index: 10000;
|
||||
overflow-y: auto; background: radial-gradient(circle at top, #12364d 0, #05070a 42%, #000 100%);
|
||||
color: #fff; cursor: auto; padding: clamp(24px, 5vw, 72px);
|
||||
}
|
||||
.display-overview.hidden { display: none; }
|
||||
.display-overview__inner { width: min(1120px, 100%); margin: 0 auto; }
|
||||
.display-overview__eyebrow {
|
||||
color: #38bdf8; font-size: 13px; font-weight: 700;
|
||||
letter-spacing: 0.16em; text-transform: uppercase; margin-bottom: 12px;
|
||||
}
|
||||
.display-overview h1 {
|
||||
font-size: clamp(34px, 6vw, 76px); line-height: 0.95;
|
||||
letter-spacing: -0.05em; margin-bottom: 18px;
|
||||
}
|
||||
.display-overview__intro {
|
||||
max-width: 720px; color: rgba(255,255,255,0.68);
|
||||
font-size: clamp(16px, 2vw, 22px); line-height: 1.5; margin-bottom: 36px;
|
||||
}
|
||||
.display-overview__grid {
|
||||
display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
||||
gap: 18px;
|
||||
}
|
||||
.display-card {
|
||||
display: flex; flex-direction: column; gap: 16px;
|
||||
min-height: 220px; padding: 24px; border-radius: 28px;
|
||||
border: 1px solid rgba(255,255,255,0.14);
|
||||
background: rgba(255,255,255,0.08); color: #fff; text-decoration: none;
|
||||
box-shadow: 0 24px 70px rgba(0,0,0,0.24);
|
||||
transition: transform 0.18s ease, border-color 0.18s ease, background 0.18s ease;
|
||||
}
|
||||
.display-card:hover {
|
||||
transform: translateY(-2px);
|
||||
border-color: rgba(56,189,248,0.55);
|
||||
background: rgba(255,255,255,0.12);
|
||||
}
|
||||
.display-card__badges { display: flex; flex-wrap: wrap; gap: 8px; }
|
||||
.display-badge {
|
||||
border-radius: 999px; padding: 6px 10px; font-size: 12px; font-weight: 700;
|
||||
background: rgba(34,197,94,0.18); color: #86efac; border: 1px solid rgba(134,239,172,0.28);
|
||||
}
|
||||
.display-badge--live { background: rgba(56,189,248,0.18); color: #7dd3fc; border-color: rgba(125,211,252,0.28); }
|
||||
.display-card__title { font-size: 28px; font-weight: 700; letter-spacing: -0.03em; }
|
||||
.display-card__meta { display: grid; gap: 6px; color: rgba(255,255,255,0.62); font-size: 15px; }
|
||||
.display-card__action { margin-top: auto; color: #7dd3fc; font-weight: 700; }
|
||||
.display-overview__empty {
|
||||
border: 1px dashed rgba(255,255,255,0.24); border-radius: 28px;
|
||||
padding: 32px; color: rgba(255,255,255,0.62);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
|
@ -434,6 +484,18 @@
|
|||
<div class="status-sub">Neustart in Kürze...</div>
|
||||
</div>
|
||||
|
||||
<div class="display-overview hidden" id="display-overview">
|
||||
<div class="display-overview__inner">
|
||||
<div class="display-overview__eyebrow">Cabinet Display Player</div>
|
||||
<h1>Aktive Live-Displays</h1>
|
||||
<p class="display-overview__intro">
|
||||
Wählen Sie ein Display aus, um die veröffentlichte Live-Bespielung zu öffnen.
|
||||
Angezeigt werden nur aktive Displays mit veröffentlichter Live-Konfiguration.
|
||||
</p>
|
||||
<div class="display-overview__grid" id="display-overview-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function escapeHtml(value) {
|
||||
const div = document.createElement('div');
|
||||
|
|
@ -462,15 +524,12 @@ class DisplayPlayer {
|
|||
this.moduleId = this.detectModuleId();
|
||||
this.itemId = this.detectItemId();
|
||||
this.displayId = this.detectDisplayId();
|
||||
if (!this.displayId && !this.previewToken && !this.moduleId) {
|
||||
this.showError('Keine Display-ID oder Vorschau angegeben. URL: /display/index.html?id=1');
|
||||
return;
|
||||
}
|
||||
|
||||
// API
|
||||
this.BASE_URL = this.detectBaseUrl();
|
||||
this.API_CONFIG = this.detectConfigUrl();
|
||||
this.API_CHECK = this.detectCheckUrl();
|
||||
this.API_OVERVIEW = `${this.BASE_URL}/api/display/overview`;
|
||||
|
||||
// Timing
|
||||
this.POLL_INTERVAL = 60000;
|
||||
|
|
@ -497,6 +556,8 @@ class DisplayPlayer {
|
|||
this.loadingInfo = document.getElementById('loading-info');
|
||||
this.errorOverlay = document.getElementById('error-overlay');
|
||||
this.errorMessage = document.getElementById('error-message');
|
||||
this.overviewOverlay = document.getElementById('display-overview');
|
||||
this.overviewList = document.getElementById('display-overview-list');
|
||||
|
||||
this.loadingInfo.textContent = this.detectLoadingLabel();
|
||||
|
||||
|
|
@ -583,13 +644,16 @@ class DisplayPlayer {
|
|||
}
|
||||
return `Modul #${this.moduleId}`;
|
||||
}
|
||||
if (!this.displayId) {
|
||||
return 'Display-Übersicht';
|
||||
}
|
||||
return `Display #${this.displayId}`;
|
||||
}
|
||||
|
||||
detectBaseUrl() {
|
||||
const hostname = window.location.hostname;
|
||||
if (hostname === 'cabinet.b2in.eu' || hostname.includes('b2in.eu')) {
|
||||
return 'https://b2in.eu';
|
||||
if (hostname === 'cabinet.b2in.eu') {
|
||||
return 'https://portal.b2in.eu';
|
||||
}
|
||||
return window.location.origin;
|
||||
}
|
||||
|
|
@ -602,6 +666,11 @@ class DisplayPlayer {
|
|||
console.log(`[Display] Initializing ${this.detectLoadingLabel()}`);
|
||||
|
||||
try {
|
||||
if (!this.displayId && !this.previewToken && !this.moduleId) {
|
||||
await this.fetchOverview();
|
||||
return;
|
||||
}
|
||||
|
||||
await this.fetchConfig();
|
||||
|
||||
if (this.playlist.length === 0) {
|
||||
|
|
@ -642,6 +711,16 @@ class DisplayPlayer {
|
|||
console.log(`[Display] Loaded ${this.playlist.length} version(s)`);
|
||||
}
|
||||
|
||||
async fetchOverview() {
|
||||
const response = await fetch(this.API_OVERVIEW);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
this.renderOverview(data.displays || []);
|
||||
}
|
||||
|
||||
startPolling() {
|
||||
if (!this.API_CHECK) {
|
||||
return;
|
||||
|
|
@ -779,11 +858,50 @@ class DisplayPlayer {
|
|||
this.loadingOverlay.classList.add('hidden');
|
||||
}
|
||||
|
||||
renderOverview(displays) {
|
||||
this.hideLoading();
|
||||
this.errorOverlay.classList.add('hidden');
|
||||
this.overviewOverlay.classList.remove('hidden');
|
||||
|
||||
if (displays.length === 0) {
|
||||
this.overviewList.innerHTML = `
|
||||
<div class="display-overview__empty">
|
||||
Es sind aktuell keine aktiven Live-Displays veröffentlicht.
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
this.overviewList.innerHTML = displays.map(display => `
|
||||
<a class="display-card" href="${this.escapeHtml(display.url)}">
|
||||
<div class="display-card__badges">
|
||||
<span class="display-badge">Aktiv</span>
|
||||
<span class="display-badge display-badge--live">Live</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="display-card__title">${this.escapeHtml(display.name)}</div>
|
||||
<div class="display-card__meta">
|
||||
<span>Display-ID: ${this.escapeHtml(display.id)}</span>
|
||||
${display.location ? `<span>Standort: ${this.escapeHtml(display.location)}</span>` : ''}
|
||||
<span>${this.escapeHtml(display.module_count)} Modul(e) veröffentlicht</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="display-card__action">Display öffnen</div>
|
||||
</a>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
showError(msg) {
|
||||
this.loadingOverlay.classList.add('hidden');
|
||||
this.errorOverlay.classList.remove('hidden');
|
||||
this.errorMessage.textContent = msg;
|
||||
}
|
||||
|
||||
escapeHtml(value) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = value ?? '';
|
||||
return div.innerHTML;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue