diff --git a/resources/views/livewire/devices/configure.blade.php b/resources/views/livewire/devices/configure.blade.php
index 93183cf..4c9b4c5 100644
--- a/resources/views/livewire/devices/configure.blade.php
+++ b/resources/views/livewire/devices/configure.blade.php
@@ -31,6 +31,10 @@ new class extends Component
public $device_model_id;
+ public $is_mirror = false;
+
+ public $mirror_device_id = null;
+
// Signal to device to use high compatibility approaches when redrawing content
public $maximum_compatibility = false;
@@ -98,6 +102,8 @@ new class extends Component
$this->sleep_mode_from = optional($device->sleep_mode_from)->format('H:i');
$this->sleep_mode_to = optional($device->sleep_mode_to)->format('H:i');
$this->special_function = $device->special_function;
+ $this->is_mirror = $device->mirror_device_id !== null;
+ $this->mirror_device_id = $device->mirror_device_id;
return view('livewire.devices.configure', [
'image' => ($current_image_uuid) ? url($current_image_path) : null,
@@ -145,6 +151,7 @@ new class extends Component
'rotate' => 'required|integer|min:0|max:359',
'image_format' => 'required|string',
'device_model_id' => 'nullable|exists:device_models,id',
+ 'mirror_device_id' => 'required_if:is_mirror,true',
'maximum_compatibility' => 'boolean',
'sleep_mode_enabled' => 'boolean',
'sleep_mode_from' => 'nullable|date_format:H:i',
@@ -152,6 +159,13 @@ new class extends Component
'special_function' => 'nullable|string',
]);
+ if ($this->is_mirror) {
+ $mirrorDevice = auth()->user()->devices()->find($this->mirror_device_id);
+ abort_unless($mirrorDevice, 403, 'Invalid mirror device selected');
+ abort_if($mirrorDevice->mirror_device_id !== null, 403, 'Cannot mirror a device that is already a mirror device');
+ abort_if((int) $this->mirror_device_id === (int) $this->device->id, 403, 'Device cannot mirror itself');
+ }
+
// Convert empty string to null for custom selection
$deviceModelId = empty($this->device_model_id) ? null : $this->device_model_id;
@@ -165,6 +179,7 @@ new class extends Component
'rotate' => $this->rotate,
'image_format' => $this->image_format,
'device_model_id' => $deviceModelId,
+ 'mirror_device_id' => $this->is_mirror ? $this->mirror_device_id : null,
'maximum_compatibility' => $this->maximum_compatibility,
'sleep_mode_enabled' => $this->sleep_mode_enabled,
'sleep_mode_from' => $this->sleep_mode_from,
@@ -433,6 +448,18 @@ new class extends Component
@endforeach
+
+ @if($is_mirror)
+
+ Select a device
+ @foreach(auth()->user()->devices->where('mirror_device_id', null)->where('id', '!=', $device->id) as $mirrorOption)
+
+ {{ $mirrorOption->name }} ({{ $mirrorOption->friendly_id }})
+
+ @endforeach
+
+ @endif
+
@if(empty($device_model_id))
diff --git a/tests/Feature/Devices/DeviceConfigureTest.php b/tests/Feature/Devices/DeviceConfigureTest.php
index dff0954..bf4e19e 100644
--- a/tests/Feature/Devices/DeviceConfigureTest.php
+++ b/tests/Feature/Devices/DeviceConfigureTest.php
@@ -5,6 +5,7 @@ namespace Tests\Feature;
use App\Models\Device;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
+use Livewire\Livewire;
use function Pest\Laravel\actingAs;
@@ -23,3 +24,35 @@ test('configure view displays last_refreshed_at timestamp', function (): void {
$response->assertOk()
->assertSee('5 minutes ago');
});
+
+test('configure edit modal shows mirror checkbox and allows unchecking mirror', function (): void {
+ $user = User::factory()->create();
+ actingAs($user);
+
+ $deviceAttributes = [
+ 'user_id' => $user->id,
+ 'width' => 800,
+ 'height' => 480,
+ 'rotate' => 0,
+ 'image_format' => 'png',
+ 'maximum_compatibility' => false,
+ ];
+ $sourceDevice = Device::factory()->create($deviceAttributes);
+ $mirrorDevice = Device::factory()->create([
+ ...$deviceAttributes,
+ 'mirror_device_id' => $sourceDevice->id,
+ ]);
+
+ $response = $this->get(route('devices.configure', $mirrorDevice));
+ $response->assertOk()
+ ->assertSee('Mirrors Device')
+ ->assertSee('Select Device to Mirror');
+
+ Livewire::test('devices.configure', ['device' => $mirrorDevice])
+ ->set('is_mirror', false)
+ ->call('updateDevice')
+ ->assertHasNoErrors();
+
+ $mirrorDevice->refresh();
+ expect($mirrorDevice->mirror_device_id)->toBeNull();
+});