mirror of
https://github.com/usetrmnl/byos_laravel.git
synced 2026-01-13 23:18:10 +00:00
This commit is contained in:
parent
a88e72b75e
commit
ba3bf31bb7
29 changed files with 2379 additions and 215 deletions
389
resources/views/livewire/device-models/index.blade.php
Normal file
389
resources/views/livewire/device-models/index.blade.php
Normal file
|
|
@ -0,0 +1,389 @@
|
|||
<?php
|
||||
|
||||
use App\Models\DeviceModel;
|
||||
use Livewire\Volt\Component;
|
||||
|
||||
new class extends Component {
|
||||
|
||||
public $deviceModels;
|
||||
|
||||
public $name;
|
||||
public $label;
|
||||
public $description;
|
||||
public $width;
|
||||
public $height;
|
||||
public $colors;
|
||||
public $bit_depth;
|
||||
public $scale_factor = 1.0;
|
||||
public $rotation = 0;
|
||||
public $mime_type = 'image/png';
|
||||
public $offset_x = 0;
|
||||
public $offset_y = 0;
|
||||
public $published_at;
|
||||
|
||||
protected $rules = [
|
||||
'name' => 'required|string|max:255|unique:device_models,name',
|
||||
'label' => 'required|string|max:255',
|
||||
'description' => 'required|string',
|
||||
'width' => 'required|integer|min:1',
|
||||
'height' => 'required|integer|min:1',
|
||||
'colors' => 'required|integer|min:1',
|
||||
'bit_depth' => 'required|integer|min:1',
|
||||
'scale_factor' => 'required|numeric|min:0.1',
|
||||
'rotation' => 'required|integer',
|
||||
'mime_type' => 'required|string|max:255',
|
||||
'offset_x' => 'required|integer',
|
||||
'offset_y' => 'required|integer',
|
||||
'published_at' => 'nullable|date',
|
||||
];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->deviceModels = DeviceModel::all();
|
||||
return view('livewire.device-models.index');
|
||||
}
|
||||
|
||||
public function createDeviceModel(): void
|
||||
{
|
||||
$this->validate();
|
||||
|
||||
DeviceModel::create([
|
||||
'name' => $this->name,
|
||||
'label' => $this->label,
|
||||
'description' => $this->description,
|
||||
'width' => $this->width,
|
||||
'height' => $this->height,
|
||||
'colors' => $this->colors,
|
||||
'bit_depth' => $this->bit_depth,
|
||||
'scale_factor' => $this->scale_factor,
|
||||
'rotation' => $this->rotation,
|
||||
'mime_type' => $this->mime_type,
|
||||
'offset_x' => $this->offset_x,
|
||||
'offset_y' => $this->offset_y,
|
||||
'published_at' => $this->published_at,
|
||||
]);
|
||||
|
||||
$this->reset(['name', 'label', 'description', 'width', 'height', 'colors', 'bit_depth', 'scale_factor', 'rotation', 'mime_type', 'offset_x', 'offset_y', 'published_at']);
|
||||
\Flux::modal('create-device-model')->close();
|
||||
|
||||
$this->deviceModels = DeviceModel::all();
|
||||
session()->flash('message', 'Device model created successfully.');
|
||||
}
|
||||
|
||||
public $editingDeviceModelId;
|
||||
|
||||
public function editDeviceModel(DeviceModel $deviceModel): void
|
||||
{
|
||||
$this->editingDeviceModelId = $deviceModel->id;
|
||||
$this->name = $deviceModel->name;
|
||||
$this->label = $deviceModel->label;
|
||||
$this->description = $deviceModel->description;
|
||||
$this->width = $deviceModel->width;
|
||||
$this->height = $deviceModel->height;
|
||||
$this->colors = $deviceModel->colors;
|
||||
$this->bit_depth = $deviceModel->bit_depth;
|
||||
$this->scale_factor = $deviceModel->scale_factor;
|
||||
$this->rotation = $deviceModel->rotation;
|
||||
$this->mime_type = $deviceModel->mime_type;
|
||||
$this->offset_x = $deviceModel->offset_x;
|
||||
$this->offset_y = $deviceModel->offset_y;
|
||||
$this->published_at = $deviceModel->published_at?->format('Y-m-d\TH:i');
|
||||
}
|
||||
|
||||
public function updateDeviceModel(): void
|
||||
{
|
||||
$deviceModel = DeviceModel::findOrFail($this->editingDeviceModelId);
|
||||
|
||||
$this->validate([
|
||||
'name' => 'required|string|max:255|unique:device_models,name,' . $deviceModel->id,
|
||||
'label' => 'required|string|max:255',
|
||||
'description' => 'required|string',
|
||||
'width' => 'required|integer|min:1',
|
||||
'height' => 'required|integer|min:1',
|
||||
'colors' => 'required|integer|min:1',
|
||||
'bit_depth' => 'required|integer|min:1',
|
||||
'scale_factor' => 'required|numeric|min:0.1',
|
||||
'rotation' => 'required|integer',
|
||||
'mime_type' => 'required|string|max:255',
|
||||
'offset_x' => 'required|integer',
|
||||
'offset_y' => 'required|integer',
|
||||
'published_at' => 'nullable|date',
|
||||
]);
|
||||
|
||||
$deviceModel->update([
|
||||
'name' => $this->name,
|
||||
'label' => $this->label,
|
||||
'description' => $this->description,
|
||||
'width' => $this->width,
|
||||
'height' => $this->height,
|
||||
'colors' => $this->colors,
|
||||
'bit_depth' => $this->bit_depth,
|
||||
'scale_factor' => $this->scale_factor,
|
||||
'rotation' => $this->rotation,
|
||||
'mime_type' => $this->mime_type,
|
||||
'offset_x' => $this->offset_x,
|
||||
'offset_y' => $this->offset_y,
|
||||
'published_at' => $this->published_at,
|
||||
]);
|
||||
|
||||
$this->reset(['name', 'label', 'description', 'width', 'height', 'colors', 'bit_depth', 'scale_factor', 'rotation', 'mime_type', 'offset_x', 'offset_y', 'published_at', 'editingDeviceModelId']);
|
||||
\Flux::modal('edit-device-model-' . $deviceModel->id)->close();
|
||||
|
||||
$this->deviceModels = DeviceModel::all();
|
||||
session()->flash('message', 'Device model updated successfully.');
|
||||
}
|
||||
|
||||
public function deleteDeviceModel(DeviceModel $deviceModel): void
|
||||
{
|
||||
$deviceModel->delete();
|
||||
|
||||
$this->deviceModels = DeviceModel::all();
|
||||
session()->flash('message', 'Device model deleted successfully.');
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<div>
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h2 class="text-2xl font-semibold dark:text-gray-100">Device Models</h2>
|
||||
{{-- <flux:modal.trigger name="create-device-model">--}}
|
||||
{{-- <flux:button icon="plus" variant="primary">Add Device Model</flux:button>--}}
|
||||
{{-- </flux:modal.trigger>--}}
|
||||
</div>
|
||||
@if (session()->has('message'))
|
||||
<div class="mb-4">
|
||||
<flux:callout variant="success" icon="check-circle" heading=" {{ session('message') }}">
|
||||
<x-slot name="controls">
|
||||
<flux:button icon="x-mark" variant="ghost"
|
||||
x-on:click="$el.closest('[data-flux-callout]').remove()"/>
|
||||
</x-slot>
|
||||
</flux:callout>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<flux:modal name="create-device-model" class="md:w-96">
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<flux:heading size="lg">Add Device Model</flux:heading>
|
||||
</div>
|
||||
|
||||
<form wire:submit="createDeviceModel">
|
||||
<div class="mb-4">
|
||||
<flux:input label="Name" wire:model="name" id="name" class="block mt-1 w-full" type="text"
|
||||
name="name" autofocus/>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<flux:input label="Label" wire:model="label" id="label" class="block mt-1 w-full"
|
||||
type="text"
|
||||
name="label"/>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<flux:input label="Description" wire:model="description" id="description"
|
||||
class="block mt-1 w-full" name="description"/>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4 mb-4">
|
||||
<flux:input label="Width" wire:model="width" id="width" class="block mt-1 w-full"
|
||||
type="number"
|
||||
name="width"/>
|
||||
<flux:input label="Height" wire:model="height" id="height" class="block mt-1 w-full"
|
||||
type="number"
|
||||
name="height"/>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4 mb-4">
|
||||
<flux:input label="Colors" wire:model="colors" id="colors" class="block mt-1 w-full"
|
||||
type="number"
|
||||
name="colors"/>
|
||||
<flux:input label="Bit Depth" wire:model="bit_depth" id="bit_depth"
|
||||
class="block mt-1 w-full" type="number"
|
||||
name="bit_depth"/>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4 mb-4">
|
||||
<flux:input label="Scale Factor" wire:model="scale_factor" id="scale_factor"
|
||||
class="block mt-1 w-full" type="number"
|
||||
name="scale_factor" step="0.1"/>
|
||||
<flux:input label="Rotation" wire:model="rotation" id="rotation" class="block mt-1 w-full"
|
||||
type="number"
|
||||
name="rotation"/>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<flux:input label="MIME Type" wire:model="mime_type" id="mime_type"
|
||||
class="block mt-1 w-full" type="text"
|
||||
name="mime_type"/>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4 mb-4">
|
||||
<flux:input label="Offset X" wire:model="offset_x" id="offset_x" class="block mt-1 w-full"
|
||||
type="number"
|
||||
name="offset_x"/>
|
||||
<flux:input label="Offset Y" wire:model="offset_y" id="offset_y" class="block mt-1 w-full"
|
||||
type="number"
|
||||
name="offset_y"/>
|
||||
</div>
|
||||
|
||||
<div class="flex">
|
||||
<flux:spacer/>
|
||||
<flux:button type="submit" variant="primary">Create Device Model</flux:button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</flux:modal>
|
||||
|
||||
@foreach ($deviceModels as $deviceModel)
|
||||
<flux:modal name="edit-device-model-{{ $deviceModel->id }}" class="md:w-96">
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<flux:heading size="lg">Edit Device Model</flux:heading>
|
||||
</div>
|
||||
|
||||
<form wire:submit="updateDeviceModel">
|
||||
<div class="mb-4">
|
||||
<flux:input label="Name" wire:model="name" id="edit_name" class="block mt-1 w-full"
|
||||
type="text"
|
||||
name="edit_name"/>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<flux:input label="Label" wire:model="label" id="edit_label" class="block mt-1 w-full"
|
||||
type="text"
|
||||
name="edit_label"/>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<flux:input label="Description" wire:model="description" id="edit_description"
|
||||
class="block mt-1 w-full" name="edit_description"/>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4 mb-4">
|
||||
<flux:input label="Width" wire:model="width" id="edit_width" class="block mt-1 w-full"
|
||||
type="number"
|
||||
name="edit_width"/>
|
||||
<flux:input label="Height" wire:model="height" id="edit_height"
|
||||
class="block mt-1 w-full" type="number"
|
||||
name="edit_height"/>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4 mb-4">
|
||||
<flux:input label="Colors" wire:model="colors" id="edit_colors"
|
||||
class="block mt-1 w-full" type="number"
|
||||
name="edit_colors"/>
|
||||
<flux:input label="Bit Depth" wire:model="bit_depth" id="edit_bit_depth"
|
||||
class="block mt-1 w-full" type="number"
|
||||
name="edit_bit_depth"/>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4 mb-4">
|
||||
<flux:input label="Scale Factor" wire:model="scale_factor" id="edit_scale_factor"
|
||||
class="block mt-1 w-full" type="number"
|
||||
name="edit_scale_factor" step="0.1"/>
|
||||
<flux:input label="Rotation" wire:model="rotation" id="edit_rotation"
|
||||
class="block mt-1 w-full" type="number"
|
||||
name="edit_rotation"/>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<flux:select label="MIME Type" wire:model="mime_type" id="edit_mime_type" name="edit_mime_type">
|
||||
<flux:select.option>image/png</flux:select.option>
|
||||
<flux:select.option>image/bmp</flux:select.option>
|
||||
</flux:select>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4 mb-4">
|
||||
<flux:input label="Offset X" wire:model="offset_x" id="edit_offset_x"
|
||||
class="block mt-1 w-full" type="number"
|
||||
name="edit_offset_x"/>
|
||||
<flux:input label="Offset Y" wire:model="offset_y" id="edit_offset_y"
|
||||
class="block mt-1 w-full" type="number"
|
||||
name="edit_offset_y"/>
|
||||
</div>
|
||||
|
||||
<div class="flex">
|
||||
<flux:spacer/>
|
||||
<flux:button type="submit" variant="primary">Update Device Model</flux:button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</flux:modal>
|
||||
@endforeach
|
||||
|
||||
<table
|
||||
class="min-w-full table-fixed text-zinc-800 divide-y divide-zinc-800/10 dark:divide-white/20 text-zinc-800"
|
||||
data-flux-table>
|
||||
<thead data-flux-columns>
|
||||
<tr>
|
||||
<th class="py-3 px-3 first:pl-0 last:pr-0 text-left text-sm font-medium text-zinc-800 dark:text-white"
|
||||
data-flux-column>
|
||||
<div class="whitespace-nowrap flex group-[]/right-align:justify-end">Description</div>
|
||||
</th>
|
||||
<th class="py-3 px-3 first:pl-0 last:pr-0 text-left text-sm font-medium text-zinc-800 dark:text-white"
|
||||
data-flux-column>
|
||||
<div class="whitespace-nowrap flex group-[]/right-align:justify-end">Width</div>
|
||||
</th>
|
||||
<th class="py-3 px-3 first:pl-0 last:pr-0 text-left text-sm font-medium text-zinc-800 dark:text-white"
|
||||
data-flux-column>
|
||||
<div class="whitespace-nowrap flex group-[]/right-align:justify-end">Height</div>
|
||||
</th>
|
||||
<th class="py-3 px-3 first:pl-0 last:pr-0 text-left text-sm font-medium text-zinc-800 dark:text-white"
|
||||
data-flux-column>
|
||||
<div class="whitespace-nowrap flex group-[]/right-align:justify-end">Bit Depth</div>
|
||||
</th>
|
||||
<th class="py-3 px-3 first:pl-0 last:pr-0 text-left text-sm font-medium text-zinc-800 dark:text-white"
|
||||
data-flux-column>
|
||||
<div class="whitespace-nowrap flex group-[]/right-align:justify-end">Actions</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody class="divide-y divide-zinc-800/10 dark:divide-white/20" data-flux-rows>
|
||||
@foreach ($deviceModels as $deviceModel)
|
||||
<tr data-flux-row>
|
||||
<td class="py-3 px-3 first:pl-0 last:pr-0 text-sm whitespace-nowrap text-zinc-500 dark:text-zinc-300"
|
||||
>
|
||||
<div>
|
||||
<div class="font-medium text-zinc-800 dark:text-white">{{ $deviceModel->label }}</div>
|
||||
<div class="text-xs text-zinc-500">{{ Str::limit($deviceModel->name, 50) }}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="py-3 px-3 first:pl-0 last:pr-0 text-sm whitespace-nowrap text-zinc-500 dark:text-zinc-300"
|
||||
>
|
||||
{{ $deviceModel->width }}
|
||||
</td>
|
||||
<td class="py-3 px-3 first:pl-0 last:pr-0 text-sm whitespace-nowrap text-zinc-500 dark:text-zinc-300"
|
||||
>
|
||||
{{ $deviceModel->height }}
|
||||
</td>
|
||||
<td class="py-3 px-3 first:pl-0 last:pr-0 text-sm whitespace-nowrap text-zinc-500 dark:text-zinc-300"
|
||||
>
|
||||
{{ $deviceModel->bit_depth }}
|
||||
</td>
|
||||
<td class="py-3 px-3 first:pl-0 last:pr-0 text-sm whitespace-nowrap font-medium text-zinc-800 dark:text-white"
|
||||
>
|
||||
<div class="flex items-center gap-4">
|
||||
<flux:button.group>
|
||||
<flux:modal.trigger name="edit-device-model-{{ $deviceModel->id }}">
|
||||
<flux:button wire:click="editDeviceModel({{ $deviceModel->id }})" icon="pencil"
|
||||
iconVariant="outline">
|
||||
</flux:button>
|
||||
</flux:modal.trigger>
|
||||
<flux:button wire:click="deleteDeviceModel({{ $deviceModel->id }})" icon="trash"
|
||||
iconVariant="outline">
|
||||
</flux:button>
|
||||
</flux:button.group>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
use App\Jobs\FirmwareDownloadJob;
|
||||
use App\Models\DeviceModel;
|
||||
use App\Models\Firmware;
|
||||
use App\Models\Playlist;
|
||||
use App\Models\PlaylistItem;
|
||||
|
|
@ -19,6 +20,7 @@ new class extends Component {
|
|||
public $height;
|
||||
public $rotate;
|
||||
public $image_format;
|
||||
public $device_model_id;
|
||||
|
||||
// Sleep mode and special function
|
||||
public $sleep_mode_enabled = false;
|
||||
|
|
@ -34,6 +36,9 @@ new class extends Component {
|
|||
public $active_until;
|
||||
public $refresh_time = null;
|
||||
|
||||
// Device model properties
|
||||
public $deviceModels;
|
||||
|
||||
// Firmware properties
|
||||
public $firmwares;
|
||||
public $selected_firmware_id;
|
||||
|
|
@ -56,6 +61,12 @@ new class extends Component {
|
|||
$this->height = $device->height;
|
||||
$this->rotate = $device->rotate;
|
||||
$this->image_format = $device->image_format;
|
||||
$this->device_model_id = $device->device_model_id;
|
||||
$this->deviceModels = DeviceModel::orderBy('label')->get()->sortBy(function ($deviceModel) {
|
||||
// Put TRMNL models at the top, then sort alphabetically within each group
|
||||
$isTrmnl = str_starts_with($deviceModel->label, 'TRMNL');
|
||||
return $isTrmnl ? '0' . $deviceModel->label : '1' . $deviceModel->label;
|
||||
});
|
||||
$this->playlists = $device->playlists()->with('items.plugin')->orderBy('created_at')->get();
|
||||
$this->firmwares = \App\Models\Firmware::orderBy('latest', 'desc')->orderBy('created_at', 'desc')->get();
|
||||
$this->selected_firmware_id = $this->firmwares->where('latest', true)->first()?->id;
|
||||
|
|
@ -77,6 +88,24 @@ new class extends Component {
|
|||
redirect()->route('devices');
|
||||
}
|
||||
|
||||
public function updatedDeviceModelId()
|
||||
{
|
||||
// Convert empty string to null for custom selection
|
||||
if (empty($this->device_model_id)) {
|
||||
$this->device_model_id = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->device_model_id) {
|
||||
$deviceModel = DeviceModel::find($this->device_model_id);
|
||||
if ($deviceModel) {
|
||||
$this->width = $deviceModel->width;
|
||||
$this->height = $deviceModel->height;
|
||||
$this->rotate = $deviceModel->rotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function updateDevice()
|
||||
{
|
||||
abort_unless(auth()->user()->devices->contains($this->device), 403);
|
||||
|
|
@ -90,12 +119,16 @@ new class extends Component {
|
|||
'height' => 'required|integer|min:1',
|
||||
'rotate' => 'required|integer|min:0|max:359',
|
||||
'image_format' => 'required|string',
|
||||
'device_model_id' => 'nullable|exists:device_models,id',
|
||||
'sleep_mode_enabled' => 'boolean',
|
||||
'sleep_mode_from' => 'nullable|date_format:H:i',
|
||||
'sleep_mode_to' => 'nullable|date_format:H:i',
|
||||
'special_function' => 'nullable|string',
|
||||
]);
|
||||
|
||||
// Convert empty string to null for custom selection
|
||||
$deviceModelId = empty($this->device_model_id) ? null : $this->device_model_id;
|
||||
|
||||
$this->device->update([
|
||||
'name' => $this->name,
|
||||
'friendly_id' => $this->friendly_id,
|
||||
|
|
@ -105,6 +138,7 @@ new class extends Component {
|
|||
'height' => $this->height,
|
||||
'rotate' => $this->rotate,
|
||||
'image_format' => $this->image_format,
|
||||
'device_model_id' => $deviceModelId,
|
||||
'sleep_mode_enabled' => $this->sleep_mode_enabled,
|
||||
'sleep_mode_from' => $this->sleep_mode_from,
|
||||
'sleep_mode_to' => $this->sleep_mode_to,
|
||||
|
|
@ -357,20 +391,33 @@ new class extends Component {
|
|||
|
||||
<flux:input label="Friendly ID" wire:model="friendly_id"/>
|
||||
<flux:input label="MAC Address" wire:model="mac_address"/>
|
||||
<flux:separator class="my-4" text="Advanced Device Settings" />
|
||||
<div class="flex gap-4">
|
||||
<flux:input label="Width (px)" wire:model="width" type="number" />
|
||||
<flux:input label="Height (px)" wire:model="height" type="number"/>
|
||||
<flux:input label="Rotate °" wire:model="rotate" type="number"/>
|
||||
</div>
|
||||
<flux:select label="Image Format" wire:model="image_format">
|
||||
@foreach(\App\Enums\ImageFormat::cases() as $format)
|
||||
<flux:select.option value="{{ $format->value }}">{{$format->label()}}</flux:select.option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
|
||||
<flux:input label="Default Refresh Interval (seconds)" wire:model="default_refresh_interval"
|
||||
type="number"/>
|
||||
|
||||
<flux:select label="Device Model" wire:model.live="device_model_id">
|
||||
<flux:select.option value="">Custom (Manual Dimensions)</flux:select.option>
|
||||
@foreach($deviceModels as $deviceModel)
|
||||
<flux:select.option value="{{ $deviceModel->id }}">
|
||||
{{ $deviceModel->label }} ({{ $deviceModel->width }}x{{ $deviceModel->height }})
|
||||
</flux:select.option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
|
||||
@if(empty($device_model_id))
|
||||
<flux:separator class="my-4" text="Advanced Device Settings" />
|
||||
<div class="flex gap-4">
|
||||
<flux:input label="Width (px)" wire:model="width" type="number" />
|
||||
<flux:input label="Height (px)" wire:model="height" type="number"/>
|
||||
<flux:input label="Rotate °" wire:model="rotate" type="number"/>
|
||||
</div>
|
||||
<flux:select label="Image Format" wire:model="image_format">
|
||||
@foreach(\App\Enums\ImageFormat::cases() as $format)
|
||||
<flux:select.option value="{{ $format->value }}">{{$format->label()}}</flux:select.option>
|
||||
@endforeach
|
||||
</flux:select>
|
||||
@endif
|
||||
|
||||
<flux:separator class="my-4" text="Special Functions" />
|
||||
<flux:select label="Special Function" wire:model="special_function">
|
||||
<flux:select.option value="sleep">Sleep</flux:select.option>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Device;
|
||||
use App\Models\DeviceModel;
|
||||
use Livewire\Volt\Component;
|
||||
|
||||
new class extends Component {
|
||||
|
|
@ -22,6 +23,8 @@ new class extends Component {
|
|||
public $is_mirror = false;
|
||||
|
||||
public $mirror_device_id = null;
|
||||
public $device_model_id = null;
|
||||
public $deviceModels;
|
||||
|
||||
public ?int $pause_duration;
|
||||
|
||||
|
|
@ -29,15 +32,29 @@ new class extends Component {
|
|||
'mac_address' => 'required',
|
||||
'api_key' => 'required',
|
||||
'default_refresh_interval' => 'required|integer',
|
||||
'device_model_id' => 'nullable|exists:device_models,id',
|
||||
'mirror_device_id' => 'required_if:is_mirror,true',
|
||||
];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->devices = auth()->user()->devices;
|
||||
$this->deviceModels = DeviceModel::orderBy('label')->get()->sortBy(function ($deviceModel) {
|
||||
// Put TRMNL models at the top, then sort alphabetically within each group
|
||||
$isTrmnl = str_starts_with($deviceModel->label, 'TRMNL');
|
||||
return $isTrmnl ? '0' . $deviceModel->label : '1' . $deviceModel->label;
|
||||
});
|
||||
return view('livewire.devices.manage');
|
||||
}
|
||||
|
||||
public function updatedDeviceModelId(): void
|
||||
{
|
||||
// Convert empty string to null for custom selection
|
||||
if (empty($this->device_model_id)) {
|
||||
$this->device_model_id = null;
|
||||
}
|
||||
}
|
||||
|
||||
public function createDevice(): void
|
||||
{
|
||||
$this->validate();
|
||||
|
|
@ -49,6 +66,9 @@ new class extends Component {
|
|||
abort_if($mirrorDevice->mirror_device_id !== null, 403, 'Cannot mirror a device that is already a mirror device');
|
||||
}
|
||||
|
||||
// Convert empty string to null for custom selection
|
||||
$deviceModelId = empty($this->device_model_id) ? null : $this->device_model_id;
|
||||
|
||||
Device::create([
|
||||
'name' => $this->name,
|
||||
'mac_address' => $this->mac_address,
|
||||
|
|
@ -56,6 +76,7 @@ new class extends Component {
|
|||
'default_refresh_interval' => $this->default_refresh_interval,
|
||||
'friendly_id' => $this->friendly_id,
|
||||
'user_id' => auth()->id(),
|
||||
'device_model_id' => $deviceModelId,
|
||||
'mirror_device_id' => $this->is_mirror ? $this->mirror_device_id : null,
|
||||
]);
|
||||
|
||||
|
|
@ -154,6 +175,19 @@ new class extends Component {
|
|||
autofocus/>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<flux:select label="Device Model" wire:model.live="device_model_id">
|
||||
<flux:select.option value="">Custom (Manual Dimensions)</flux:select.option>
|
||||
@if ($deviceModels && $deviceModels->count() > 0)
|
||||
@foreach($deviceModels as $deviceModel)
|
||||
<flux:select.option value="{{ $deviceModel->id }}">
|
||||
{{ $deviceModel->label }} ({{ $deviceModel->width }}x{{ $deviceModel->height }})
|
||||
</flux:select.option>
|
||||
@endforeach
|
||||
@endif
|
||||
</flux:select>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<flux:checkbox wire:model.live="is_mirror" label="Mirrors Device"/>
|
||||
</div>
|
||||
|
|
|
|||
35
resources/views/vendor/trmnl/components/screen.blade.php
vendored
Normal file
35
resources/views/vendor/trmnl/components/screen.blade.php
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
@props([
|
||||
'noBleed' => false,
|
||||
'darkMode' => false,
|
||||
'deviceVariant' => 'og',
|
||||
'deviceOrientation' => null,
|
||||
'colorDepth' => '2bit',
|
||||
'scaleLevel' => null,
|
||||
])
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="preconnect" href="https://fonts.bunny.net">
|
||||
<link href="https://fonts.bunny.net/css?family=Inter:300,400,500" rel="stylesheet"/>
|
||||
@if (config('trmnl-blade.framework_css_url'))
|
||||
<link rel="stylesheet"
|
||||
href="{{ config('trmnl-blade.framework_css_url') }}">
|
||||
@else
|
||||
<link rel="stylesheet"
|
||||
href="https://usetrmnl.com/css/{{ config('trmnl-blade.framework_version', '1.2.0') }}/plugins.css">
|
||||
@endif
|
||||
@if (config('trmnl-blade.framework_js_url'))
|
||||
<script src="{{ config('trmnl-blade.framework_js_url') }}"></script>
|
||||
@else
|
||||
<script src="https://usetrmnl.com/js/{{ config('trmnl-blade.framework_version', '1.2.0') }}/plugins.js"></script>
|
||||
@endif
|
||||
<title>{{ $title ?? config('app.name') }}</title>
|
||||
</head>
|
||||
<body class="environment trmnl">
|
||||
<div class="screen {{$noBleed ? 'screen--no-bleed' : ''}} {{ $darkMode ? 'dark-mode' : '' }} {{$deviceVariant ? 'screen--' . $deviceVariant : ''}} {{ $deviceOrientation ? 'screen--' . $deviceOrientation : ''}} {{ $colorDepth ? 'screen--' . $colorDepth : ''}} {{ $scaleLevel ? 'screen--scale-' . $scaleLevel : ''}}">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue