Refactor UI components to use 'coolbox' class for consistent styling across various views
- Updated dashboard, destination, project, and server views to replace 'box' class with 'coolbox' for improved visual consistency. - Modified links and buttons in shared variables and scheduled tasks views to utilize 'coolbox' class. - Ensured all relevant components reflect the new styling approach, enhancing user experience and interface coherence.
This commit is contained in:
parent
d71efadce4
commit
d905ae107b
24 changed files with 219 additions and 118 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
<div @class([
|
||||
'transition-all duration-150 box-without-bg dark:bg-coolgray-100 bg-white group 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' => !$upgrade,
|
||||
'hover:border-l-red-500 cursor-not-allowed' => $upgrade,
|
||||
'coolbox group',
|
||||
'!cursor-not-allowed hover:border-l-red-500' => $upgrade,
|
||||
])>
|
||||
<div class="flex items-center">
|
||||
<div class="w-[4.5rem] h-[4.5rem] flex items-center justify-center text-black dark:text-white shrink-0 rounded-lg overflow-hidden">
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
@if ($foundUsers->count() > 0)
|
||||
<div class="flex flex-wrap gap-2 pt-4">
|
||||
@foreach ($foundUsers as $user)
|
||||
<div class="box w-64 group" wire:click="switchUser({{ $user->id }})">
|
||||
<div class="coolbox w-64 group" wire:click="switchUser({{ $user->id }})">
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="box-title">{{ $user->name }}</div>
|
||||
<div class="box-description">{{ $user->email }}</div>
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ class="flex items-center justify-center size-4 text-white rounded hover:bg-coolg
|
|||
@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 box group">
|
||||
<div class="relative gap-2 cursor-pointer coolbox group">
|
||||
<a href="{{ $project->navigateTo() }}" class="absolute inset-0"></a>
|
||||
<div class="flex flex-1 mx-6">
|
||||
<div class="flex flex-col justify-center flex-1">
|
||||
|
|
@ -103,7 +103,7 @@ class="flex items-center justify-center size-4 text-white rounded hover:bg-coolg
|
|||
@foreach ($servers as $server)
|
||||
<a href="{{ route('server.show', ['server_uuid' => data_get($server, 'uuid')]) }}"
|
||||
@class([
|
||||
'gap-2 border cursor-pointer box group',
|
||||
'gap-2 border cursor-pointer coolbox group',
|
||||
'border-red-500' =>
|
||||
!$server->settings->is_reachable || $server->settings->force_disabled,
|
||||
])>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
@forelse ($servers as $server)
|
||||
@forelse ($server->destinations() as $destination)
|
||||
@if ($destination->getMorphClass() === 'App\Models\StandaloneDocker')
|
||||
<a class="box group"
|
||||
<a class="coolbox group"
|
||||
href="{{ route('destination.show', ['destination_uuid' => data_get($destination, 'uuid')]) }}">
|
||||
<div class="flex flex-col justify-center mx-6">
|
||||
<div class="box-title">{{ $destination->name }}</div>
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
</a>
|
||||
@endif
|
||||
@if ($destination->getMorphClass() === 'App\Models\SwarmDocker')
|
||||
<a class="box group"
|
||||
<a class="coolbox group"
|
||||
href="{{ route('destination.show', ['destination_uuid' => data_get($destination, 'uuid')]) }}">
|
||||
<div class="flex flex-col mx-6">
|
||||
<div class="box-title">{{ $destination->name }}</div>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<div class="subtitle">All your projects are here.</div>
|
||||
<div class="grid grid-cols-1 gap-4 xl:grid-cols-2 -mt-1">
|
||||
@foreach ($projects as $project)
|
||||
<div class="relative gap-2 cursor-pointer box group">
|
||||
<div class="relative gap-2 cursor-pointer coolbox group">
|
||||
<a href="{{ $project->navigateTo() }}" class="absolute inset-0"></a>
|
||||
<div class="flex flex-1 mx-6">
|
||||
<div class="flex flex-col justify-center flex-1">
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<div class="flex flex-col justify-center gap-2 text-left ">
|
||||
@forelse ($private_keys as $key)
|
||||
@if ($private_key_id == $key->id)
|
||||
<div class="gap-2 py-4 cursor-pointer group hover:bg-coollabs bg-coolgray-100 box"
|
||||
<div class="gap-2 py-4 cursor-pointer group hover:bg-coollabs bg-coolgray-100 coolbox"
|
||||
wire:click="setPrivateKey('{{ $key->id }}')" wire:key="{{ $key->id }}">
|
||||
<div class="flex flex-col mx-6">
|
||||
<div class="box-title">
|
||||
|
|
@ -20,7 +20,7 @@ class="loading loading-xs dark:text-warning loading-spinner"></span>
|
|||
</div>
|
||||
</div>
|
||||
@else
|
||||
<div class="gap-2 py-4 cursor-pointer group hover:bg-coollabs bg-coolgray-100 box"
|
||||
<div class="gap-2 py-4 cursor-pointer group hover:bg-coollabs bg-coolgray-100 coolbox"
|
||||
wire:click="setPrivateKey('{{ $key->id }}')" wire:key="{{ $key->id }}">
|
||||
<div class="flex flex-col mx-6">
|
||||
<div class="box-title">
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
<div class="flex flex-col justify-center gap-2 text-left">
|
||||
@foreach ($github_apps as $ghapp)
|
||||
<div class="flex">
|
||||
<div class="w-full gap-2 py-4 bg-white cursor-pointer group hover:bg-coollabs dark:bg-coolgray-200 box"
|
||||
<div class="w-full gap-2 py-4 bg-white cursor-pointer group hover:bg-coollabs dark:bg-coolgray-200 coolbox"
|
||||
wire:click.prevent="loadRepositories({{ $ghapp->id }})"
|
||||
wire:key="{{ $ghapp->id }}">
|
||||
<div class="flex mr-4">
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
<div x-data x-init="$wire.loadServers">
|
||||
<div x-data="searchResources()">
|
||||
@if ($current_step === 'type')
|
||||
<div x-init="window.addEventListener('scroll', () => isSticky = window.pageYOffset > 100)" class="sticky z-10 top-0 py-4 backdrop-blur-sm border-b border-neutral-200 dark:border-coolgray-400">
|
||||
<div class="flex flex-col gap-4 lg:flex-row mb-4">
|
||||
<div x-init="window.addEventListener('scroll', () => isSticky = window.pageYOffset > 100)"
|
||||
class="sticky z-10 top-0 backdrop-blur-sm border-b border-neutral-200 dark:border-coolgray-400">
|
||||
<div class="flex flex-col gap-4 lg:flex-row">
|
||||
<h1>New Resource</h1>
|
||||
<div class="w-full lg:w-96">
|
||||
<x-forms.select wire:model.live="selectedEnvironment">
|
||||
|
|
@ -23,25 +24,34 @@
|
|||
<div x-show="loading || categories.length === 0"
|
||||
class="flex items-center justify-between gap-2 py-1.5 px-3 w-64 text-sm rounded-sm border-0 ring-2 ring-inset ring-neutral-200 dark:ring-coolgray-300 bg-neutral-100 dark:bg-coolgray-200 cursor-not-allowed whitespace-nowrap opacity-50">
|
||||
<span class="text-sm text-neutral-400 dark:text-neutral-600">Filter by category</span>
|
||||
<svg class="w-4 h-4 text-neutral-400 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||||
<svg class="w-4 h-4 text-neutral-400 shrink-0" fill="none" stroke="currentColor"
|
||||
viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- Active State -->
|
||||
<div x-show="!loading && categories.length > 0"
|
||||
@click="openCategoryDropdown = !openCategoryDropdown; $nextTick(() => { if (openCategoryDropdown) $refs.categorySearchInput.focus() })"
|
||||
class="flex items-center justify-between gap-2 py-1.5 px-3 w-64 text-sm rounded-sm border-0 ring-2 ring-inset ring-neutral-200 dark:ring-coolgray-300 bg-white dark:bg-coolgray-100 cursor-pointer hover:ring-coolgray-400 transition-all whitespace-nowrap">
|
||||
<span class="text-sm truncate" x-text="selectedCategory === '' ? 'Filter by category' : selectedCategory" :class="selectedCategory === '' ? 'text-neutral-400 dark:text-neutral-600' : 'capitalize text-black dark:text-white'"></span>
|
||||
<svg class="w-4 h-4 transition-transform text-neutral-400 shrink-0" :class="{ 'rotate-180': openCategoryDropdown }" fill="none"
|
||||
stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||||
<span class="text-sm truncate"
|
||||
x-text="selectedCategory === '' ? 'Filter by category' : selectedCategory"
|
||||
:class="selectedCategory === '' ? 'text-neutral-400 dark:text-neutral-600' :
|
||||
'capitalize text-black dark:text-white'"></span>
|
||||
<svg class="w-4 h-4 transition-transform text-neutral-400 shrink-0"
|
||||
:class="{ 'rotate-180': openCategoryDropdown }" fill="none" stroke="currentColor"
|
||||
viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- Dropdown Menu -->
|
||||
<div x-show="openCategoryDropdown" x-transition
|
||||
class="absolute z-50 w-full mt-1 bg-white dark:bg-coolgray-100 border border-neutral-300 dark:border-coolgray-400 rounded shadow-lg overflow-hidden">
|
||||
<div class="sticky top-0 p-2 bg-white dark:bg-coolgray-100 border-b border-neutral-300 dark:border-coolgray-400">
|
||||
<input type="text" x-ref="categorySearchInput" x-model="categorySearch" placeholder="Search categories..."
|
||||
<div
|
||||
class="sticky top-0 p-2 bg-white dark:bg-coolgray-100 border-b border-neutral-300 dark:border-coolgray-400">
|
||||
<input type="text" x-ref="categorySearchInput" x-model="categorySearch"
|
||||
placeholder="Search categories..."
|
||||
class="w-full px-2 py-1 text-sm rounded border border-neutral-300 dark:border-coolgray-400 bg-white dark:bg-coolgray-200 focus:outline-none focus:ring-2 focus:ring-coolgray-400"
|
||||
@click.stop>
|
||||
</div>
|
||||
|
|
@ -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 === '' }">
|
||||
<span class="text-sm">All Categories</span>
|
||||
</div>
|
||||
<template x-for="category in categories.filter(cat => categorySearch === '' || cat.toLowerCase().includes(categorySearch.toLowerCase()))" :key="category">
|
||||
<template
|
||||
x-for="category in categories.filter(cat => categorySearch === '' || cat.toLowerCase().includes(categorySearch.toLowerCase()))"
|
||||
:key="category">
|
||||
<div @click="selectedCategory = category; categorySearch = ''; openCategoryDropdown = false"
|
||||
class="px-3 py-2 cursor-pointer hover:bg-neutral-100 dark:hover:bg-coolgray-200 capitalize"
|
||||
:class="{ 'bg-neutral-50 dark:bg-coolgray-300': selectedCategory === category }">
|
||||
|
|
@ -66,7 +78,8 @@ class="px-3 py-2 cursor-pointer hover:bg-neutral-100 dark:hover:bg-coolgray-200
|
|||
<div x-show="loading">Loading...</div>
|
||||
<div x-show="!loading" class="flex flex-col gap-4 py-4">
|
||||
<h2 x-show="filteredGitBasedApplications.length > 0">Applications</h2>
|
||||
<div x-show="filteredGitBasedApplications.length > 0 || filteredDockerBasedApplications.length > 0" class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
<div x-show="filteredGitBasedApplications.length > 0 || filteredDockerBasedApplications.length > 0"
|
||||
class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
<div x-show="filteredGitBasedApplications.length > 0" class="space-y-4">
|
||||
<h4>Git Based</h4>
|
||||
<div class="grid justify-start grid-cols-1 gap-4 text-left">
|
||||
|
|
@ -75,13 +88,13 @@ class="px-3 py-2 cursor-pointer hover:bg-neutral-100 dark:hover:bg-coolgray-200
|
|||
:class="{ 'cursor-pointer': !selecting, 'cursor-not-allowed opacity-50': selecting }">
|
||||
<x-resource-view>
|
||||
<x-slot:title><span x-text="application.name"></span></x-slot>
|
||||
<x-slot:description>
|
||||
<span x-html="window.sanitizeHTML(application.description)"></span>
|
||||
</x-slot>
|
||||
<x-slot:logo>
|
||||
<img class="w-full h-full p-2 transition-all duration-200 dark:bg-white/10 bg-black/10 object-contain"
|
||||
:src="application.logo">
|
||||
</x-slot:logo>
|
||||
<x-slot:description>
|
||||
<span x-html="window.sanitizeHTML(application.description)"></span>
|
||||
</x-slot>
|
||||
<x-slot:logo>
|
||||
<img class="w-full h-full p-2 transition-all duration-200 dark:bg-white/10 bg-black/10 object-contain"
|
||||
:src="application.logo">
|
||||
</x-slot:logo>
|
||||
</x-resource-view>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -95,10 +108,10 @@ class="px-3 py-2 cursor-pointer hover:bg-neutral-100 dark:hover:bg-coolgray-200
|
|||
:class="{ 'cursor-pointer': !selecting, 'cursor-not-allowed opacity-50': selecting }">
|
||||
<x-resource-view>
|
||||
<x-slot:title><span x-text="application.name"></span></x-slot>
|
||||
<x-slot:description><span x-text="application.description"></span></x-slot>
|
||||
<x-slot:logo> <img
|
||||
class="w-full h-full p-2 transition-all duration-200 dark:bg-white/10 bg-black/10 object-contain"
|
||||
:src="application.logo"></x-slot>
|
||||
<x-slot:description><span x-text="application.description"></span></x-slot>
|
||||
<x-slot:logo> <img
|
||||
class="w-full h-full p-2 transition-all duration-200 dark:bg-white/10 bg-black/10 object-contain"
|
||||
:src="application.logo"></x-slot>
|
||||
</x-resource-view>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -107,23 +120,23 @@ class="w-full h-full p-2 transition-all duration-200 dark:bg-white/10 bg-black/1
|
|||
</div>
|
||||
<div x-show="filteredDatabases.length > 0" class="mt-8">
|
||||
<h2 class="mb-4">Databases</h2>
|
||||
<div x-show="filteredDatabases.length > 0"
|
||||
class="grid justify-start grid-cols-1 gap-4 text-left xl:grid-cols-3">
|
||||
<template x-for="database in filteredDatabases" :key="database.id">
|
||||
<div x-on:click="setType(database.id)"
|
||||
:class="{ 'cursor-pointer': !selecting, 'cursor-not-allowed opacity-50': selecting }">
|
||||
<x-resource-view>
|
||||
<x-slot:title><span x-text="database.name"></span></x-slot>
|
||||
<div x-show="filteredDatabases.length > 0"
|
||||
class="grid justify-start grid-cols-1 gap-4 text-left xl:grid-cols-3">
|
||||
<template x-for="database in filteredDatabases" :key="database.id">
|
||||
<div x-on:click="setType(database.id)"
|
||||
:class="{ 'cursor-pointer': !selecting, 'cursor-not-allowed opacity-50': selecting }">
|
||||
<x-resource-view>
|
||||
<x-slot:title><span x-text="database.name"></span></x-slot>
|
||||
<x-slot:description><span x-text="database.description"></span></x-slot>
|
||||
<x-slot:logo>
|
||||
<span x-show="database.logo">
|
||||
<span x-html="database.logo"></span>
|
||||
</span>
|
||||
</x-slot>
|
||||
</x-resource-view>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<x-slot:logo>
|
||||
<span x-show="database.logo">
|
||||
<span x-html="database.logo"></span>
|
||||
</span>
|
||||
</x-slot>
|
||||
</x-resource-view>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div x-show="filteredServices.length > 0" class="mt-8">
|
||||
<div class="flex items-center gap-4" x-init="loadResources">
|
||||
|
|
@ -131,46 +144,49 @@ class="grid justify-start grid-cols-1 gap-4 text-left xl:grid-cols-3">
|
|||
<x-forms.button x-on:click="loadResources">Reload List</x-forms.button>
|
||||
</div>
|
||||
<x-callout type="info" title="Trademarks Policy" class="mt-4 mb-6">
|
||||
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.
|
||||
</x-callout>
|
||||
|
||||
<div class="grid justify-start grid-cols-1 gap-4 text-left xl:grid-cols-3">
|
||||
<template x-for="service in filteredServices" :key="service.name">
|
||||
<div x-on:click="setType('one-click-service-' + service.name)"
|
||||
<div class="relative" x-on:click="setType('one-click-service-' + service.name)"
|
||||
:class="{ 'cursor-pointer': !selecting, 'cursor-not-allowed opacity-50': selecting }">
|
||||
<x-resource-view>
|
||||
<x-slot:title>
|
||||
<template x-if="service.name">
|
||||
<span x-text="service.name"></span>
|
||||
</template>
|
||||
</x-slot>
|
||||
<x-slot:description>
|
||||
<template x-if="service.slogan">
|
||||
<span x-text="service.slogan"></span>
|
||||
</template>
|
||||
</x-slot>
|
||||
<x-slot:logo>
|
||||
<template x-if="service.logo">
|
||||
<img class="w-full h-full p-2 transition-all duration-200 dark:bg-white/10 bg-black/10 object-contain"
|
||||
:src='service.logo'
|
||||
x-on:error.window="$event.target.src = service.logo_github_url"
|
||||
onerror="this.onerror=null; this.src=this.getAttribute('data-fallback');"
|
||||
x-on:error="$event.target.src = '/coolify-logo.svg'"
|
||||
:data-fallback='service.logo_github_url' />
|
||||
</template>
|
||||
</x-slot:logo>
|
||||
<x-slot:documentation>
|
||||
<template x-if="service.documentation">
|
||||
<div class="flex items-center px-2" title="Read the documentation.">
|
||||
<a class="p-2 rounded-sm hover:bg-gray-100 dark:hover:bg-coolgray-200 hover:no-underline dark:group-hover:text-white text-neutral-600"
|
||||
onclick="event.stopPropagation()" :href="service.documentation"
|
||||
target="_blank">
|
||||
Docs
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
</x-slot:documentation>
|
||||
</x-slot>
|
||||
<x-slot:description>
|
||||
<template x-if="service.slogan">
|
||||
<span x-text="service.slogan"></span>
|
||||
</template>
|
||||
</x-slot>
|
||||
<x-slot:logo>
|
||||
<template x-if="service.logo">
|
||||
<img class="w-full h-full p-2 transition-all duration-200 dark:bg-white/10 bg-black/10 object-contain"
|
||||
:src='service.logo'
|
||||
x-on:error.window="$event.target.src = service.logo_github_url"
|
||||
onerror="this.onerror=null; this.src=this.getAttribute('data-fallback');"
|
||||
x-on:error="$event.target.src = '/coolify-logo.svg'"
|
||||
:data-fallback='service.logo_github_url' />
|
||||
</template>
|
||||
</x-slot:logo>
|
||||
</x-resource-view>
|
||||
<template x-if="shouldShowDocIcon(service)">
|
||||
<a :href="getDocLink(service) || coolifyDocsUrl(service.name)" target="_blank"
|
||||
@click.stop @mouseenter="resolveDocLink(service)"
|
||||
class="absolute top-2 right-2 p-1.5 rounded hover:bg-neutral-200 dark:hover:bg-coolgray-300 transition-colors"
|
||||
:class="{ 'opacity-50': docCheckInProgress[service.name] }"
|
||||
title="View documentation">
|
||||
<svg class="w-4 h-4 text-neutral-600 dark:text-neutral-400" fill="none"
|
||||
stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
</a>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
|
@ -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() {
|
|||
</a> </div>
|
||||
@else
|
||||
@forelse($servers as $server)
|
||||
<div class="w-full box group" wire:click="setServer({{ $server }})">
|
||||
<div class="w-full coolbox group" wire:click="setServer({{ $server }})">
|
||||
<div class="flex flex-col mx-6">
|
||||
<div class="box-title">
|
||||
{{ $server->name }}
|
||||
|
|
@ -310,7 +404,8 @@ function searchResources() {
|
|||
@empty
|
||||
<div>
|
||||
|
||||
<div>No validated & reachable servers found. <a class="underline dark:text-white" href="/servers">
|
||||
<div>No validated & reachable servers found. <a class="underline dark:text-white"
|
||||
href="/servers">
|
||||
Go to servers page
|
||||
</a></div>
|
||||
</div>
|
||||
|
|
@ -326,7 +421,7 @@ function searchResources() {
|
|||
<div class="flex flex-col justify-center gap-4 text-left xl:flex-row xl:flex-wrap">
|
||||
@if ($server->isSwarm())
|
||||
@foreach ($swarmDockers as $swarmDocker)
|
||||
<div class="w-full box group" wire:click="setDestination('{{ $swarmDocker->uuid }}')">
|
||||
<div class="w-full coolbox group" wire:click="setDestination('{{ $swarmDocker->uuid }}')">
|
||||
<div class="flex flex-col mx-6">
|
||||
<div class="font-bold dark:group-hover:text-white">
|
||||
Swarm Docker <span class="text-xs">({{ $swarmDocker->name }})</span>
|
||||
|
|
@ -336,7 +431,7 @@ function searchResources() {
|
|||
@endforeach
|
||||
@else
|
||||
@foreach ($standaloneDockers as $standaloneDocker)
|
||||
<div class="w-full box group" wire:click="setDestination('{{ $standaloneDocker->uuid }}')">
|
||||
<div class="w-full coolbox group" wire:click="setDestination('{{ $standaloneDocker->uuid }}')">
|
||||
<div class="flex flex-col mx-6">
|
||||
<div class="box-title">
|
||||
Standalone Docker <span class="text-xs">({{ $standaloneDocker->name }})</span>
|
||||
|
|
@ -370,7 +465,8 @@ function searchResources() {
|
|||
|
||||
<div class="flex items-center px-2" title="Read the documentation.">
|
||||
<a class="p-2 hover:underline dark:group-hover:text-white dark:text-white text-neutral-6000"
|
||||
onclick="event.stopPropagation()" href="https://hub.docker.com/_/postgres/" target="_blank">
|
||||
onclick="event.stopPropagation()" href="https://hub.docker.com/_/postgres/"
|
||||
target="_blank">
|
||||
Documentation
|
||||
</a>
|
||||
</div>
|
||||
|
|
@ -388,7 +484,8 @@ function searchResources() {
|
|||
<div class="flex-1"></div>
|
||||
<div class="flex items-center px-2" title="Read the documentation.">
|
||||
<a class="p-2 hover:underline dark:group-hover:text-white dark:text-white text-neutral-600"
|
||||
onclick="event.stopPropagation()" href="https://github.com/supabase/postgres" target="_blank">
|
||||
onclick="event.stopPropagation()" href="https://github.com/supabase/postgres"
|
||||
target="_blank">
|
||||
Documentation
|
||||
</a>
|
||||
</div>
|
||||
|
|
@ -426,7 +523,8 @@ function searchResources() {
|
|||
|
||||
<div class="flex items-center px-2" title="Read the documentation.">
|
||||
<a class="p-2 hover:underline dark:group-hover:text-white dark:text-white text-neutral-600"
|
||||
onclick="event.stopPropagation()" href="https://github.com/pgvector/pgvector" target="_blank">
|
||||
onclick="event.stopPropagation()" href="https://github.com/pgvector/pgvector"
|
||||
target="_blank">
|
||||
Documentation
|
||||
</a>
|
||||
</div>
|
||||
|
|
@ -441,4 +539,4 @@ function searchResources() {
|
|||
<x-forms.button type="submit">Add Database</x-forms.button>
|
||||
</form>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ class="button">+
|
|||
@if ($environment->isEmpty())
|
||||
@can('createAnyResource')
|
||||
<a href="{{ route('project.resource.create', ['project_uuid' => data_get($parameters, 'project_uuid'), 'environment_uuid' => data_get($environment, 'uuid')]) }}"
|
||||
class="items-center justify-center box">+ Add Resource</a>
|
||||
class="items-center justify-center coolbox">+ Add Resource</a>
|
||||
@else
|
||||
<div
|
||||
class="flex flex-col items-center justify-center p-8 text-center border border-dashed border-neutral-300 dark:border-coolgray-300 rounded-lg">
|
||||
|
|
@ -94,7 +94,7 @@ class="font-semibold" x-text="search"></span>".</p>
|
|||
class="grid grid-cols-1 gap-4 pt-4 lg:grid-cols-2 xl:grid-cols-3">
|
||||
<template x-for="item in filteredApplications" :key="item.uuid">
|
||||
<span>
|
||||
<a class="h-24 box group" :href="item.hrefLink">
|
||||
<a class="h-24 coolbox group" :href="item.hrefLink">
|
||||
<div class="flex flex-col w-full">
|
||||
<div class="flex gap-2 px-4">
|
||||
<div class="pb-2 truncate box-title" x-text="item.name"></div>
|
||||
|
|
@ -143,7 +143,7 @@ class="flex flex-wrap gap-1 pt-1 dark:group-hover:text-white group-hover:text-bl
|
|||
class="grid grid-cols-1 gap-4 pt-4 lg:grid-cols-2 xl:grid-cols-3">
|
||||
<template x-for="item in filteredDatabases" :key="item.uuid">
|
||||
<span>
|
||||
<a class="h-24 box group" :href="item.hrefLink">
|
||||
<a class="h-24 coolbox group" :href="item.hrefLink">
|
||||
<div class="flex flex-col w-full">
|
||||
<div class="flex gap-2 px-4">
|
||||
<div class="pb-2 truncate box-title" x-text="item.name"></div>
|
||||
|
|
@ -192,7 +192,7 @@ class="flex flex-wrap gap-1 pt-1 dark:group-hover:text-white group-hover:text-bl
|
|||
class="grid grid-cols-1 gap-4 pt-4 lg:grid-cols-2 xl:grid-cols-3">
|
||||
<template x-for="item in filteredServices" :key="item.uuid">
|
||||
<span>
|
||||
<a class="h-24 box group" :href="item.hrefLink">
|
||||
<a class="h-24 coolbox group" :href="item.hrefLink">
|
||||
<div class="flex flex-col w-full">
|
||||
<div class="flex gap-2 px-4">
|
||||
<div class="pb-2 truncate box-title" x-text="item.name"></div>
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ class="absolute bg-error -top-1 -left-1 badge "></div>
|
|||
<div class="grid grid-cols-1 gap-4">
|
||||
@foreach ($networks as $network)
|
||||
<div wire:click="addServer('{{ $network->id }}','{{ data_get($network, 'server.id') }}')"
|
||||
class="relative flex flex-col dark:text-white box group">
|
||||
class="relative flex flex-col dark:text-white coolbox group">
|
||||
<div>
|
||||
<div class="box-title">
|
||||
Server: {{ data_get($network, 'server.name') }}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
<div class="flex flex-col flex-wrap gap-2 pt-4">
|
||||
@forelse($resource->scheduled_tasks as $task)
|
||||
@if ($resource->type() == 'application')
|
||||
<a class="box"
|
||||
<a class="coolbox"
|
||||
href="{{ route('project.application.scheduled-tasks', [...$parameters, 'task_uuid' => $task->uuid]) }}">
|
||||
<span class="flex flex-col">
|
||||
<span class="text-lg font-bold">{{ $task->name }}
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
</span>
|
||||
</a>
|
||||
@elseif ($resource->type() == 'service')
|
||||
<a class="box"
|
||||
<a class="coolbox"
|
||||
href="{{ route('project.service.scheduled-tasks', [...$parameters, 'task_uuid' => $task->uuid]) }}">
|
||||
<span class="flex flex-col">
|
||||
<span class="text-lg font-bold">{{ $task->name }}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
<div class="text-xs truncate subtitle lg:text-sm">{{ $project->name }}.</div>
|
||||
<div class="grid gap-2 lg:grid-cols-2">
|
||||
@forelse ($project->environments->sortBy('created_at') as $environment)
|
||||
<div class="gap-2 box group">
|
||||
<div class="gap-2 coolbox group">
|
||||
<div class="flex flex-1 mx-6">
|
||||
<a class="flex flex-col justify-center flex-1"
|
||||
href="{{ route('project.resource.index', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid]) }}">
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
@forelse ($privateKeys as $key)
|
||||
@can('view', $key)
|
||||
{{-- Admin/Owner: Clickable link --}}
|
||||
<a class="box group"
|
||||
<a class="coolbox group"
|
||||
href="{{ route('security.private-key.show', ['private_key_uuid' => data_get($key, 'uuid')]) }}">
|
||||
<div class="flex flex-col justify-center mx-6">
|
||||
<div class="box-title">
|
||||
|
|
@ -33,7 +33,7 @@ class="inline-flex items-center px-2 py-0.5 rounded-sm text-xs font-medium bg-ye
|
|||
</a>
|
||||
@else
|
||||
{{-- Member: Visible but not clickable --}}
|
||||
<div class="box opacity-60 cursor-not-allowed hover:bg-transparent dark:hover:bg-transparent" title="You don't have permission to view this private key">
|
||||
<div class="coolbox opacity-60 !cursor-not-allowed hover:bg-transparent dark:hover:bg-transparent" title="You don't have permission to view this private key">
|
||||
<div class="flex flex-col justify-center mx-6">
|
||||
<div class="box-title">
|
||||
{{ data_get($key, 'name') }}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<div>
|
||||
<x-modal-input title="Connect a Hetzner Server">
|
||||
<x-slot:content>
|
||||
<div class="relative gap-2 cursor-pointer box group">
|
||||
<div class="relative gap-2 cursor-pointer coolbox group">
|
||||
<div class="flex items-center gap-4 mx-6">
|
||||
<svg class="w-10 h-10 flex-shrink-0" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="200" height="200" fill="#D50C2D" rx="8" />
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
@forelse ($servers as $server)
|
||||
<a href="{{ route('server.show', ['server_uuid' => data_get($server, 'uuid')]) }}"
|
||||
@class([
|
||||
'gap-2 border cursor-pointer box group',
|
||||
'gap-2 border cursor-pointer coolbox group',
|
||||
'border-red-500' =>
|
||||
!$server->settings->is_reachable || $server->settings->force_disabled,
|
||||
])>
|
||||
|
|
|
|||
|
|
@ -170,13 +170,13 @@ class="underline text-white">Traefik changelog</a> to understand breaking change
|
|||
<div class="subtitle">Select a proxy you would like to use on this server.</div>
|
||||
@can('update', $server)
|
||||
<div class="grid gap-4">
|
||||
<x-forms.button class="box" wire:click="selectProxy('NONE')">
|
||||
<x-forms.button class="coolbox" wire:click="selectProxy('NONE')">
|
||||
Custom (None)
|
||||
</x-forms.button>
|
||||
<x-forms.button class="box" wire:click="selectProxy('TRAEFIK')">
|
||||
<x-forms.button class="coolbox" wire:click="selectProxy('TRAEFIK')">
|
||||
Traefik
|
||||
</x-forms.button>
|
||||
<x-forms.button class="box" wire:click="selectProxy('CADDY')">
|
||||
<x-forms.button class="coolbox" wire:click="selectProxy('CADDY')">
|
||||
Caddy
|
||||
</x-forms.button>
|
||||
{{-- <x-forms.button disabled class="box">
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
<h2>Project: {{ data_get($project, 'name') }}</h2>
|
||||
<div class="pt-0 pb-3">{{ data_get($project, 'description') }}</div>
|
||||
@forelse ($project->environments as $environment)
|
||||
<a class="box group"
|
||||
<a class="coolbox group"
|
||||
href="{{ route('shared-variables.environment.show', [
|
||||
'project_uuid' => $project->uuid,
|
||||
'environment_uuid' => $environment->uuid,
|
||||
|
|
|
|||
|
|
@ -8,19 +8,19 @@
|
|||
<div class="subtitle">Set Team / Project / Environment wide variables.</div>
|
||||
|
||||
<div class="flex flex-col gap-2 -mt-1">
|
||||
<a class="box group" href="{{ route('shared-variables.team.index') }}">
|
||||
<a class="coolbox group" href="{{ route('shared-variables.team.index') }}">
|
||||
<div class="flex flex-col justify-center mx-6">
|
||||
<div class="box-title">Team wide</div>
|
||||
<div class="box-description">Usable for all resources in a team.</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="box group" href="{{ route('shared-variables.project.index') }}">
|
||||
<a class="coolbox group" href="{{ route('shared-variables.project.index') }}">
|
||||
<div class="flex flex-col justify-center mx-6">
|
||||
<div class="box-title">Project wide</div>
|
||||
<div class="box-description">Usable for all resources in a project.</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="box group" href="{{ route('shared-variables.environment.index') }}">
|
||||
<a class="coolbox group" href="{{ route('shared-variables.environment.index') }}">
|
||||
<div class="flex flex-col justify-center mx-6">
|
||||
<div class="box-title">Environment wide</div>
|
||||
<div class="box-description">Usable for all resources in an environment.</div>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<div class="subtitle">List of your projects.</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
@forelse ($projects as $project)
|
||||
<a class="box group"
|
||||
<a class="coolbox group"
|
||||
href="{{ route('shared-variables.project.show', ['project_uuid' => data_get($project, 'uuid')]) }}">
|
||||
<div class="flex flex-col justify-center mx-6 ">
|
||||
<div class="box-title">{{ $project->name }}</div>
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
</svg>
|
||||
<span>You must complete this step before you can use this source!</span>
|
||||
</div>
|
||||
<a class="items-center justify-center box" href="{{ getInstallationPath($github_app) }}">
|
||||
<a class="items-center justify-center coolbox" href="{{ getInstallationPath($github_app) }}">
|
||||
Install Repositories on GitHub
|
||||
</a>
|
||||
@else
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<div class="subtitle">S3 storages for backups.</div>
|
||||
<div class="grid gap-4 lg:grid-cols-2 -mt-1">
|
||||
@forelse ($s3 as $storage)
|
||||
<a href="/storages/{{ $storage->uuid }}" @class(['gap-2 border cursor-pointer box group'])>
|
||||
<a href="/storages/{{ $storage->uuid }}" @class(['gap-2 border cursor-pointer coolbox group'])>
|
||||
<div class="flex flex-col justify-center mx-6">
|
||||
<div class="box-title">
|
||||
{{ $storage->name }}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<div class="flex flex-wrap gap-2 ">
|
||||
@forelse ($tags as $oneTag)
|
||||
<a :class="{{ $tag?->id == $oneTag->id }} && 'dark:bg-coollabs'"
|
||||
class="min-w-32 box-without-bg dark:bg-coolgray-100 dark:text-white font-bold dark:hover:bg-coollabs-100 flex justify-center items-center"
|
||||
class="min-w-32 coolbox dark:text-white font-bold flex justify-center items-center"
|
||||
href="{{ route('tags.show', ['tagName' => $oneTag->name]) }}">{{ data_get_str($oneTag, 'name')->limit(30) }}</a>
|
||||
@empty
|
||||
<div>No tags yet defined yet. Go to a resource and add a tag there.</div>
|
||||
|
|
@ -34,7 +34,7 @@ class="min-w-32 box-without-bg dark:bg-coolgray-100 dark:text-white font-bold da
|
|||
<div class="grid grid-cols-1 gap-2 pt-4 lg:grid-cols-2 xl:grid-cols-3">
|
||||
@if (isset($applications) && count($applications) > 0)
|
||||
@foreach ($applications as $application)
|
||||
<a href="{{ $application->link() }}" class="box group">
|
||||
<a href="{{ $application->link() }}" class="coolbox group">
|
||||
<div class="flex flex-col justify-center">
|
||||
<div class="box-title">
|
||||
{{ $application->project()->name }}/{{ $application->environment->name }}
|
||||
|
|
@ -47,7 +47,7 @@ class="min-w-32 box-without-bg dark:bg-coolgray-100 dark:text-white font-bold da
|
|||
@endif
|
||||
@if (isset($services) && count($services) > 0)
|
||||
@foreach ($services as $service)
|
||||
<a href="{{ $service->link() }}" class="flex flex-col box group">
|
||||
<a href="{{ $service->link() }}" class="flex flex-col coolbox group">
|
||||
<div class="flex flex-col">
|
||||
<div class="box-title">
|
||||
{{ $service->project()->name }}/{{ $service->environment->name }}
|
||||
|
|
@ -71,7 +71,7 @@ class="min-w-32 box-without-bg dark:bg-coolgray-100 dark:text-white font-bold da
|
|||
<div class="grid grid-cols-1 gap-2 lg:grid-cols-3">
|
||||
@foreach ($deployments as $deployment)
|
||||
<a href="{{ data_get($deployment, 'deployment_url') }}" @class([
|
||||
'gap-2 cursor-pointer box group border-l-2 border-dotted',
|
||||
'gap-2 cursor-pointer coolbox group border-l-2 border-dotted',
|
||||
'dark:border-coolgray-300' => data_get($deployment, 'status') === 'queued',
|
||||
'border-yellow-500' => data_get($deployment, 'status') === 'in_progress',
|
||||
])>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
<div class="grid gap-4 lg:grid-cols-2 -mt-1">
|
||||
@forelse ($sources as $source)
|
||||
@if ($source->getMorphClass() === 'App\Models\GithubApp')
|
||||
<a class="flex gap-2 text-center hover:no-underline box group"
|
||||
<a class="flex gap-2 text-center hover:no-underline coolbox group"
|
||||
href="{{ route('source.github.show', ['github_app_uuid' => data_get($source, 'uuid')]) }}">
|
||||
{{-- <x-git-icon class="dark:text-white w-8 h-8 mt-1" git="{{ $source->getMorphClass() }}" /> --}}
|
||||
<div class="text-left dark:group-hover:text-white flex flex-col justify-center mx-6">
|
||||
|
|
|
|||
Loading…
Reference in a new issue