id(); $table->uuid('uuid')->unique(); $table->string('title'); $table->text('public_description')->nullable(); $table->text('internal_notes')->nullable(); $table->timestamp('starts_at'); $table->timestamp('ends_at'); $table->string('state')->default('draft'); // draft | scheduled | cancelled $table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete(); $table->timestamp('published_at')->nullable(); $table->timestamp('cancelled_at')->nullable(); $table->string('cancellation_reason')->nullable(); $table->timestamps(); }); Schema::create('host_maintenance_window', function (Blueprint $table) { $table->id(); $table->foreignId('host_id')->constrained()->cascadeOnDelete(); $table->foreignId('maintenance_window_id')->constrained()->cascadeOnDelete(); $table->unique(['host_id', 'maintenance_window_id']); }); // Idempotent notification ledger — one row per (window, customer, event). Schema::create('maintenance_notifications', function (Blueprint $table) { $table->id(); $table->foreignId('maintenance_window_id')->constrained()->cascadeOnDelete(); $table->foreignId('customer_id')->constrained()->cascadeOnDelete(); $table->string('event'); // announcement | cancelled $table->string('email'); $table->timestamp('sent_at')->nullable(); $table->timestamps(); $table->unique(['maintenance_window_id', 'customer_id', 'event'], 'mw_notif_unique'); }); } public function down(): void { Schema::dropIfExists('maintenance_notifications'); Schema::dropIfExists('host_maintenance_window'); Schema::dropIfExists('maintenance_windows'); } };