fix(services): preserve template keys for selection

This commit is contained in:
Andras Bacsai 2026-07-02 17:16:40 +02:00
parent ec34c5192f
commit d5395f0500
3 changed files with 43 additions and 8 deletions

View file

@ -112,14 +112,17 @@ public function loadServices()
$default_logo = 'images/default.webp';
$logo = data_get($service, 'logo', $default_logo);
$local_logo_path = public_path($logo);
$serviceKey = (string) $key;
return [
'name' => str($key)->headline(),
'id' => $serviceKey,
'name' => str($serviceKey)->headline(),
'docsSlug' => str($serviceKey)->lower()->value(),
'logo' => asset($logo),
'logo_github_url' => file_exists($local_logo_path)
? 'https://raw.githubusercontent.com/coollabsio/coolify/refs/heads/main/public/'.$logo
: asset($default_logo),
'templateLastUpdated' => $templateLastUpdatedMap[(string) $key] ?? null,
'templateLastUpdated' => $templateLastUpdatedMap[$serviceKey] ?? null,
] + (array) $service;
})->all();
@ -336,7 +339,10 @@ private function formatLastModified(string $path): ?string
public function setType(string $type)
{
$type = str($type)->lower()->slug()->value();
if (! str($type)->startsWith('one-click-service-')) {
$type = str($type)->lower()->slug()->value();
}
if ($this->loading) {
return;
}

View file

@ -154,7 +154,7 @@ class="text-xs text-neutral-500 dark:text-neutral-400">
<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 class="relative" x-on:click="setType('one-click-service-' + service.name)"
<div class="relative" x-on:click="setType('one-click-service-' + service.id)"
:class="{ 'cursor-pointer': !selecting, 'cursor-not-allowed opacity-50': selecting }">
<x-resource-view>
<x-slot:title>
@ -214,7 +214,7 @@ class="px-2 py-0.5 text-xs rounded-full bg-amber-100 text-amber-800 dark:bg-ambe
</div>
</template>
<template x-if="shouldShowDocIcon(service)">
<a :href="getDocLink(service) || coolifyDocsUrl(service.name)" target="_blank"
<a :href="getDocLink(service) || coolifyDocsUrl(service)" 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] }"
@ -287,8 +287,8 @@ function searchResources() {
// Remove flavor suffixes: -with-*, -without-*
return normalized.replace(/-(with|without)-.+$/, '');
},
coolifyDocsUrl(serviceName) {
const baseName = this.extractBaseServiceName(serviceName);
coolifyDocsUrl(service) {
const baseName = service.docsSlug || this.extractBaseServiceName(service.name);
return 'https://coolify.io/docs/services/' + baseName;
},
officialDocsUrl(service) {
@ -322,7 +322,7 @@ function searchResources() {
this.docCheckInProgress[serviceName] = true;
// 1. Try Coolify docs first
const coolifyUrl = this.coolifyDocsUrl(serviceName);
const coolifyUrl = this.coolifyDocsUrl(service);
const coolifyExists = await this.checkUrlExists(coolifyUrl);
if (coolifyExists) {

View file

@ -87,3 +87,32 @@
$view->assertSee('serviceTemplatesLastUpdated');
$view->assertSee('service.templateLastUpdated');
});
it('keeps service template keys for service selection and docs links', function () {
$services = collect((new Select)->loadServices()['services']);
$denoKv = $services->firstWhere('id', 'denoKV');
expect($denoKv)
->not->toBeNull()
->and($denoKv['docsSlug'])->toBe('denokv');
View::share('errors', new ViewErrorBag);
$view = $this->view('livewire.project.new.select', [
'current_step' => 'type',
'environments' => collect(),
]);
$view->assertSee("setType('one-click-service-' + service.id)", false);
$view->assertSee('service.docsSlug || this.extractBaseServiceName(service.name)', false);
});
it('preserves one click service key casing when selecting a service template', function () {
$component = new Select;
$component->servers = collect();
$component->allServers = collect();
$component->setType('one-click-service-denoKV');
expect($component->type)->toBe('one-click-service-denoKV');
});