mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
fix(ui): replace native browser dialogs with confirmation modals
Use the Coolify confirmation modal instead of wire:confirm for server management, server transfer and import, resource migration, and S3 backup disabling. Show a toast instead of alert() for a missing GitHub App webhook endpoint. Add a test that blocks native dialogs in views. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
15909fa641
commit
1f0e2f74d0
@@ -1,4 +1,4 @@
|
||||
<div x-data="{
|
||||
<div x-on:migrate-resource-confirmed.window="$wire.migrateTo(selectedMigrateDestination)" x-data="{
|
||||
selectedCloneServer: null,
|
||||
selectedCloneDestination: null,
|
||||
selectedCloneProject: null,
|
||||
@@ -217,11 +217,17 @@
|
||||
The resource keeps the same UUID and configuration. Only the hosting server and network
|
||||
destination change.
|
||||
</p>
|
||||
<x-forms.button
|
||||
wire:confirm="Migrate this resource? It will be stopped on the source server. Redeploy after migration completes."
|
||||
@click="$wire.migrateTo(selectedMigrateDestination)">
|
||||
Migrate resource
|
||||
</x-forms.button>
|
||||
<x-modal-confirmation title="Migrate this resource?" submitAction="migrate-resource-confirmed"
|
||||
dispatchAction :confirmWithText="false" :confirmWithPassword="false"
|
||||
step2ButtonText="Migrate resource"
|
||||
:actions="[
|
||||
'The resource will be stopped on the source server.',
|
||||
'Redeploy the resource after the migration completes.',
|
||||
]">
|
||||
<x-slot:trigger>
|
||||
<x-forms.button>Migrate resource</x-forms.button>
|
||||
</x-slot:trigger>
|
||||
</x-modal-confirmation>
|
||||
</div>
|
||||
</x-application.settings-section>
|
||||
@endif
|
||||
|
||||
@@ -124,17 +124,37 @@
|
||||
</x-forms.button>
|
||||
</x-process-dialog>
|
||||
@if (isDev())
|
||||
<x-forms.button type="button" wire:click="toggleManagement"
|
||||
wire:confirm="{{ $server->isTransferredAway()
|
||||
? 'Enable management? Confirm that no other Coolify instance manages this server. Two active instances can cause conflicting deployments, proxy changes, backups, and restarts.'
|
||||
: 'Disable management? Deployments, webhooks, scheduled tasks, backups, proxy updates, and monitoring will stop on this instance. Existing workloads continue to run.' }}"
|
||||
title="{{ $server->isTransferredAway()
|
||||
? 'Let this instance run deployments, webhooks, scheduled tasks, backups, proxy updates, and monitoring for this server'
|
||||
: 'Stop deployments, webhooks, scheduled tasks, backups, proxy updates, and monitoring for this server on this instance' }}"
|
||||
canGate="update" :canResource="$server">
|
||||
<x-reicon name="{{ $server->isTransferredAway() ? 'play-circle' : 'stop-circle' }}" class="size-3.5" />
|
||||
{{ $server->isTransferredAway() ? 'Enable management' : 'Disable management' }}
|
||||
</x-forms.button>
|
||||
<div wire:key="server-management-{{ $server->isTransferredAway() ? 'enable' : 'disable' }}">
|
||||
@if ($server->isTransferredAway())
|
||||
<x-modal-confirmation title="Enable management on this instance?"
|
||||
submitAction="toggleManagement" :confirmWithText="false"
|
||||
:confirmWithPassword="false" step2ButtonText="Enable management"
|
||||
warningMessage="Before you continue, disable this server on every other Coolify instance. Two active instances can cause conflicting deployments, proxy changes, backups, and restarts."
|
||||
:actions="['This instance will run deployments, webhooks, scheduled tasks, backups, proxy updates, and monitoring for this server.']">
|
||||
<x-slot:trigger>
|
||||
<x-forms.button type="button" canGate="update" :canResource="$server">
|
||||
<x-reicon name="play-circle" class="size-3.5" />
|
||||
Enable management
|
||||
</x-forms.button>
|
||||
</x-slot:trigger>
|
||||
</x-modal-confirmation>
|
||||
@else
|
||||
<x-modal-confirmation title="Disable management on this instance?"
|
||||
submitAction="toggleManagement" :confirmWithText="false"
|
||||
:confirmWithPassword="false" step2ButtonText="Disable management"
|
||||
:actions="[
|
||||
'Deployments, webhooks, scheduled tasks, backups, proxy updates, and monitoring will stop on this instance for this server.',
|
||||
'Existing workloads continue to run on the server.',
|
||||
]">
|
||||
<x-slot:trigger>
|
||||
<x-forms.button type="button" canGate="update" :canResource="$server">
|
||||
<x-reicon name="stop-circle" class="size-3.5" />
|
||||
Disable management
|
||||
</x-forms.button>
|
||||
</x-slot:trigger>
|
||||
</x-modal-confirmation>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
@if ($server->isTransferredAway())
|
||||
<x-status-badge label="Transferred away" type="warning" />
|
||||
|
||||
@@ -29,12 +29,16 @@
|
||||
<span wire:loading.remove wire:target="dryRun">Dry run</span>
|
||||
<span wire:loading wire:target="dryRun">Checking…</span>
|
||||
</x-forms.button>
|
||||
<x-forms.button wire:click="importBundle" wire:loading.attr="disabled"
|
||||
wire:target="dryRun,importBundle"
|
||||
wire:confirm="Import this server into the current team?">
|
||||
<span wire:loading.remove wire:target="importBundle">Import server</span>
|
||||
<span wire:loading wire:target="importBundle">Importing…</span>
|
||||
</x-forms.button>
|
||||
<x-modal-confirmation title="Import this server?" submitAction="importBundle"
|
||||
:confirmWithText="false" :confirmWithPassword="false" step2ButtonText="Import server"
|
||||
:actions="['The server from the bundle will be added to the current team.']">
|
||||
<x-slot:trigger>
|
||||
<x-forms.button wire:loading.attr="disabled" wire:target="dryRun,importBundle">
|
||||
<span wire:loading.remove wire:target="importBundle">Import server</span>
|
||||
<span wire:loading wire:target="importBundle">Importing…</span>
|
||||
</x-forms.button>
|
||||
</x-slot:trigger>
|
||||
</x-modal-confirmation>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -50,12 +50,20 @@
|
||||
label="Write ownership file on the host via SSH (optional)" />
|
||||
</div>
|
||||
<div>
|
||||
<x-forms.button canGate="update" :canResource="$server" wire:click="migrateServer"
|
||||
wire:loading.attr="disabled"
|
||||
wire:confirm="Transfer this server to the target instance? Automations will be disabled here.">
|
||||
<span wire:loading.remove wire:target="migrateServer">Transfer server</span>
|
||||
<span wire:loading wire:target="migrateServer">Transferring…</span>
|
||||
</x-forms.button>
|
||||
<x-modal-confirmation title="Transfer this server?" submitAction="migrateServer"
|
||||
:confirmWithText="false" :confirmWithPassword="false" step2ButtonText="Transfer server"
|
||||
:actions="[
|
||||
'The server will be transferred to the target instance.',
|
||||
'Automations for this server will be disabled on this instance.',
|
||||
]">
|
||||
<x-slot:trigger>
|
||||
<x-forms.button canGate="update" :canResource="$server" wire:loading.attr="disabled"
|
||||
wire:target="migrateServer">
|
||||
<span wire:loading.remove wire:target="migrateServer">Transfer server</span>
|
||||
<span wire:loading wire:target="migrateServer">Transferring…</span>
|
||||
</x-forms.button>
|
||||
</x-slot:trigger>
|
||||
</x-modal-confirmation>
|
||||
</div>
|
||||
@if (count($lastWarnings) > 0)
|
||||
<x-callout type="warning" title="Warnings">
|
||||
@@ -116,11 +124,16 @@
|
||||
<p class="mb-3 text-xs leading-5 text-neutral-500 dark:text-fg-dim">
|
||||
Disable automations after a manual import on the target.
|
||||
</p>
|
||||
<x-forms.button canGate="update" :canResource="$server" wire:click="completeTransfer"
|
||||
wire:loading.attr="disabled"
|
||||
wire:confirm="Disable automations on this server?">
|
||||
Mark transferred & disable automations
|
||||
</x-forms.button>
|
||||
<x-modal-confirmation title="Disable automations on this server?"
|
||||
submitAction="completeTransfer" :confirmWithText="false"
|
||||
:confirmWithPassword="false" step2ButtonText="Disable automations"
|
||||
:actions="['The server will be marked as transferred and automations will be disabled on this instance.']">
|
||||
<x-slot:trigger>
|
||||
<x-forms.button canGate="update" :canResource="$server">
|
||||
Mark transferred & disable automations
|
||||
</x-forms.button>
|
||||
</x-slot:trigger>
|
||||
</x-modal-confirmation>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
@@ -360,11 +360,11 @@
|
||||
const selectedEndpoint = webhook_endpoint ? webhook_endpoint.trim() : '';
|
||||
const customEndpoint = custom_webhook_endpoint ? custom_webhook_endpoint.trim() : '';
|
||||
if (use_custom_webhook_endpoint && !customEndpoint) {
|
||||
alert('Please enter a custom webhook endpoint.');
|
||||
window.toast('Error', { type: 'danger', description: 'Please enter a custom webhook endpoint.' });
|
||||
return;
|
||||
}
|
||||
if (!use_custom_webhook_endpoint && !selectedEndpoint) {
|
||||
alert('Please enter a webhook endpoint.');
|
||||
window.toast('Error', { type: 'danger', description: 'Please enter a webhook endpoint.' });
|
||||
return;
|
||||
}
|
||||
let baseUrl = (use_custom_webhook_endpoint ? customEndpoint : selectedEndpoint).replace(/\/+$/, '');
|
||||
|
||||
@@ -102,11 +102,16 @@
|
||||
portal />
|
||||
<button type="button" class="button shrink-0"
|
||||
wire:click="moveBackup({{ $backup->id }})">Move</button>
|
||||
<button type="button" class="button shrink-0 text-error"
|
||||
wire:click="disableS3({{ $backup->id }})"
|
||||
wire:confirm="Are you sure you want to disable S3 for this backup schedule?">
|
||||
Disable
|
||||
</button>
|
||||
<div wire:key="disableS3-{{ $backup->id }}">
|
||||
<x-modal-confirmation title="Disable S3 for this backup schedule?"
|
||||
submitAction="disableS3({{ $backup->id }})" :confirmWithText="false"
|
||||
:confirmWithPassword="false" step2ButtonText="Disable S3"
|
||||
:actions="['Backups from this schedule will no longer be uploaded to S3.']">
|
||||
<x-slot:trigger>
|
||||
<button type="button" class="button shrink-0 text-error">Disable</button>
|
||||
</x-slot:trigger>
|
||||
</x-modal-confirmation>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
@@ -142,11 +147,16 @@
|
||||
portal />
|
||||
<button type="button" class="button shrink-0"
|
||||
wire:click="moveVolumeBackup({{ $backup->id }})">Move</button>
|
||||
<button type="button" class="button shrink-0 text-error"
|
||||
wire:click="disableVolumeS3({{ $backup->id }})"
|
||||
wire:confirm="Are you sure you want to disable S3 for this backup schedule?">
|
||||
Disable
|
||||
</button>
|
||||
<div wire:key="disableVolumeS3-{{ $backup->id }}">
|
||||
<x-modal-confirmation title="Disable S3 for this backup schedule?"
|
||||
submitAction="disableVolumeS3({{ $backup->id }})" :confirmWithText="false"
|
||||
:confirmWithPassword="false" step2ButtonText="Disable S3"
|
||||
:actions="['Backups from this schedule will no longer be uploaded to S3.']">
|
||||
<x-slot:trigger>
|
||||
<button type="button" class="button shrink-0 text-error">Disable</button>
|
||||
</x-slot:trigger>
|
||||
</x-modal-confirmation>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
@@ -148,7 +148,8 @@ test('server overview shows a management button that matches the ownership state
|
||||
Livewire::test(Show::class, ['server_uuid' => $this->server->uuid])
|
||||
->assertSee('Disable management')
|
||||
->assertDontSee('Enable management')
|
||||
->call('toggleManagement')
|
||||
->assertDontSeeHtml('wire:confirm')
|
||||
->call('toggleManagement', '')
|
||||
->assertSee('Enable management')
|
||||
->assertDontSee('Disable management');
|
||||
});
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Finder\Finder;
|
||||
|
||||
it('does not use native browser dialogs in views or scripts', function () {
|
||||
$files = Finder::create()
|
||||
->files()
|
||||
->in([__DIR__.'/../../resources/views', __DIR__.'/../../resources/js'])
|
||||
->name(['*.blade.php', '*.js']);
|
||||
|
||||
$offenders = [];
|
||||
foreach ($files as $file) {
|
||||
foreach (explode("\n", $file->getContents()) as $number => $line) {
|
||||
if (preg_match('/wire:confirm|(?<![\w.$])(?:window\.)?(?:alert|confirm|prompt)\s*\(/', $line)) {
|
||||
$offenders[] = $file->getRelativePathname().':'.($number + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect($offenders)->toBeEmpty();
|
||||
});
|
||||
Reference in New Issue
Block a user