Implement instance-wide SPA navigation toggle that enables smooth page transitions with prefetching on hover. Excludes terminal links which require full page lifecycle for WebSocket connections. Adds defensive checks to global-search component for SPA navigation compatibility. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
160 lines
8.2 KiB
PHP
160 lines
8.2 KiB
PHP
<div>
|
|
<x-slot:title>
|
|
Dashboard | Coolify
|
|
</x-slot>
|
|
@if (session('error'))
|
|
<span x-data x-init="$wire.emit('error', '{{ session('error') }}')" />
|
|
@endif
|
|
<h1>Dashboard</h1>
|
|
<div class="subtitle">Your self-hosted infrastructure.</div>
|
|
@if (request()->query->get('success'))
|
|
<div class=" mb-10 font-bold alert alert-success">
|
|
Your subscription has been activated! Welcome onboard! It could take a few seconds before your
|
|
subscription is activated.<br> Please be patient.
|
|
</div>
|
|
@endif
|
|
|
|
<section class="-mt-2">
|
|
<div class="flex items-center gap-2 pb-2">
|
|
<h3>Projects</h3>
|
|
@if ($projects->count() > 0)
|
|
<x-modal-input buttonTitle="Add" title="New Project">
|
|
<x-slot:content>
|
|
<button
|
|
class="flex items-center justify-center size-4 text-white rounded hover:bg-coolgray-400 dark:hover:bg-coolgray-300 cursor-pointer">
|
|
<svg class="size-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
|
stroke-width="2" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
|
|
</svg>
|
|
</button>
|
|
</x-slot:content>
|
|
<livewire:project.add-empty />
|
|
</x-modal-input>
|
|
@endif
|
|
</div>
|
|
@if ($projects->count() > 0)
|
|
<div class="grid grid-cols-1 gap-4 xl:grid-cols-2">
|
|
@foreach ($projects as $project)
|
|
<div class="relative gap-2 cursor-pointer coolbox group">
|
|
<a href="{{ $project->navigateTo() }}" {{ wireNavigate() }} class="absolute inset-0"></a>
|
|
<div class="flex flex-1 mx-6">
|
|
<div class="flex flex-col justify-center flex-1">
|
|
<div class="box-title">{{ $project->name }}</div>
|
|
<div class="box-description">
|
|
{{ $project->description }}
|
|
</div>
|
|
</div>
|
|
<div class="relative z-10 flex items-center justify-center gap-4 text-xs font-bold">
|
|
@if ($project->environments->first())
|
|
@can('createAnyResource')
|
|
<a class="hover:underline" {{ wireNavigate() }}
|
|
href="{{ route('project.resource.create', [
|
|
'project_uuid' => $project->uuid,
|
|
'environment_uuid' => $project->environments->first()->uuid,
|
|
]) }}">
|
|
+ Add Resource
|
|
</a>
|
|
@endcan
|
|
@endif
|
|
@can('update', $project)
|
|
<a class="hover:underline" {{ wireNavigate() }}
|
|
href="{{ route('project.edit', ['project_uuid' => $project->uuid]) }}">
|
|
Settings
|
|
</a>
|
|
@endcan
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
@else
|
|
<div class="flex flex-col gap-1">
|
|
<div class='font-bold dark:text-warning'>No projects found.</div>
|
|
<div class="flex items-center gap-1">
|
|
<x-modal-input buttonTitle="Add" title="New Project">
|
|
<livewire:project.add-empty />
|
|
</x-modal-input> your first project or
|
|
go to the <a class="underline dark:text-white" href="{{ route('onboarding') }}" {{ wireNavigate() }}>onboarding</a> page.
|
|
</div>
|
|
</div>
|
|
@endif
|
|
</section>
|
|
|
|
<section>
|
|
<div class="flex items-center gap-2 pb-2">
|
|
<h3>Servers</h3>
|
|
@if ($servers->count() > 0 && $privateKeys->count() > 0)
|
|
<x-modal-input buttonTitle="Add" title="New Server" :closeOutside="false">
|
|
<x-slot:content>
|
|
<button
|
|
class="flex items-center justify-center size-4 text-white rounded hover:bg-coolgray-400 dark:hover:bg-coolgray-300 cursor-pointer">
|
|
<svg class="size-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
|
stroke-width="2" stroke="currentColor">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
|
|
</svg>
|
|
</button>
|
|
</x-slot:content>
|
|
<livewire:server.create />
|
|
</x-modal-input>
|
|
@endif
|
|
</div>
|
|
@if ($servers->count() > 0)
|
|
<div class="grid grid-cols-1 gap-4 xl:grid-cols-2">
|
|
@foreach ($servers as $server)
|
|
<a href="{{ route('server.show', ['server_uuid' => data_get($server, 'uuid')]) }}" {{ wireNavigate() }}
|
|
@class([
|
|
'gap-2 border cursor-pointer coolbox group',
|
|
'border-red-500' =>
|
|
!$server->settings->is_reachable || $server->settings->force_disabled,
|
|
])>
|
|
<div class="flex flex-col justify-center mx-6">
|
|
<div class="box-title">
|
|
{{ $server->name }}
|
|
</div>
|
|
<div class="box-description">
|
|
{{ $server->description }}</div>
|
|
<div class="flex gap-1 text-xs text-error">
|
|
@if (!$server->settings->is_reachable)
|
|
Not reachable
|
|
@endif
|
|
@if (!$server->settings->is_reachable && !$server->settings->is_usable)
|
|
&
|
|
@endif
|
|
@if (!$server->settings->is_usable)
|
|
Not usable by Coolify
|
|
@endif
|
|
</div>
|
|
</div>
|
|
<div class="flex-1"></div>
|
|
</a>
|
|
@endforeach
|
|
</div>
|
|
@else
|
|
@if ($privateKeys->count() === 0)
|
|
<div class="flex flex-col gap-1">
|
|
<div class='font-bold dark:text-warning'>No private keys found.</div>
|
|
<div class="flex items-center gap-1">Before you can add your server, first <x-modal-input
|
|
buttonTitle="add" title="New Private Key">
|
|
<livewire:security.private-key.create from="server" />
|
|
</x-modal-input> a private key
|
|
or
|
|
go to the <a class="underline dark:text-white" href="{{ route('onboarding') }}" {{ wireNavigate() }}>onboarding</a>
|
|
page.
|
|
</div>
|
|
</div>
|
|
@else
|
|
<div class="flex flex-col gap-1">
|
|
<div class='font-bold dark:text-warning'>No servers found.</div>
|
|
<div class="flex items-center gap-1">
|
|
<x-modal-input buttonTitle="Add" title="New Server" :closeOutside="false">
|
|
<livewire:server.create />
|
|
</x-modal-input> your first server
|
|
or
|
|
go to the <a class="underline dark:text-white" href="{{ route('onboarding') }}" {{ wireNavigate() }}>onboarding</a>
|
|
page.
|
|
</div>
|
|
</div>
|
|
@endif
|
|
@endif
|
|
</section>
|
|
</div>
|