diff --git a/app/Livewire/Admin/Maintenance.php b/app/Livewire/Admin/Maintenance.php index 14b4472..70219b0 100644 --- a/app/Livewire/Admin/Maintenance.php +++ b/app/Livewire/Admin/Maintenance.php @@ -76,17 +76,17 @@ class Maintenance extends Component return null; } - if ($state === 'scheduled') { - if (empty($this->hostIds)) { - $this->addError('hostIds', __('maintenance.need_host')); + // A window always needs a host — otherwise a hostless draft can never be + // published (there is no edit-hosts action) and is stuck. + if (empty($this->hostIds)) { + $this->addError('hostIds', __('maintenance.need_host')); - return null; - } - if ($ends->isPast()) { - $this->addError('endsAt', __('maintenance.end_future')); + return null; + } + if ($state === 'scheduled' && $ends->isPast()) { + $this->addError('endsAt', __('maintenance.end_future')); - return null; - } + return null; } // Create the window and attach hosts atomically so a bad id can't leave @@ -163,6 +163,7 @@ class Maintenance extends Component $notified = MaintenanceNotification::query() ->where('maintenance_window_id', $window->id) ->where('event', 'announcement') + ->whereNotNull('sent_at') // only customers who actually received it ->pluck('customer_id'); foreach (Customer::query()->whereIn('id', $notified)->get() as $customer) { $this->deliverOnce($window, $customer, 'cancelled', new MaintenanceCancelledMail($window, $customer)); diff --git a/tests/Feature/Admin/MaintenanceTest.php b/tests/Feature/Admin/MaintenanceTest.php index 70c8a4d..a9c97f7 100644 --- a/tests/Feature/Admin/MaintenanceTest.php +++ b/tests/Feature/Admin/MaintenanceTest.php @@ -54,9 +54,9 @@ it('emails a cancellation to customers who were announced', function () { $customer = affectedCustomerOn($host); $window = MaintenanceWindow::factory()->scheduled()->create(); $window->hosts()->attach($host->id); - // Simulate the customer having received the announcement. + // Simulate the customer having actually received the announcement. \App\Models\MaintenanceNotification::create([ - 'maintenance_window_id' => $window->id, 'customer_id' => $customer->id, 'event' => 'announcement', 'email' => $customer->email, + 'maintenance_window_id' => $window->id, 'customer_id' => $customer->id, 'event' => 'announcement', 'email' => $customer->email, 'sent_at' => now(), ]); Livewire::actingAs(operator('Owner'))->test(Maintenance::class)->call('cancel', $window->uuid);