diff --git a/app/Models/User.php b/app/Models/User.php
index 949cafa..5528c2c 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -25,6 +25,7 @@ class User extends Authenticatable // implements MustVerifyEmail
'email',
'password',
'assign_new_devices',
+ 'assign_new_device_id'
];
/**
diff --git a/database/migrations/2025_05_08_225241_add_assign_new_device_id_to_users_table.php b/database/migrations/2025_05_08_225241_add_assign_new_device_id_to_users_table.php
new file mode 100644
index 0000000..242b368
--- /dev/null
+++ b/database/migrations/2025_05_08_225241_add_assign_new_device_id_to_users_table.php
@@ -0,0 +1,29 @@
+foreignId('assign_new_device_id')->nullable()->constrained('devices')->nullOnDelete();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('users', function (Blueprint $table) {
+ $table->dropForeign(['assign_new_device_id']);
+ $table->dropColumn('assign_new_device_id');
+ });
+ }
+};
diff --git a/resources/views/components/layouts/app/header.blade.php b/resources/views/components/layouts/app/header.blade.php
index 6f405e3..4589601 100644
--- a/resources/views/components/layouts/app/header.blade.php
+++ b/resources/views/components/layouts/app/header.blade.php
@@ -66,7 +66,7 @@
- Settings
+ Settings
diff --git a/resources/views/components/settings/layout.blade.php b/resources/views/components/settings/layout.blade.php
index 1c2a9b7..4d4c9fd 100644
--- a/resources/views/components/settings/layout.blade.php
+++ b/resources/views/components/settings/layout.blade.php
@@ -1,6 +1,7 @@
+ Preferences
Profile
Password
Appearance
diff --git a/resources/views/livewire/settings/preferences.blade.php b/resources/views/livewire/settings/preferences.blade.php
new file mode 100644
index 0000000..5c89a96
--- /dev/null
+++ b/resources/views/livewire/settings/preferences.blade.php
@@ -0,0 +1,63 @@
+assign_new_device_id = Auth::user()->assign_new_device_id;
+ }
+
+ public function updatePreferences(): void
+ {
+ $validated = $this->validate([
+ 'assign_new_device_id' => [
+ 'nullable',
+ Rule::exists('devices', 'id')->where(function ($query) {
+ $query->where('user_id', Auth::id())
+ ->whereNull('mirror_device_id');
+ }),
+ ],
+ ]);
+
+ Auth::user()->update($validated);
+
+ $this->dispatch('profile-updated');
+ }
+}; ?>
+
+
+ @include('partials.settings-heading')
+
+
+
+
+
+
diff --git a/routes/api.php b/routes/api.php
index 1a36c21..dae2e11 100644
--- a/routes/api.php
+++ b/routes/api.php
@@ -28,6 +28,7 @@ Route::get('/display', function (Request $request) {
'name' => "{$auto_assign_user->name}'s TRMNL",
'friendly_id' => Str::random(6),
'default_refresh_interval' => 900,
+ 'mirror_device_id' => $auto_assign_user->assign_new_device_id,
]);
} else {
return response()->json([
@@ -135,6 +136,7 @@ Route::get('/setup', function (Request $request) {
'name' => "{$auto_assign_user->name}'s TRMNL",
'friendly_id' => Str::random(6),
'default_refresh_interval' => 900,
+ 'mirror_device_id' => $auto_assign_user->assign_new_device_id,
]);
} else {
return response()->json([
diff --git a/routes/web.php b/routes/web.php
index 65d9912..c9210da 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -8,7 +8,8 @@ Route::get('/', function () {
})->name('home');
Route::middleware(['auth'])->group(function () {
- Route::redirect('settings', 'settings/profile');
+ Route::redirect('settings', 'settings/preferences');
+ Volt::route('settings/preferences', 'settings.preferences')->name('settings.preferences');
Volt::route('settings/profile', 'settings.profile')->name('settings.profile');
Volt::route('settings/password', 'settings.password')->name('settings.password');
Volt::route('settings/appearance', 'settings.appearance')->name('settings.appearance');
diff --git a/tests/Feature/Api/DeviceEndpointsTest.php b/tests/Feature/Api/DeviceEndpointsTest.php
index 06db54b..395151c 100644
--- a/tests/Feature/Api/DeviceEndpointsTest.php
+++ b/tests/Feature/Api/DeviceEndpointsTest.php
@@ -105,6 +105,45 @@ test('new device is auto-assigned to user with auto-assign enabled', function ()
->api_key->toBe('new-device-key');
});
+test('new device is auto-assigned and mirrors specified device', function () {
+ // Create a source device that will be mirrored
+ $sourceDevice = Device::factory()->create([
+ 'mac_address' => 'AA:BB:CC:DD:EE:FF',
+ 'api_key' => 'source-api-key',
+ 'current_screen_image' => 'source-image',
+ ]);
+
+ // Create user with auto-assign enabled and mirror device set
+ $user = User::factory()->create([
+ 'assign_new_devices' => true,
+ 'assign_new_device_id' => $sourceDevice->id,
+ ]);
+
+ // Make request from new device
+ $response = $this->withHeaders([
+ 'id' => '00:11:22:33:44:55',
+ 'access-token' => 'new-device-key',
+ 'rssi' => -70,
+ 'battery_voltage' => 3.8,
+ 'fw-version' => '1.0.0',
+ ])->get('/api/display');
+
+ $response->assertOk();
+
+ // Verify the new device was created and mirrors the source device
+ $newDevice = Device::where('mac_address', '00:11:22:33:44:55')->first();
+ expect($newDevice)
+ ->not->toBeNull()
+ ->user_id->toBe($user->id)
+ ->api_key->toBe('new-device-key')
+ ->mirror_device_id->toBe($sourceDevice->id);
+
+ // Verify the response contains the source device's image
+ $response->assertJson([
+ 'filename' => 'source-image.bmp',
+ ]);
+});
+
test('device setup endpoint returns correct data', function () {
$device = Device::factory()->create([
'mac_address' => '00:11:22:33:44:55',