fix(ui): standardize permission denial callouts

This commit is contained in:
Andras Bacsai 2026-06-04 11:37:16 +02:00
parent 5973bb4d4f
commit 8a2373e49f
9 changed files with 98 additions and 26 deletions

View file

@ -18,7 +18,7 @@
</form> </form>
</div> </div>
@else @else
<x-callout type="warning" title="Permission Required"> <x-callout type="danger" title="Insufficient Permissions">
You don't have permission to create new destinations. Please contact your team administrator for access. You don't have permission to create new destinations. Please contact your team administrator for access.
</x-callout> </x-callout>
@endcan @endcan

View file

@ -101,16 +101,10 @@
</div> </div>
</div> </div>
</div> </div>
@else
<x-callout type="warning" title="Access Restricted">
You don't have permission to clone resources. Contact your team administrator to request access.
</x-callout>
@endcan
<h3 class="pt-4">Move Resource</h3> <h3 class="pt-4">Move Resource</h3>
<div class="pb-4">Transfer this resource between projects and environments.</div> <div class="pb-4">Transfer this resource between projects and environments.</div>
@can('update', $resource)
@if ($projects->count() > 0) @if ($projects->count() > 0)
<div class="space-y-4"> <div class="space-y-4">
<div class="flex flex-col lg:flex-row gap-4"> <div class="flex flex-col lg:flex-row gap-4">
@ -160,9 +154,8 @@
</div> </div>
@endif @endif
@else @else
<x-callout type="warning" title="Access Restricted"> <x-callout type="danger" title="Insufficient Permissions">
You don't have permission to move resources between projects or environments. Contact your team You don't have permission to modify this resource. Contact your team administrator for access.
administrator to request access.
</x-callout> </x-callout>
@endcan @endcan
</div> </div>

View file

@ -10,8 +10,8 @@
<x-forms.button type="submit">Add</x-forms.button> <x-forms.button type="submit">Add</x-forms.button>
</form> </form>
@else @else
<x-callout type="warning" title="Access Restricted" class="mt-4"> <x-callout type="danger" title="Insufficient Permissions" class="mt-4">
You don't have permission to manage tags. Contact your team administrator to request access. You don't have permission to manage this resource. Contact your team administrator for access.
</x-callout> </x-callout>
@endcan @endcan
@if (data_get($this->resource, 'tags') && count(data_get($this->resource, 'tags')) > 0) @if (data_get($this->resource, 'tags') && count(data_get($this->resource, 'tags')) > 0)

View file

@ -71,6 +71,11 @@ class="underline">documentation</a>.
</x-callout> </x-callout>
@endif @endif
@if (!$isCloudflareTunnelsEnabled && $server->isFunctional()) @if (!$isCloudflareTunnelsEnabled && $server->isFunctional())
@cannot('update', $server)
<x-callout type="danger" title="Insufficient Permissions" class="mb-4">
You don't have permission to configure Cloudflare Tunnel for this server.
</x-callout>
@endcannot
<div class="flex flex-col pb-2"> <div class="flex flex-col pb-2">
<h3>Automated </h3> <h3>Automated </h3>
<a href="https://coolify.io/docs/knowledge-base/cloudflare/tunnels/server-ssh" target="_blank" <a href="https://coolify.io/docs/knowledge-base/cloudflare/tunnels/server-ssh" target="_blank"
@ -91,10 +96,6 @@ class="flex flex-col gap-2 w-full">
helper="The SSH domain you configured in Cloudflare. Make sure there is no protocol like http(s):// so you provide a FQDN not a URL. <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/cloudflare/tunnels/server-ssh' target='_blank'>Documentation</a>" /> helper="The SSH domain you configured in Cloudflare. Make sure there is no protocol like http(s):// so you provide a FQDN not a URL. <a class='underline dark:text-white' href='https://coolify.io/docs/knowledge-base/cloudflare/tunnels/server-ssh' target='_blank'>Documentation</a>" />
<x-forms.button type="submit" isHighlighted>Continue</x-forms.button> <x-forms.button type="submit" isHighlighted>Continue</x-forms.button>
</form> </form>
@else
<x-callout type="warning" title="Permission Required" class="mb-4">
You don't have permission to configure Cloudflare Tunnel for this server.
</x-callout>
@endcan @endcan
</div> </div>
@script @script
@ -121,10 +122,6 @@ class="flex flex-col gap-2 w-full">
]" confirmationText="I manually configured Cloudflare Tunnel" ]" confirmationText="I manually configured Cloudflare Tunnel"
confirmationLabel="Please type the confirmation text to confirm that you manually configured Cloudflare Tunnel." confirmationLabel="Please type the confirmation text to confirm that you manually configured Cloudflare Tunnel."
shortConfirmationLabel="Confirmation text" /> shortConfirmationLabel="Confirmation text" />
@else
<x-callout type="warning" title="Permission Required" class="mb-4">
You don't have permission to configure Cloudflare Tunnel for this server.
</x-callout>
@endcan @endcan
</div> </div>
@endif @endif

View file

@ -174,7 +174,7 @@ class="underline text-white">Traefik changelog</a> to understand breaking change
</x-forms.button> --}} </x-forms.button> --}}
</div> </div>
@else @else
<x-callout type="warning" title="Permission Required" class="mb-4"> <x-callout type="danger" title="Insufficient Permissions" class="mb-4">
You don't have permission to configure proxy settings for this server. You don't have permission to configure proxy settings for this server.
</x-callout> </x-callout>
@endcan @endcan

View file

@ -63,7 +63,7 @@ class="flex items-center justify-between w-full px-1 py-2 text-left select-none
</x-forms.button> </x-forms.button>
</form> </form>
@else @else
<x-callout type="warning" title="Permission Required"> <x-callout type="danger" title="Insufficient Permissions">
You don't have permission to create new GitHub Apps. Please contact your team administrator for access. You don't have permission to create new GitHub Apps. Please contact your team administrator for access.
</x-callout> </x-callout>
@endcan @endcan

View file

@ -24,7 +24,7 @@
</form> </form>
</div> </div>
@else @else
<x-callout type="warning" title="Permission Required"> <x-callout type="danger" title="Insufficient Permissions">
You don't have permission to create new S3 storage configurations. Please contact your team administrator for You don't have permission to create new S3 storage configurations. Please contact your team administrator for
access. access.
</x-callout> </x-callout>

View file

@ -54,7 +54,7 @@
<div class="flex gap-2"> <div class="flex gap-2">
<h1>Subscription</h1> <h1>Subscription</h1>
</div> </div>
<x-callout type="warning" title="Permission Required"> <x-callout type="danger" title="Insufficient Permissions">
You are not an admin so you cannot manage your Team's subscription. If this does not make sense, please You are not an admin so you cannot manage your Team's subscription. If this does not make sense, please
<span class="underline cursor-pointer dark:text-white" wire:click="help">contact us</span>. <span class="underline cursor-pointer dark:text-white" wire:click="help">contact us</span>.
</x-callout> </x-callout>

View file

@ -0,0 +1,82 @@
<?php
use App\Livewire\Project\Shared\ResourceOperations;
use App\Livewire\Project\Shared\Tags;
use App\Models\Application;
use App\Models\Environment;
use App\Models\Project;
use App\Models\Server;
use App\Models\Team;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
uses(RefreshDatabase::class);
function resourcePermissionCalloutApplicationFor(string $role): Application
{
$user = User::factory()->create();
$team = Team::factory()->create();
$user->teams()->attach($team, ['role' => $role]);
$server = Server::factory()->create(['team_id' => $team->id]);
$destination = $server->standaloneDockers()->firstOrFail();
$project = Project::factory()->create(['team_id' => $team->id]);
$environment = Environment::factory()->create(['project_id' => $project->id]);
test()->actingAs($user);
session(['currentTeam' => $team]);
return Application::factory()->create([
'environment_id' => $environment->id,
'destination_id' => $destination->id,
'destination_type' => $destination->getMorphClass(),
]);
}
it('shows one red insufficient permissions callout for resource operations when update is denied', function () {
$application = resourcePermissionCalloutApplicationFor('member');
$component = Livewire::test(ResourceOperations::class, ['resource' => $application])
->assertSee('Insufficient Permissions')
->assertSee('permission to modify this resource')
->assertSee('team administrator for access')
->assertDontSee('Access Restricted')
->assertDontSee("You don't have permission to clone resources")
->assertDontSee("You don't have permission to move resources");
expect(substr_count($component->html(), 'Insufficient Permissions'))->toBe(1)
->and($component->html())->toContain('bg-red-50')
->and($component->html())->not->toContain('bg-warning-50');
});
it('shows the red insufficient permissions callout for tags when update is denied', function () {
$application = resourcePermissionCalloutApplicationFor('member');
$component = Livewire::test(Tags::class, ['resource' => $application])
->assertSee('Insufficient Permissions')
->assertSee('permission to manage this resource')
->assertSee('team administrator for access')
->assertDontSee('Access Restricted')
->assertDontSee("You don't have permission to manage tags");
expect(substr_count($component->html(), 'Insufficient Permissions'))->toBe(1)
->and($component->html())->toContain('bg-red-50')
->and($component->html())->not->toContain('bg-warning-50');
});
it('does not use yellow permission callouts in blade views', function () {
$offendingFiles = collect(new RecursiveIteratorIterator(new RecursiveDirectoryIterator(resource_path('views'))))
->filter(fn (SplFileInfo $file) => $file->isFile() && $file->getExtension() === 'php')
->filter(function (SplFileInfo $file) {
$contents = file_get_contents($file->getPathname());
return str_contains($contents, 'type="warning" title="Permission Required"')
|| str_contains($contents, 'title="Access Restricted"');
})
->map(fn (SplFileInfo $file) => str_replace(base_path().'/', '', $file->getPathname()))
->values()
->all();
expect($offendingFiles)->toBeEmpty();
});