12-05-2026 admin, Panel Displays

This commit is contained in:
Kevin Adametz 2026-05-12 18:28:38 +02:00
parent 0762e3beac
commit 6a65354f4c
43 changed files with 3273 additions and 410 deletions

View file

@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* Erweitert die displays-Tabelle um:
* - is_test: Kennzeichnet ein Display als Test-Display (eigene Kachel im Admin,
* beliebige Module/Bespielungen können dort gefahrlos kombiniert werden).
* - preview_token: Eindeutiger Token für die Vorschau einer Entwurfs-Bespielung
* über /preview/{token} ohne Login.
*/
public function up(): void
{
Schema::table('displays', function (Blueprint $table) {
$table->boolean('is_test')->default(false)->after('is_active');
$table->string('preview_token', 64)->nullable()->unique()->after('is_test');
});
}
public function down(): void
{
Schema::table('displays', function (Blueprint $table) {
$table->dropUnique(['preview_token']);
$table->dropColumn(['is_test', 'preview_token']);
});
}
};

View file

@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* Eine Display-Bespielung pro Display existiert maximal eine Live-Bespielung
* (status = published) und optional eine Entwurfs-Bespielung (status = draft).
*
* Der Player liest immer die published-Playlist. Die Vorschau-Route greift
* über den preview_token des Displays auf die draft-Playlist zu.
*/
public function up(): void
{
Schema::create('display_playlists', function (Blueprint $table) {
$table->id();
$table->foreignId('display_id')->constrained()->cascadeOnDelete();
$table->string('status'); // 'published' | 'draft'
$table->timestamp('published_at')->nullable();
$table->foreignId('published_by')->nullable()->constrained('users')->nullOnDelete();
$table->text('notes')->nullable();
$table->timestamps();
$table->unique(['display_id', 'status']);
});
}
public function down(): void
{
Schema::dropIfExists('display_playlists');
}
};

View file

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* Eintrag in einer Display-Bespielung. Ersetzt funktional die alte
* Pivot-Tabelle display_display_version. display_versions selbst bleibt
* unverändert Module sind weiterhin wiederverwendbar (shared) zwischen
* Displays.
*/
public function up(): void
{
Schema::create('display_playlist_items', function (Blueprint $table) {
$table->id();
$table->foreignId('display_playlist_id')->constrained()->cascadeOnDelete();
$table->foreignId('display_version_id')->constrained()->cascadeOnDelete();
$table->unsignedInteger('sort_order')->default(0);
$table->timestamps();
$table->index(['display_playlist_id', 'sort_order']);
});
}
public function down(): void
{
Schema::dropIfExists('display_playlist_items');
}
};

View file

@ -0,0 +1,72 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* Überführt bestehende Pivot-Daten aus display_display_version in die neue
* Struktur display_playlists + display_playlist_items. Pro Display wird eine
* Live-Bespielung (status = published) erzeugt und die bestehende Reihenfolge
* 1:1 übernommen. Die alte Pivot-Tabelle bleibt erhalten und wird erst in
* Phase 7 dropped (Bestandsfunktionen müssen weiterlaufen).
*
* Idempotent: Existiert bereits eine Published-Playlist für ein Display, wird
* sie übersprungen.
*/
public function up(): void
{
if (! Schema::hasTable('display_display_version')) {
return;
}
$pivots = DB::table('display_display_version')
->orderBy('display_id')
->orderBy('sort_order')
->get()
->groupBy('display_id');
$now = now();
foreach ($pivots as $displayId => $entries) {
$existing = DB::table('display_playlists')
->where('display_id', $displayId)
->where('status', 'published')
->first();
if ($existing) {
continue;
}
$playlistId = DB::table('display_playlists')->insertGetId([
'display_id' => $displayId,
'status' => 'published',
'published_at' => $now,
'published_by' => null,
'notes' => 'Initial-Import aus display_display_version',
'created_at' => $now,
'updated_at' => $now,
]);
foreach ($entries as $entry) {
DB::table('display_playlist_items')->insert([
'display_playlist_id' => $playlistId,
'display_version_id' => $entry->display_version_id,
'sort_order' => $entry->sort_order,
'created_at' => $now,
'updated_at' => $now,
]);
}
}
}
public function down(): void
{
DB::table('display_playlist_items')->delete();
DB::table('display_playlists')->delete();
}
};