mirror of
https://github.com/usetrmnl/byos_laravel.git
synced 2026-01-13 15:07:49 +00:00
parent
ed9d03d0b8
commit
56638b26e8
28 changed files with 1067 additions and 346 deletions
172
app/Console/Commands/MashupCreateCommand.php
Normal file
172
app/Console/Commands/MashupCreateCommand.php
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Device;
|
||||
use App\Models\Playlist;
|
||||
use App\Models\PlaylistItem;
|
||||
use App\Models\Plugin;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
use function Laravel\Prompts\select;
|
||||
use function Laravel\Prompts\text;
|
||||
|
||||
class MashupCreateCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'mashup:create';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new mashup and add it to a playlist';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
// Select device
|
||||
$device = $this->selectDevice();
|
||||
if (! $device) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Select playlist
|
||||
$playlist = $this->selectPlaylist($device);
|
||||
if (! $playlist) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Select mashup layout
|
||||
$layout = $this->selectLayout();
|
||||
if (! $layout) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Get mashup name
|
||||
$name = $this->getMashupName();
|
||||
if (! $name) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Select plugins
|
||||
$plugins = $this->selectPlugins($layout);
|
||||
if ($plugins->isEmpty()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
$maxOrder = $playlist->items()->max('order') ?? 0;
|
||||
|
||||
// Create playlist item with mashup
|
||||
PlaylistItem::createMashup(
|
||||
playlist: $playlist,
|
||||
layout: $layout,
|
||||
pluginIds: $plugins->pluck('id')->toArray(),
|
||||
name: $name,
|
||||
order: $maxOrder + 1
|
||||
);
|
||||
|
||||
$this->info('Mashup created successfully!');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected function selectDevice(): ?Device
|
||||
{
|
||||
$devices = Device::all();
|
||||
if ($devices->isEmpty()) {
|
||||
$this->error('No devices found. Please create a device first.');
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$deviceId = select(
|
||||
label: 'Select a device',
|
||||
options: $devices->mapWithKeys(fn ($device) => [$device->id => $device->name])->toArray()
|
||||
);
|
||||
|
||||
return $devices->firstWhere('id', $deviceId);
|
||||
}
|
||||
|
||||
protected function selectPlaylist(Device $device): ?Playlist
|
||||
{
|
||||
$playlists = $device->playlists;
|
||||
if ($playlists->isEmpty()) {
|
||||
$this->error('No playlists found for this device. Please create a playlist first.');
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$playlistId = select(
|
||||
label: 'Select a playlist',
|
||||
options: $playlists->mapWithKeys(fn ($playlist) => [$playlist->id => $playlist->name])->toArray()
|
||||
);
|
||||
|
||||
return $playlists->firstWhere('id', $playlistId);
|
||||
}
|
||||
|
||||
protected function selectLayout(): ?string
|
||||
{
|
||||
return select(
|
||||
label: 'Select a layout',
|
||||
options: PlaylistItem::getAvailableLayouts()
|
||||
);
|
||||
}
|
||||
|
||||
protected function getMashupName(): ?string
|
||||
{
|
||||
return text(
|
||||
label: 'Enter a name for this mashup',
|
||||
required: true,
|
||||
default: 'Mashup',
|
||||
validate: fn (string $value) => match (true) {
|
||||
strlen($value) < 1 => 'The name must be at least 2 characters.',
|
||||
strlen($value) > 50 => 'The name must not exceed 50 characters.',
|
||||
default => null,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
protected function selectPlugins(string $layout): Collection
|
||||
{
|
||||
$requiredCount = PlaylistItem::getRequiredPluginCountForLayout($layout);
|
||||
|
||||
$plugins = Plugin::all();
|
||||
if ($plugins->isEmpty()) {
|
||||
$this->error('No plugins found. Please create some plugins first.');
|
||||
|
||||
return collect();
|
||||
}
|
||||
|
||||
$selectedPlugins = collect();
|
||||
$availablePlugins = $plugins->mapWithKeys(fn ($plugin) => [$plugin->id => $plugin->name])->toArray();
|
||||
|
||||
for ($i = 0; $i < $requiredCount; $i++) {
|
||||
$position = match ($i) {
|
||||
0 => 'first',
|
||||
1 => 'second',
|
||||
2 => 'third',
|
||||
3 => 'fourth',
|
||||
default => ($i + 1).'th'
|
||||
};
|
||||
|
||||
$pluginId = select(
|
||||
label: "Select the $position plugin",
|
||||
options: $availablePlugins
|
||||
);
|
||||
|
||||
$selectedPlugins->push($plugins->firstWhere('id', $pluginId));
|
||||
unset($availablePlugins[$pluginId]);
|
||||
}
|
||||
|
||||
return $selectedPlugins;
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ class PlaylistItem extends Model
|
|||
protected $casts = [
|
||||
'is_active' => 'boolean',
|
||||
'last_displayed_at' => 'datetime',
|
||||
'mashup' => 'json',
|
||||
];
|
||||
|
||||
public function playlist(): BelongsTo
|
||||
|
|
@ -26,4 +27,179 @@ class PlaylistItem extends Model
|
|||
{
|
||||
return $this->belongsTo(Plugin::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this playlist item is a mashup
|
||||
*/
|
||||
public function isMashup(): bool
|
||||
{
|
||||
return ! is_null($this->mashup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mashup name if this is a mashup
|
||||
*/
|
||||
public function getMashupName(): ?string
|
||||
{
|
||||
return $this->mashup['mashup_name'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mashup layout type if this is a mashup
|
||||
*/
|
||||
public function getMashupLayoutType(): ?string
|
||||
{
|
||||
return $this->mashup['mashup_layout'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all plugin IDs for this mashup
|
||||
*/
|
||||
public function getMashupPluginIds(): array
|
||||
{
|
||||
return $this->mashup['plugin_ids'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of plugins required for the current layout
|
||||
*/
|
||||
public function getRequiredPluginCount(): int
|
||||
{
|
||||
if (! $this->isMashup()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return match ($this->getMashupLayoutType()) {
|
||||
'1Lx1R', '1Tx1B' => 2, // Left-Right or Top-Bottom split
|
||||
'1Lx2R', '2Lx1R', '2Tx1B', '1Tx2B' => 3, // Two on one side, one on other
|
||||
'2x2' => 4, // Quadrant
|
||||
default => 1,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the layout type (horizontal, vertical, or grid)
|
||||
*/
|
||||
public function getLayoutType(): string
|
||||
{
|
||||
if (! $this->isMashup()) {
|
||||
return 'single';
|
||||
}
|
||||
|
||||
return match ($this->getMashupLayoutType()) {
|
||||
'1Lx1R', '1Lx2R', '2Lx1R' => 'vertical',
|
||||
'1Tx1B', '2Tx1B', '1Tx2B' => 'horizontal',
|
||||
'2x2' => 'grid',
|
||||
default => 'single',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the layout size for a plugin based on its position
|
||||
*/
|
||||
public function getLayoutSize(int $position = 0): string
|
||||
{
|
||||
if (! $this->isMashup()) {
|
||||
return 'full';
|
||||
}
|
||||
|
||||
return match ($this->getMashupLayoutType()) {
|
||||
'1Lx1R' => 'half_vertical', // Both sides are single plugins
|
||||
'1Tx1B' => 'half_horizontal', // Both sides are single plugins
|
||||
'2Lx1R' => match ($position) {
|
||||
0, 1 => 'quadrant', // Left side has 2 plugins
|
||||
2 => 'half_vertical', // Right side has 1 plugin
|
||||
default => 'full'
|
||||
},
|
||||
'1Lx2R' => match ($position) {
|
||||
0 => 'half_vertical', // Left side has 1 plugin
|
||||
1, 2 => 'quadrant', // Right side has 2 plugins
|
||||
default => 'full'
|
||||
},
|
||||
'2Tx1B' => match ($position) {
|
||||
0, 1 => 'quadrant', // Top side has 2 plugins
|
||||
2 => 'half_horizontal', // Bottom side has 1 plugin
|
||||
default => 'full'
|
||||
},
|
||||
'1Tx2B' => match ($position) {
|
||||
0 => 'half_horizontal', // Top side has 1 plugin
|
||||
1, 2 => 'quadrant', // Bottom side has 2 plugins
|
||||
default => 'full'
|
||||
},
|
||||
'2x2' => 'quadrant', // All positions are quadrants
|
||||
default => 'full'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Render all plugins with appropriate layout
|
||||
*/
|
||||
public function render(): string
|
||||
{
|
||||
if (! $this->isMashup()) {
|
||||
return view('trmnl-layouts.single', [
|
||||
'slot' => $this->plugin->render('full', false),
|
||||
])->render();
|
||||
}
|
||||
|
||||
$pluginMarkups = [];
|
||||
$plugins = Plugin::whereIn('id', $this->getMashupPluginIds())->get();
|
||||
|
||||
foreach ($plugins as $index => $plugin) {
|
||||
$size = $this->getLayoutSize($index);
|
||||
$pluginMarkups[] = $plugin->render($size, false);
|
||||
}
|
||||
|
||||
return view('trmnl-layouts.mashup', [
|
||||
'mashupLayout' => $this->getMashupLayoutType(),
|
||||
'slot' => implode('', $pluginMarkups),
|
||||
])->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Available mashup layouts with their descriptions
|
||||
*/
|
||||
public static function getAvailableLayouts(): array
|
||||
{
|
||||
return [
|
||||
'1Lx1R' => '1 Left - 1 Right (2 plugins)',
|
||||
'1Lx2R' => '1 Left - 2 Right (3 plugins)',
|
||||
'2Lx1R' => '2 Left - 1 Right (3 plugins)',
|
||||
'1Tx1B' => '1 Top - 1 Bottom (2 plugins)',
|
||||
'2Tx1B' => '2 Top - 1 Bottom (3 plugins)',
|
||||
'1Tx2B' => '1 Top - 2 Bottom (3 plugins)',
|
||||
'2x2' => 'Quadrant (4 plugins)',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required number of plugins for a given layout
|
||||
*/
|
||||
public static function getRequiredPluginCountForLayout(string $layout): int
|
||||
{
|
||||
return match ($layout) {
|
||||
'1Lx1R', '1Tx1B' => 2,
|
||||
'1Lx2R', '2Lx1R', '2Tx1B', '1Tx2B' => 3,
|
||||
'2x2' => 4,
|
||||
default => 1,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new mashup with the given layout and plugins
|
||||
*/
|
||||
public static function createMashup(Playlist $playlist, string $layout, array $pluginIds, string $name, $order): self
|
||||
{
|
||||
return static::create([
|
||||
'playlist_id' => $playlist->id,
|
||||
'plugin_id' => $pluginIds[0], // First plugin is the main plugin
|
||||
'mashup' => [
|
||||
'mashup_layout' => $layout,
|
||||
'mashup_name' => $name,
|
||||
'plugin_ids' => $pluginIds,
|
||||
],
|
||||
'is_active' => true,
|
||||
'order' => $order,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ namespace App\Models;
|
|||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
|
|
@ -65,4 +66,38 @@ class Plugin extends Model
|
|||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the plugin's markup
|
||||
*/
|
||||
public function render(string $size = 'full', bool $standalone = true): string
|
||||
{
|
||||
if ($this->render_markup) {
|
||||
if ($standalone) {
|
||||
return view('trmnl-layouts.single', [
|
||||
'slot' => Blade::render($this->render_markup, ['size' => $size, 'data' => $this->data_payload]),
|
||||
])->render();
|
||||
}
|
||||
|
||||
return Blade::render($this->render_markup, ['size' => $size, 'data' => $this->data_payload]);
|
||||
}
|
||||
|
||||
if ($this->render_markup_view) {
|
||||
if ($standalone) {
|
||||
return view('trmnl-layouts.single', [
|
||||
'slot' => view($this->render_markup_view, [
|
||||
'size' => $size,
|
||||
'data' => $this->data_payload,
|
||||
])->render(),
|
||||
])->render();
|
||||
} else {
|
||||
return view($this->render_markup_view, [
|
||||
'size' => $size,
|
||||
'data' => $this->data_payload,
|
||||
])->render();
|
||||
}
|
||||
}
|
||||
|
||||
return '<p>No render markup yet defined for this plugin.</p>';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue