feat: add delete confirmation modal for ConnectionProviders

- Introduced a Livewire-powered confirmation modal for deleting connection providers.
- Updated `delete` method to support selected provider deletion and modal interaction.
- Added `connectedApps` relationship to `ConnectionProvider` model for mass soft-deletes.
- Refactored delete logic to handle associated connected apps during provider deletion.
- Updated UI to include modal trigger and actions for enhanced user safety.
This commit is contained in:
= 2026-05-15 11:52:31 +00:00
parent 1c5ab498d7
commit 3c6e4bf4bd
4 changed files with 56 additions and 15 deletions

View File

@ -4,8 +4,8 @@
namespace App\Models; namespace App\Models;
use Illuminate\Database\Eloquent\{Model, Relations\BelongsTo, SoftDeletes};
use Illuminate\Database\Eloquent\Attributes\Fillable; use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\{Model, SoftDeletes};
use Illuminate\Database\Eloquent\Relations\HasOne; use Illuminate\Database\Eloquent\Relations\HasOne;
#[Fillable([ #[Fillable([
@ -24,9 +24,9 @@ public function protocol(): HasOne
return $this->hasOne(ConnectionProtocol::class, 'id', 'connection_protocol_id'); return $this->hasOne(ConnectionProtocol::class, 'id', 'connection_protocol_id');
} }
public function provider(): HasOne public function provider(): BelongsTo
{ {
return $this->hasOne(ConnectionProvider::class, 'id', 'connection_provider_id'); return $this->belongsTo(ConnectionProvider::class, 'connection_provider_id');
} }
public function status(): HasOne public function status(): HasOne

View File

@ -4,11 +4,16 @@
namespace App\Models; namespace App\Models;
use Illuminate\Database\Eloquent\{Model, Relations\HasMany, SoftDeletes};
use Illuminate\Database\Eloquent\Attributes\Fillable; use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\{Model, SoftDeletes};
#[Fillable(['name', 'slug'])] #[Fillable(['name', 'slug'])]
final class ConnectionProvider extends Model final class ConnectionProvider extends Model
{ {
use SoftDeletes; use SoftDeletes;
public function connectedApps(): HasMany
{
return $this->hasMany(ConnectedApp::class);
}
} }

View File

@ -65,15 +65,24 @@ public function update(string $slug, StoreConnectionProviderRequestData $data):
} }
/** /**
* Delete the connection provider and associated connected apps.
* @throws ModelNotFoundException when the connection provider is not found.
* @throws Throwable when an error occurs during the deletion. * @throws Throwable when an error occurs during the deletion.
* @throws InvalidArgumentException when id is invalid
*/ */
public function delete(string $slug): void public function delete(string $slug): void
{ {
DB::transaction( DB::transaction(
fn () => ConnectionProvider::query() function () use ($slug): void {
$provider = ConnectionProvider::query()
->where('slug', $slug) ->where('slug', $slug)
->delete() ->firstOrFail();
// Note: This performs a mass soft-delete query.
// Model events for ConnectedApp will NOT fire.
$provider->connectedApps()->delete();
$provider->delete();
}
); );
} }

View File

@ -17,6 +17,9 @@
#[DataCollectionOf(TableHeader::class)] #[DataCollectionOf(TableHeader::class)]
public DataCollection $headers; public DataCollection $headers;
public bool $cnfModal = false;
public string $selectedProvider = '';
public function boot(ConnectionProviderService $service): void public function boot(ConnectionProviderService $service): void
{ {
$this->service = $service; $this->service = $service;
@ -45,13 +48,26 @@ public function edit(string $providerSlug): void
$this->redirectRoute('apps.connectionProviders.edit', ['slug' => $providerSlug]); $this->redirectRoute('apps.connectionProviders.edit', ['slug' => $providerSlug]);
} }
public function delete(string $providerSlug): void public function delete(): void
{ {
$this->cnfModal = false;
$this->attempt( $this->attempt(
action: fn() => $this->service->delete($providerSlug), action: function () {
successMessage: "Connection Provider deleted!" $this->service->delete($this->selectedProvider);
}
); );
}
public function showModal(string $providerSlug): void
{
$this->selectedProvider = $providerSlug;
$this->cnfModal = true;
}
public function cancelModal(): void
{
$this->selectedProvider = '';
$this->cnfModal = false;
} }
}; };
?> ?>
@ -68,12 +84,23 @@ public function delete(string $providerSlug): void
<div class="flex"> <div class="flex">
<x-mary-button icon="lucide.edit" wire:click="edit('{{$row->slug}}')" spinner="edit('{{$row->slug}}')" <x-mary-button icon="lucide.edit" wire:click="edit('{{$row->slug}}')" spinner="edit('{{$row->slug}}')"
class="mr-2 btn-sm text-gray-500"/> class="mr-2 btn-sm text-gray-500"/>
<x-mary-button icon="lucide.trash" wire:click="delete('{{$row->slug}}')" <x-mary-button icon="lucide.trash" wire:click="showModal('{{$row->slug}}')"
spinner="delete('{{$row->slug}}')" spinner="showModal('{{$row->slug}}')"
variant="danger" variant="danger"
class="btn-sm btn-error btn-soft"/> class="btn-sm btn-error btn-soft"/>
</div> </div>
@endscope @endscope
</x-mary-table> </x-mary-table>
</x-shared.card> </x-shared.card>
<x-mary-modal wire:model="cnfModal" title="Delete Confirmation" persistent separator>
<x-mary-alert title="This will delete all the connected apps of this provider."
icon="o-exclamation-triangle"
class="alert-warning"
/>
<x-slot:actions>
<x-mary-button label="Cancel" wire:click="cancelModal"/>
<x-mary-button label="Proceed" wire:click="delete" spinner="delete" class="btn-error"/>
</x-slot:actions>
</x-mary-modal>
</div> </div>