diff --git a/resources/css/utilities.css b/resources/css/utilities.css index f36297bdf..5cd8cfd48 100644 --- a/resources/css/utilities.css +++ b/resources/css/utilities.css @@ -179,6 +179,10 @@ @utility box-without-bg-without-border { @apply flex p-2 transition-colors dark:hover:text-white hover:no-underline min-h-[4rem]; } +@utility coolbox { + @apply flex transition-all duration-150 dark:bg-coolgray-100 bg-white p-2 rounded-lg border border-neutral-200 dark:border-coolgray-400 hover:ring-2 dark:hover:ring-yellow-400 hover:ring-coollabs cursor-pointer min-h-[4rem]; +} + @utility on-box { @apply rounded-sm hover:bg-neutral-300 dark:hover:bg-coolgray-500/20; } diff --git a/resources/views/components/resource-view.blade.php b/resources/views/components/resource-view.blade.php index db8360fac..3d216d8da 100644 --- a/resources/views/components/resource-view.blade.php +++ b/resources/views/components/resource-view.blade.php @@ -1,7 +1,6 @@
!$upgrade, - 'hover:border-l-red-500 cursor-not-allowed' => $upgrade, + 'coolbox group', + '!cursor-not-allowed hover:border-l-red-500' => $upgrade, ])>
diff --git a/resources/views/livewire/admin/index.blade.php b/resources/views/livewire/admin/index.blade.php index 7bf3c7c0c..acba3acce 100644 --- a/resources/views/livewire/admin/index.blade.php +++ b/resources/views/livewire/admin/index.blade.php @@ -17,7 +17,7 @@ @if ($foundUsers->count() > 0)
@foreach ($foundUsers as $user) -
+
{{ $user->name }}
{{ $user->email }}
diff --git a/resources/views/livewire/dashboard.blade.php b/resources/views/livewire/dashboard.blade.php index 02a8d8367..9c6cb1463 100644 --- a/resources/views/livewire/dashboard.blade.php +++ b/resources/views/livewire/dashboard.blade.php @@ -35,7 +35,7 @@ class="flex items-center justify-center size-4 text-white rounded hover:bg-coolg @if ($projects->count() > 0)
@foreach ($projects as $project) -
+
@@ -103,7 +103,7 @@ class="flex items-center justify-center size-4 text-white rounded hover:bg-coolg @foreach ($servers as $server) !$server->settings->is_reachable || $server->settings->force_disabled, ])> diff --git a/resources/views/livewire/destination/index.blade.php b/resources/views/livewire/destination/index.blade.php index 0e6f3a005..b86998c48 100644 --- a/resources/views/livewire/destination/index.blade.php +++ b/resources/views/livewire/destination/index.blade.php @@ -17,7 +17,7 @@ @forelse ($servers as $server) @forelse ($server->destinations() as $destination) @if ($destination->getMorphClass() === 'App\Models\StandaloneDocker') -
{{ $destination->name }}
@@ -26,7 +26,7 @@
@endif @if ($destination->getMorphClass() === 'App\Models\SwarmDocker') -
{{ $destination->name }}
diff --git a/resources/views/livewire/project/index.blade.php b/resources/views/livewire/project/index.blade.php index e1e1a22bc..8b3f0af60 100644 --- a/resources/views/livewire/project/index.blade.php +++ b/resources/views/livewire/project/index.blade.php @@ -13,7 +13,7 @@
All your projects are here.
@foreach ($projects as $project) -
+
diff --git a/resources/views/livewire/project/new/github-private-repository-deploy-key.blade.php b/resources/views/livewire/project/new/github-private-repository-deploy-key.blade.php index bce55c4ad..6d644ba2c 100644 --- a/resources/views/livewire/project/new/github-private-repository-deploy-key.blade.php +++ b/resources/views/livewire/project/new/github-private-repository-deploy-key.blade.php @@ -7,7 +7,7 @@
@forelse ($private_keys as $key) @if ($private_key_id == $key->id) -
@@ -20,7 +20,7 @@ class="loading loading-xs dark:text-warning loading-spinner">
@else -
diff --git a/resources/views/livewire/project/new/github-private-repository.blade.php b/resources/views/livewire/project/new/github-private-repository.blade.php index 188e88c9d..ddc5217c7 100644 --- a/resources/views/livewire/project/new/github-private-repository.blade.php +++ b/resources/views/livewire/project/new/github-private-repository.blade.php @@ -21,7 +21,7 @@
@foreach ($github_apps as $ghapp)
-
diff --git a/resources/views/livewire/project/new/select.blade.php b/resources/views/livewire/project/new/select.blade.php index b777adfd8..c74c22dc0 100644 --- a/resources/views/livewire/project/new/select.blade.php +++ b/resources/views/livewire/project/new/select.blade.php @@ -1,8 +1,9 @@
@if ($current_step === 'type') -
-
+
+

New Resource

@@ -23,25 +24,34 @@
Filter by category - - + +
- - - + + +
-
- +
@@ -51,7 +61,9 @@ class="px-3 py-2 cursor-pointer hover:bg-neutral-100 dark:hover:bg-coolgray-200" :class="{ 'bg-neutral-50 dark:bg-coolgray-300': selectedCategory === '' }"> All Categories
- @@ -107,23 +120,23 @@ class="w-full h-full p-2 transition-all duration-200 dark:bg-white/10 bg-black/1

Databases

-
- +
@@ -131,46 +144,49 @@ class="grid justify-start grid-cols-1 gap-4 text-left xl:grid-cols-3"> Reload List
- The respective trademarks mentioned here are owned by the respective companies, and use of them does not imply any affiliation or endorsement. + The respective trademarks mentioned here are owned by the respective companies, and use of them + does not imply any affiliation or endorsement.
@@ -197,6 +213,8 @@ function searchResources() { gitBasedApplications: [], dockerBasedApplications: [], databases: [], + docLinkCache: {}, // Cache resolved doc URLs: { serviceName: url | null } + docCheckInProgress: {}, // Track ongoing checks: { serviceName: boolean } setType(type) { if (this.selecting) return; this.selecting = true; @@ -221,6 +239,81 @@ function searchResources() { this.$refs.searchInput.focus(); }); }, + extractBaseServiceName(serviceName) { + // Convert to lowercase and replace spaces with dashes to match original format + const normalized = serviceName.toLowerCase().replace(/\s+/g, '-'); + // Remove flavor suffixes: -with-*, -without-* + return normalized.replace(/-(with|without)-.+$/, ''); + }, + coolifyDocsUrl(serviceName) { + const baseName = this.extractBaseServiceName(serviceName); + return 'https://coolify.io/docs/services/' + baseName; + }, + officialDocsUrl(service) { + return service.documentation || null; + }, + async checkUrlExists(url) { + if (!url) return false; + try { + const response = await fetch(url, { + method: 'HEAD' + }); + return response.ok; + } catch (e) { + // CORS error or network error - assume URL exists + return true; + } + }, + async resolveDocLink(service) { + const serviceName = service.name; + + // Already cached? + if (this.docLinkCache.hasOwnProperty(serviceName)) { + return this.docLinkCache[serviceName]; + } + + // Already checking? + if (this.docCheckInProgress[serviceName]) { + return null; + } + + this.docCheckInProgress[serviceName] = true; + + // 1. Try Coolify docs first + const coolifyUrl = this.coolifyDocsUrl(serviceName); + const coolifyExists = await this.checkUrlExists(coolifyUrl); + + if (coolifyExists) { + this.docLinkCache[serviceName] = coolifyUrl; + this.docCheckInProgress[serviceName] = false; + return coolifyUrl; + } + + // 2. Fall back to official docs + const officialUrl = this.officialDocsUrl(service); + if (officialUrl) { + const officialExists = await this.checkUrlExists(officialUrl); + + if (officialExists) { + this.docLinkCache[serviceName] = officialUrl; + this.docCheckInProgress[serviceName] = false; + return officialUrl; + } + } + + // 3. Both failed - cache null to hide icon + this.docLinkCache[serviceName] = null; + this.docCheckInProgress[serviceName] = false; + return null; + }, + getDocLink(service) { + return this.docLinkCache[service.name]; + }, + shouldShowDocIcon(service) { + const cached = this.docLinkCache[service.name]; + // Show icon if: not checked yet OR has a valid URL + return cached === undefined || cached !== null; + }, filterAndSort(items, isSort = true) { const searchLower = this.search.trim().toLowerCase(); let filtered = Object.values(items); @@ -231,9 +324,10 @@ function searchResources() { filtered = filtered.filter(item => { if (!item.category) return false; // Handle comma-separated categories - const categories = item.category.includes(',') - ? item.category.split(',').map(c => c.trim().toLowerCase()) - : [item.category.toLowerCase()]; + const categories = item.category.includes(',') ? + item.category.split(',').map(c => c.trim().toLowerCase()) : [item.category + .toLowerCase() + ]; return categories.includes(selectedCategoryLower); }); } @@ -297,7 +391,7 @@ function searchResources() {
@else @forelse($servers as $server) -
+
{{ $server->name }} @@ -310,7 +404,8 @@ function searchResources() { @empty
- @@ -326,7 +421,7 @@ function searchResources() {
@if ($server->isSwarm()) @foreach ($swarmDockers as $swarmDocker) -
+
Swarm Docker ({{ $swarmDocker->name }}) @@ -336,7 +431,7 @@ function searchResources() { @endforeach @else @foreach ($standaloneDockers as $standaloneDocker) -
+
Standalone Docker ({{ $standaloneDocker->name }}) @@ -370,7 +465,8 @@ function searchResources() { @@ -388,7 +484,8 @@ function searchResources() {
@@ -426,7 +523,8 @@ function searchResources() { @@ -441,4 +539,4 @@ function searchResources() { Add Database @endif -
\ No newline at end of file +
diff --git a/resources/views/livewire/project/resource/index.blade.php b/resources/views/livewire/project/resource/index.blade.php index 807eb89b4..fab52fba6 100644 --- a/resources/views/livewire/project/resource/index.blade.php +++ b/resources/views/livewire/project/resource/index.blade.php @@ -54,7 +54,7 @@ class="button">+ @if ($environment->isEmpty()) @can('createAnyResource') + Add Resource + class="items-center justify-center coolbox">+ Add Resource @else
@@ -94,7 +94,7 @@ class="font-semibold" x-text="search">".

class="grid grid-cols-1 gap-4 pt-4 lg:grid-cols-2 xl:grid-cols-3">