feat(ui): unify brand accents and polish shared controls

Standardize highlighted buttons, selected controls, and loading
indicators on coollabs accent colors; improve light-mode log toolbar
styling; enhance small popups with compact/icon modes; and align
navbars, helpers, and related layouts with matching tests.
This commit is contained in:
Andras Bacsai
2026-08-05 22:34:56 +02:00
parent 8e57a9ab6b
commit bdb367555d
81 changed files with 423 additions and 149 deletions
+1 -1
View File
@@ -118,7 +118,7 @@ return [
'navigate' => [
'show_progress_bar' => true,
'progress_bar_color' => '#ffff00',
'progress_bar_color' => '#6b16ed',
],
/*
+28 -8
View File
@@ -268,7 +268,7 @@ button[isError]:not(:disabled) {
}
button[isHighlighted]:not(:disabled) {
@apply text-coollabs-200 bg-coollabs-50 border-coollabs hover:bg-coollabs hover:text-white dark:bg-warning/15 dark:text-warning dark:border-warning/25 dark:hover:bg-warning/20 dark:hover:text-warning;
@apply button-highlighted;
}
h1 {
@@ -2427,11 +2427,16 @@ input[type="search"]::-webkit-search-results-decoration {
Mobile stacks: search meta full-width actions (no side-by-side overlap). */
.logs-viewer-toolbar {
flex-shrink: 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
background: rgba(255, 255, 255, 0.035);
border-bottom: 1px solid var(--color-neutral-200);
background: var(--color-neutral-50);
padding: 0.5rem 0.75rem;
}
.dark .logs-viewer-toolbar {
border-bottom-color: rgba(255, 255, 255, 0.08);
background: rgba(255, 255, 255, 0.035);
}
.logs-viewer-toolbar-controls {
display: flex;
min-width: 0;
@@ -2503,11 +2508,17 @@ input[type="search"]::-webkit-search-results-decoration {
.logs-viewer-status-badge {
height: 1.5rem !important;
flex-shrink: 0;
border-color: rgba(255, 255, 255, 0.1) !important;
background: rgba(255, 255, 255, 0.05) !important;
border-color: var(--color-neutral-200) !important;
background: white !important;
padding-inline: 0.5rem !important;
font-size: 0.6875rem !important;
font-weight: 500 !important;
color: var(--color-neutral-700) !important;
}
.dark .logs-viewer-status-badge {
border-color: rgba(255, 255, 255, 0.1) !important;
background: rgba(255, 255, 255, 0.05) !important;
color: #d4d4d4 !important;
}
@@ -2615,18 +2626,27 @@ input[type="search"]::-webkit-search-results-decoration {
align-items: center;
justify-content: center;
border-radius: 0.5rem;
color: #a3a3a3;
color: var(--color-neutral-500);
transition: color 0.15s, background-color 0.15s;
}
.logs-viewer-btn:hover {
background: var(--color-neutral-100);
color: var(--color-neutral-900);
}
.dark .logs-viewer-btn {
color: #a3a3a3;
}
.dark .logs-viewer-btn:hover {
background: rgba(255, 255, 255, 0.06);
color: #f5f5f5;
}
.logs-viewer-btn-active {
background: color-mix(in oklab, var(--color-warning) 14%, transparent);
color: var(--color-warning);
background: var(--color-coollabs);
color: #fff;
}
.logs-viewer-viewport {
+12
View File
@@ -130,6 +130,18 @@
@apply inline-flex shrink-0 gap-1.5 justify-center items-center whitespace-nowrap px-2.5 h-9 min-h-9 text-[13px] text-black normal-case rounded-md border outline-0 cursor-pointer font-medium transition-colors bg-white border-neutral-200 hover:bg-neutral-100 dark:bg-white/[0.06] dark:text-fg dark:hover:text-fg dark:hover:bg-white/[0.1] dark:border-white/[0.08] hover:text-black disabled:cursor-not-allowed min-w-fit dark:disabled:text-fg-faint disabled:border-neutral-200 dark:disabled:border-white/[0.06] disabled:hover:bg-transparent disabled:bg-transparent disabled:text-neutral-300 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent;
}
@utility button-highlighted {
@apply border-2 text-coollabs-200 dark:text-white bg-coollabs-50 dark:bg-coollabs/20 border-coollabs dark:border-coollabs-100 hover:bg-coollabs hover:text-white dark:hover:bg-coollabs-100 dark:hover:text-white;
}
@utility control-selected {
@apply bg-coollabs text-white dark:bg-coollabs dark:text-white;
}
@utility loading-indicator {
@apply text-coollabs dark:text-coollabs-100;
}
/* Compact icon-only control (gear, chevrons, etc.) */
@utility icon-button {
@apply inline-flex size-7 shrink-0 items-center justify-center rounded-md border border-transparent text-neutral-400 outline-0 transition-colors hover:bg-neutral-100 hover:text-black focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent disabled:pointer-events-none disabled:opacity-35 dark:text-fg-faint dark:hover:bg-white/[0.07] dark:hover:text-fg;
@@ -6,13 +6,18 @@
])
<section {{ $attributes->merge(['class' => 'application-settings-section']) }}>
<header @class(['items-start!' => filled($description ?? $helper)])>
<header>
<div class="min-w-0 py-0.5">
<h3>{{ $title }}</h3>
@if (filled($description ?? $helper))
<p class="mt-0.5 text-xs leading-4 text-neutral-500 dark:text-[var(--coollabs-subtle)]">
{!! $description ?? $helper !!}
</p>
<h3>
<x-helper :helper="$description ?? $helper" :label="'More information about '.$title">
<x-slot:trigger>
<span class="underline underline-offset-4">{{ $title }}</span>
</x-slot:trigger>
</x-helper>
</h3>
@else
<h3>{{ $title }}</h3>
@endif
</div>
@isset($actions)
@@ -27,8 +27,12 @@
<x-reicon name="chevron-down" class="size-3 opacity-55" />
</span>
</button>
<div x-show="open" x-cloak x-transition.origin.top.right
class="listbox-panel top-full! right-0! left-auto! mt-1! min-w-60! max-w-96!" role="menu">
<div x-show="open" x-cloak x-transition.origin.top.right role="menu"
@class([
'listbox-panel top-full! mt-1!',
'left-0! right-0! w-full! min-w-0! max-w-none!' => $fullWidth,
'right-0! left-auto! min-w-60! max-w-96!' => !$fullWidth,
])>
@if ($hasLinks)
@if (data_get($application, 'gitBrancLocation'))
<a target="_blank" class="{{ $linkItemClasses }}" href="{{ $application->gitBranchLocation }}">
@@ -25,9 +25,6 @@
<a href="{{ $item['href'] }}" {{ wireNavigate() }} @click="open = false"
class="listbox-option {{ $item['active'] ? 'bg-neutral-100 font-medium text-black dark:bg-white/[0.07] dark:text-fg' : '' }}">
<span class="min-w-0 flex-1 truncate">{{ $item['label'] }}</span>
@if ($item['active'])
<x-reicon name="check-circle" class="size-3.5 shrink-0 text-warning" />
@endif
</a>
@endforeach
</div>
@@ -79,6 +79,14 @@
$hasTitle = filled($title);
$hasActions = isset($actions);
$showNav = $showTabs || $hasActions;
$stackTabsOnMobile = $section === 'shared-variables';
$sharedVariableIcons = [
'Overview' => 'dashboard',
'Team' => 'teams',
'Projects' => 'projects',
'Environments' => 'layers',
'Servers' => 'servers',
];
@endphp
@if ($hasTitle)
@@ -123,8 +131,24 @@
<div
class="flex w-full flex-col gap-2 sm:flex-row sm:items-center sm:justify-between sm:gap-3 lg:h-full lg:gap-4">
@if ($showTabs)
<div
class="flex min-w-0 w-full items-center gap-0.5 overflow-x-auto rounded-[10px] border border-neutral-200 bg-neutral-100 p-1 sm:flex-1 dark:border-white/[0.07] dark:bg-white/[0.035]">
@if ($stackTabsOnMobile)
<div
class="grid grid-cols-2 gap-0.5 border-y border-neutral-200 py-3 sm:grid-cols-3 lg:hidden dark:border-white/[0.06]">
@foreach ($items as $item)
<a @class([
'menu-item',
'menu-item-active' => $item['active'],
]) {{ wireNavigate() }} href="{{ route($item['route'], $parameters) }}">
<x-reicon :name="$sharedVariableIcons[$item['label']]" class="menu-item-icon" />
<span class="menu-item-label">{{ $item['label'] }}</span>
</a>
@endforeach
</div>
@endif
<div @class([
'flex min-w-0 w-full items-center gap-0.5 overflow-x-auto rounded-[10px] border border-neutral-200 bg-neutral-100 p-1 sm:flex-1 dark:border-white/[0.07] dark:bg-white/[0.035]',
'hidden lg:flex' => $stackTabsOnMobile,
])>
@foreach ($items as $item)
<a @class([
'app-tab shrink-0',
+13 -3
View File
@@ -1,3 +1,8 @@
@props([
'helper',
'label' => 'More information',
])
<div x-data="{
open: false,
pinned: false,
@@ -55,10 +60,15 @@
{{ $attributes->merge(['class' => 'relative z-10 inline-block align-middle']) }}>
{{-- button (not div) so label-for associations do not steal the click on mobile --}}
<button type="button" x-ref="trigger"
class="info-helper relative z-10 inline-flex size-3.5 shrink-0 items-center justify-center border-0 bg-transparent p-0 leading-none"
aria-label="More information" @mouseenter="show(false)" @mouseleave="hide"
@class([
'info-helper relative z-10 inline-flex shrink-0 items-center justify-center border-0 bg-transparent p-0 leading-none',
'size-3.5' => ! isset($trigger),
])
aria-label="{{ $label }}" @mouseenter="show(false)" @mouseleave="hide"
@click.prevent.stop="open && pinned ? close() : show(true)">
@isset($icon)
@isset($trigger)
{{ $trigger }}
@elseif (isset($icon))
{{ $icon }}
@else
<x-reicon name="info-circle"
+1 -1
View File
@@ -2,7 +2,7 @@
<div {{ $attributes->merge(['class' => 'inline-flex items-center justify-center gap-2 text-[13px] text-neutral-500 dark:text-fg-dim']) }}
role="status" aria-live="polite">
<svg @class(['shrink-0 animate-spin', 'size-3' => $compact, 'size-4' => ! $compact]) viewBox="0 0 24 24"
<svg @class(['loading-indicator shrink-0 animate-spin', 'size-3' => $compact, 'size-4' => ! $compact]) viewBox="0 0 24 24"
fill="none" aria-hidden="true">
<circle class="opacity-20" cx="12" cy="12" r="9" stroke="currentColor" stroke-width="2" />
<path class="opacity-80" d="M21 12a9 9 0 0 0-9-9" stroke="currentColor" stroke-width="2"
@@ -3,7 +3,7 @@
@if (isset($text))
<div>{{ $text }}</div>
@endif
<svg class="w-4 h-4 mx-1 ml-3 text-coollabs dark:text-warning animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none"
<svg class="loading-indicator w-4 h-4 mx-1 ml-3 animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor"
@@ -1,14 +1,60 @@
@props(['title' => 'Default title', 'description' => 'Default description'])
@props([
'title' => 'Default title',
'description' => 'Default description',
'compactAfter' => null,
'compactStorageKey' => null,
'compactStoragePrefix' => null,
])
<div x-data="{ bannerVisible: true }" x-show="bannerVisible" x-cloak
<template x-teleport="body">
<div x-data="{
bannerVisible: true,
compact: false,
iconOnly: false,
storageKey: @js($compactStorageKey),
storagePrefix: @js($compactStoragePrefix),
init() {
if (this.storagePrefix) {
Object.keys(localStorage)
.filter(key => key.startsWith(this.storagePrefix) && key !== this.storageKey)
.forEach(key => localStorage.removeItem(key));
}
const storedState = this.storageKey ? localStorage.getItem(this.storageKey) : null;
this.iconOnly = storedState === 'icon';
this.compact = this.iconOnly || storedState === 'compact';
if (!this.compact && {{ $compactAfter ?? 'false' }}) {
setTimeout(() => {
if (this.iconOnly) return;
this.compact = true;
if (this.storageKey) localStorage.setItem(this.storageKey, 'compact');
}, {{ $compactAfter ?? 0 }});
}
},
minimizeToIcon() {
this.iconOnly = true;
this.compact = true;
if (this.storageKey) localStorage.setItem(this.storageKey, 'icon');
},
restore() {
if (!this.compact && !this.iconOnly) return;
this.iconOnly = false;
this.compact = false;
if (this.storageKey) localStorage.setItem(this.storageKey, 'compact');
},
}"
x-show="bannerVisible" x-cloak
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="translate-y-3 opacity-0"
x-transition:enter-end="translate-y-0 opacity-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="translate-y-0 opacity-100"
x-transition:leave-end="translate-y-3 opacity-0"
class="fixed bottom-4 right-4 z-999 w-[calc(100%-2rem)] max-w-sm">
class="fixed bottom-4 right-4 z-999"
:class="compact ? 'w-auto max-w-[calc(100%-2rem)]' : 'w-[calc(100%-2rem)] max-w-sm'">
<div class="relative flex items-start gap-2.5 rounded-lg p-3 pr-10"
:class="compact ? (iconOnly ? 'cursor-pointer p-2! pr-2!' : 'cursor-pointer') : ''" @click="restore()"
style="background: var(--coollabs-elevated); box-shadow: 0 0 0 1px var(--coollabs-line), var(--shadow-modal);">
@isset($icon)
<div
@@ -17,18 +63,20 @@
</div>
@endisset
<div class="min-w-0 flex-1">
<div x-show="!iconOnly" class="min-w-0 flex-1">
<h4 class="text-[13px] font-semibold leading-4 text-neutral-950 dark:text-fg">
{{ $title }}
</h4>
<div class="mt-0.5 text-[11px] leading-4 text-neutral-600 dark:text-fg-dim">
<div x-show="!compact" x-transition.opacity
class="mt-0.5 text-[11px] leading-4 text-neutral-600 dark:text-fg-dim">
{{ $description }}
</div>
</div>
<button type="button" @click="bannerVisible = false" aria-label="Dismiss"
<button x-show="!iconOnly" type="button" @click.stop="minimizeToIcon()" aria-label="Minimize warning"
class="absolute right-2 top-2 flex size-6 items-center justify-center rounded-md text-neutral-400 transition-colors hover:bg-black/5 hover:text-neutral-700 dark:text-fg-faint dark:hover:bg-white/[0.06] dark:hover:text-fg">
<x-reicon name="x" class="size-3.5" />
</button>
</div>
</div>
</template>
@@ -44,7 +44,7 @@
@endisset
@can('createAnyResource')
<a href="{{ route('project.resource.create', $environmentParameters) }}" {{ wireNavigate() }}
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button button-highlighted">
<x-reicon name="plus" class="size-3.5" />
New resource
</a>
@@ -86,7 +86,7 @@
<x-status-badge label="Match found" type="success" />
</div>
<button type="button"
class="button mt-3 bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!"
class="button mt-3 button-highlighted"
wire:click="{{ $linkMethod }}">
Link resource
</button>
@@ -14,7 +14,7 @@
<x-modal-input title="Add {{ $providerLabel }} Token">
<x-slot:content>
<button type="button"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button button-highlighted">
<x-reicon name="plus" class="size-3.5" />
Add token
</button>
@@ -16,9 +16,12 @@
<x-reicon name="chevron-down" class="size-3 opacity-55" />
</span>
</button>
<div x-show="open" x-cloak x-transition.origin.top.right
class="listbox-panel top-full! right-0! left-auto! mt-1! min-w-60! max-w-96! max-h-80! overflow-y-auto!"
role="menu">
<div x-show="open" x-cloak x-transition.origin.top.right role="menu"
@class([
'listbox-panel top-full! mt-1! max-h-80! overflow-y-auto!',
'left-0! right-0! w-full! min-w-0! max-w-none!' => $fullWidth,
'right-0! left-auto! min-w-60! max-w-96!' => !$fullWidth,
])>
@forelse ($links as $link)
<a class="{{ $linkItemClasses }}" target="_blank" href="{{ $link }}">
<span class="min-w-0 truncate">{{ $link }}</span>
@@ -30,7 +30,9 @@
}
}">
<x-dashboard.navbar section="shared-variables" title="Shared variables"
subtitle="Reusable environment variables across resources" :titleOnDesktop="false">
subtitle="Reusable environment variables across resources" :titleOnDesktop="false" />
<x-application.settings-section :title="$title" flush>
<x-slot:actions>
<x-forms.button type="button" wire:click="switch" class="whitespace-nowrap">
<x-reicon :name="$view === 'normal' ? 'browser-code' : 'unordered-list'" class="size-3.5" />
@@ -38,9 +40,6 @@
<span class="sm:hidden">{{ $view === 'normal' ? 'Developer' : 'Normal' }}</span>
</x-forms.button>
</x-slot:actions>
</x-dashboard.navbar>
<x-application.settings-section :title="$title" flush>
@if ($view === 'normal')
<div
class="flex flex-col gap-3 border-b border-neutral-200 p-3 sm:flex-row sm:items-center sm:justify-between dark:border-white/[0.08]">
@@ -89,7 +88,7 @@
<x-modal-input title="New Shared Variable">
<x-slot:content>
<button type="button"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button button-highlighted">
<x-reicon name="plus" class="size-3.5" />
Add variable
</button>
@@ -101,9 +100,11 @@
</div>
@if ($variables->isEmpty())
<x-empty title="No shared variables"
description="Add a variable to make it available to resources in this scope."
icon-name="variables" size="sm" />
<div class="p-3">
<x-empty title="No shared variables"
description="Add a variable to make it available to resources in this scope."
icon-name="variables" size="sm" />
</div>
@else
<div class="data-table flex w-full flex-col">
<div class="data-table-header env-table-grid-shared order-[-1]">
@@ -52,7 +52,7 @@
<span wire:loading.remove wire:target="{{ $wireTarget }}">{{ $currentPage }}</span>
<span wire:loading.inline-flex wire:target="{{ $wireTarget }}" class="inline-flex items-center"
aria-live="polite">
<svg class="size-3.5 animate-spin text-coollabs dark:text-warning"
<svg class="loading-indicator size-3.5 animate-spin"
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" aria-hidden="true">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor"
stroke-width="4"></circle>
@@ -18,7 +18,7 @@
<x-modal-input title="New Team">
<x-slot:content>
<button type="button"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button button-highlighted">
<x-reicon name="plus" class="size-3.5" />
New team
</button>
@@ -106,9 +106,6 @@
<a href="{{ $destination['href'] }}" {{ wireNavigate() }} @click="open = false"
class="listbox-option {{ $destination['label'] === $dashboardContext ? 'bg-neutral-100 font-medium text-black dark:bg-white/[0.07] dark:text-fg' : '' }}">
<span class="min-w-0 flex-1 truncate">{{ $destination['label'] }}</span>
@if ($destination['label'] === $dashboardContext)
<x-reicon name="check-circle" class="size-3.5 shrink-0 text-warning" />
@endif
</a>
@endforeach
</div>
@@ -214,9 +211,6 @@
<a href="{{ route($projectDestinationRoute, ['project_uuid' => $p->uuid]) }}" {{ wireNavigate() }} @click="open = false"
class="listbox-option {{ $p->uuid === $currentProject->uuid ? 'bg-neutral-100 font-medium text-black dark:bg-white/[0.07] dark:text-fg' : '' }}">
<span class="min-w-0 flex-1 truncate">{{ $p->name }}</span>
@if ($p->uuid === $currentProject->uuid)
<x-reicon name="check-circle" class="size-3.5 shrink-0 text-warning" />
@endif
</a>
@endforeach
</div>
@@ -243,9 +237,6 @@
<a href="{{ route('project.resource.index', ['project_uuid' => $currentProject->uuid, 'environment_uuid' => $env->uuid]) }}" {{ wireNavigate() }} @click="open = false"
class="listbox-option {{ $env->uuid === $currentEnvironment->uuid ? 'bg-neutral-100 font-medium text-black dark:bg-white/[0.07] dark:text-fg' : '' }}">
<span class="min-w-0 flex-1 truncate">{{ $env->name }}</span>
@if ($env->uuid === $currentEnvironment->uuid)
<x-reicon name="check-circle" class="size-3.5 shrink-0 text-warning" />
@endif
</a>
@endforeach
</div>
@@ -22,7 +22,7 @@
<div wire:dirty.class="is-dirty"
wire:loading.class="!opacity-0 !translate-y-6 !pointer-events-none"
@if ($targets) wire:target="{{ $targets }}" @endif
class="pointer-events-none fixed inset-x-3 bottom-[max(1.5rem,env(safe-area-inset-bottom,0px)+0.75rem)] z-[80] flex max-w-full translate-y-6 flex-col items-stretch gap-2 rounded-2xl border border-white/10 bg-surface py-2.5 pr-2.5 pl-4 opacity-0 shadow-modal transition-[opacity,transform] duration-200 ease-out delay-0 [&.is-dirty]:pointer-events-auto [&.is-dirty]:translate-y-0 [&.is-dirty]:opacity-100 [&.is-dirty]:delay-300 sm:inset-x-auto sm:left-1/2 sm:bottom-6 sm:w-max sm:max-w-none sm:-translate-x-1/2 sm:flex-row sm:items-center sm:gap-8 sm:py-2 sm:pl-5 sm:pr-2">
class="pointer-events-none fixed inset-x-3 bottom-[max(1.5rem,env(safe-area-inset-bottom,0px)+0.75rem)] z-[1000] flex max-w-full translate-y-6 flex-col items-stretch gap-2 rounded-2xl border border-white/10 bg-surface py-2.5 pr-2.5 pl-4 opacity-0 shadow-modal transition-[opacity,transform] duration-200 ease-out delay-0 [&.is-dirty]:pointer-events-auto [&.is-dirty]:translate-y-0 [&.is-dirty]:opacity-100 [&.is-dirty]:delay-300 sm:inset-x-auto sm:left-1/2 sm:bottom-6 sm:w-max sm:max-w-none sm:-translate-x-1/2 sm:flex-row sm:items-center sm:gap-8 sm:py-2 sm:pl-5 sm:pr-2">
<span class="text-[13px] font-semibold leading-snug text-fg sm:whitespace-nowrap">{{ $label }}</span>
<div class="flex shrink-0 items-center justify-end gap-2">
<button type="button" onclick="window.location.reload()"
@@ -30,7 +30,7 @@
Reset
</button>
<button type="button" wire:click="{{ $action }}" wire:loading.attr="disabled"
class="h-8 rounded-lg bg-coollabs/10 px-4 text-[13px] font-semibold text-coollabs ring-1 ring-coollabs/25 transition-[transform,background-color] hover:bg-coollabs/15 active:scale-[0.98] dark:bg-warning/15 dark:text-warning dark:ring-warning/25 dark:hover:bg-warning/20">
class="button-highlighted h-8 rounded-lg px-4 text-[13px] font-semibold transition-[transform,background-color] active:scale-[0.98]">
Save changes
</button>
</div>
@@ -40,7 +40,7 @@
<x-reicon name="check-circle" class="size-3" />
</template>
<template x-if="currentStep === {{ $stepNumber }}">
<svg class="size-3 animate-spin text-coollabs dark:text-warning" xmlns="http://www.w3.org/2000/svg"
<svg class="loading-indicator size-3 animate-spin" xmlns="http://www.w3.org/2000/svg"
fill="none" viewBox="0 0 24 24" aria-hidden="true">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor"
stroke-width="4"></circle>
+2 -2
View File
@@ -143,7 +143,7 @@
@can('create', App\Models\PrivateKey::class)
<x-slot:contents>
<a href="{{ route('security.private-key.index') }}" {{ wireNavigate() }}
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button button-highlighted">
<x-reicon name="plus" class="size-3.5" />
Add private key
</a>
@@ -157,7 +157,7 @@
@can('createAnyResource')
<x-slot:contents>
<a href="{{ route('server.create') }}" {{ wireNavigate() }}
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button button-highlighted">
<x-reicon name="plus" class="size-3.5" />
New server
</a>
@@ -76,7 +76,7 @@
class="flex items-center gap-2 rounded-xl border border-neutral-200 bg-white px-3.5 py-2 text-sm font-medium text-neutral-800 transition-colors hover:bg-neutral-50 dark:border-white/[0.08] dark:bg-surface dark:text-fg dark:hover:bg-white/[0.04]"
style="box-shadow: 0 0 0 1px var(--coollabs-hairline), var(--shadow-modal);"
:aria-expanded="expanded.toString()" aria-label="Active deployments">
<svg class="size-3.5 shrink-0 animate-spin text-coollabs dark:text-warning"
<svg class="loading-indicator size-3.5 shrink-0 animate-spin"
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" aria-hidden="true">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor"
stroke-width="4"></circle>
@@ -16,7 +16,7 @@
<x-modal-input title="New Destination">
<x-slot:content>
<button type="button"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button button-highlighted">
<x-reicon name="plus" class="size-3.5" />
New destination
</button>
@@ -15,7 +15,7 @@
->all()" />
<div class="flex justify-end border-t border-neutral-200 pt-4 dark:border-white/[0.07]">
<x-forms.button type="submit" wire:target="submit"
class="bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button-highlighted">
Create destination
</x-forms.button>
</div>
@@ -172,7 +172,7 @@
<div class="flex flex-col gap-2 sm:flex-row sm:items-center">
<a target="_blank" href="https://github.com/sponsors/coollabsio"
class="button h-9 justify-center bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! sm:flex-1 dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button button-highlighted h-9 justify-center sm:flex-1">
GitHub Sponsors
</a>
<a x-show="!reminders.sponsorship.compact" x-transition.opacity target="_blank"
@@ -24,7 +24,7 @@
id="testEmailAddress" label="Recipient" required />
<div class="flex justify-end border-t border-neutral-200 pt-4 dark:border-white/[0.08]">
<button type="submit" @click="modalOpen=false"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button button-highlighted">
Send email
</button>
</div>
@@ -11,7 +11,7 @@
<footer class="flex justify-end border-t border-neutral-200 pt-4 dark:border-white/[0.08]">
<x-forms.button type="submit"
defaultClass="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
defaultClass="button button-highlighted">
Create project
</x-forms.button>
</footer>
@@ -16,7 +16,7 @@
<div class="mt-2 flex justify-end border-t border-neutral-200 pt-4 dark:border-white/[0.08]">
<x-forms.button type="submit"
class="bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button-highlighted">
Create schedule
</x-forms.button>
</div>
@@ -63,7 +63,7 @@
<x-modal-input title="New scheduled backup" :wireIgnore="false">
<x-slot:content>
<button type="button"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button button-highlighted">
<x-reicon name="plus" class="size-3.5" />
Add
</button>
@@ -274,8 +274,8 @@
}" class="flex h-[calc(100dvh-8rem)] min-h-[32rem] w-full flex-col overflow-hidden xl:h-auto xl:min-h-0 xl:flex-1">
<div id="screen" :class="fullscreen ? 'fullscreen flex flex-col' : 'mt-2 flex flex-1 min-h-0 flex-col overflow-hidden lg:mt-0'">
<div @if ($isKeepAliveOn) wire:poll.2000ms="polling" @endif
class="logs-viewer flex min-h-0 w-full flex-col overflow-hidden bg-[#0d0d0d] text-neutral-100"
:class="fullscreen ? 'h-full' : 'flex-1 rounded-xl border border-neutral-800 shadow-sm'">
class="logs-viewer flex min-h-0 w-full flex-col overflow-hidden bg-white text-neutral-800 dark:bg-[#0d0d0d] dark:text-neutral-100"
:class="fullscreen ? 'h-full' : 'flex-1 rounded-xl border border-neutral-200 shadow-sm dark:border-neutral-800'">
<div class="logs-viewer-toolbar">
@php
$deploymentStatus = data_get($application_deployment_queue, 'status');
@@ -292,12 +292,12 @@
<div class="logs-viewer-toolbar-controls">
<div class="logs-viewer-search relative">
<x-reicon name="search"
class="pointer-events-none absolute top-1/2 left-2.5 z-10 size-3.5 -translate-y-1/2 text-neutral-500" />
class="pointer-events-none absolute top-1/2 left-2.5 z-10 size-3.5 -translate-y-1/2 text-neutral-400 dark:text-neutral-500" />
<input type="search" x-model.debounce.300ms="searchQuery" placeholder="Find in logs"
aria-label="Find in logs"
class="h-8! w-full rounded-lg! border-white/[0.08]! bg-white/[0.05]! py-0! pr-8! pl-8! text-[12px]! text-white! shadow-none! placeholder:text-neutral-500 focus:border-accent! focus:ring-0!" />
class="h-8! w-full rounded-lg! border-neutral-200! bg-white! py-0! pr-8! pl-8! text-[12px]! text-neutral-800! shadow-none! placeholder:text-neutral-400 focus:border-accent! focus:ring-0! dark:border-white/[0.08]! dark:bg-white/[0.05]! dark:text-white! dark:placeholder:text-neutral-500" />
<button x-cloak x-show="searchQuery" x-on:click="searchQuery = ''" type="button"
class="absolute top-1/2 right-2 z-10 flex size-5 -translate-y-1/2 items-center justify-center rounded text-neutral-500 transition-colors hover:bg-white/[0.07] hover:text-white"
class="absolute top-1/2 right-2 z-10 flex size-5 -translate-y-1/2 items-center justify-center rounded text-neutral-400 transition-colors hover:bg-neutral-100 hover:text-neutral-800 dark:text-neutral-500 dark:hover:bg-white/[0.07] dark:hover:text-white"
aria-label="Clear search">
<x-reicon name="x" class="size-3" />
</button>
@@ -333,10 +333,10 @@
x-transition:leave="transition ease-in duration-75"
x-transition:leave-start="transform opacity-100 scale-100"
x-transition:leave-end="transform opacity-0 scale-95"
class="absolute right-0 z-50 mt-2 w-max origin-top-right rounded-lg border border-white/[0.1] bg-[#181818] p-1 shadow-modal focus:outline-none">
class="absolute right-0 z-50 mt-2 w-max origin-top-right rounded-lg border border-neutral-200 bg-white p-1 shadow-modal focus:outline-none dark:border-white/[0.1] dark:bg-[#181818]">
<div>
<button x-on:click="downloadLogs(); downloadMenuOpen = false"
class="listbox-option text-neutral-200! hover:bg-white/[0.07]!">
class="listbox-option text-neutral-700! hover:bg-neutral-100! dark:text-neutral-200! dark:hover:bg-white/[0.07]!">
Download displayed logs
</button>
@can('update', $application)
@@ -360,7 +360,7 @@
"
:disabled="downloadingAllLogs"
:class="{ 'opacity-50 cursor-not-allowed': downloadingAllLogs }"
class="listbox-option text-neutral-200! hover:bg-white/[0.07]!">
class="listbox-option text-neutral-700! hover:bg-neutral-100! dark:text-neutral-200! dark:hover:bg-white/[0.07]!">
<span x-show="!downloadingAllLogs">Download all logs</span>
<span x-show="downloadingAllLogs" class="flex items-center gap-2">
<svg class="w-4 h-4 animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
@@ -128,7 +128,7 @@
canGate="update" :canResource="$application">
<x-slot:content>
<button type="button"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button button-highlighted">
<x-reicon name="plus" class="size-3.5" />
Add
</button>
@@ -11,7 +11,7 @@
<x-modal-input title="New Project">
<x-slot:content>
<button type="button"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button button-highlighted">
<x-reicon name="plus" class="size-3.5" />
New project
</button>
@@ -82,7 +82,7 @@
class="flex size-6.5 items-center justify-center rounded-md transition-colors"
:class="viewMode === 'table'
?
'bg-coollabs/10 text-coollabs dark:bg-warning/15 dark:text-warning' :
'control-selected' :
'text-neutral-400 hover:bg-neutral-100 hover:text-black dark:text-fg-faint dark:hover:bg-white/[0.06] dark:hover:text-fg'"
aria-label="Table view" title="Table view">
<x-reicon name="unordered-list" class="size-3.5" />
@@ -91,7 +91,7 @@
class="flex size-6.5 items-center justify-center rounded-md transition-colors"
:class="viewMode === 'grid'
?
'bg-coollabs/10 text-coollabs dark:bg-warning/15 dark:text-warning' :
'control-selected' :
'text-neutral-400 hover:bg-neutral-100 hover:text-black dark:text-fg-faint dark:hover:bg-white/[0.06] dark:hover:text-fg'"
aria-label="Grid view" title="Grid view">
<x-reicon name="grid" class="size-3.5" />
@@ -131,7 +131,7 @@
Docs
</a>
<button type="button"
class="button ml-auto bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!"
class="button ml-auto button-highlighted"
:disabled="selecting" @click="setType(application.id)">
Deploy
</button>
@@ -166,7 +166,7 @@
Docs
</a>
<button type="button"
class="button ml-auto bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!"
class="button ml-auto button-highlighted"
:disabled="selecting" @click="setType(application.id)">
Deploy
</button>
@@ -219,7 +219,7 @@
Website
</a>
<button type="button"
class="button ml-auto bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!"
class="button ml-auto button-highlighted"
:disabled="selecting" @click="setType(database.id)">
Deploy
</button>
@@ -295,7 +295,7 @@
Website
</a>
<button type="button"
class="button ml-auto bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!"
class="button ml-auto button-highlighted"
:disabled="selecting"
@click="setType('one-click-service-' + service.id)">
Deploy
@@ -25,7 +25,7 @@
@can('createAnyResource')
<a href="{{ route('project.resource.create', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid]) }}"
{{ wireNavigate() }}
class="button whitespace-nowrap bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button whitespace-nowrap button-highlighted">
<x-reicon name="plus" class="size-3.5" />
New resource
</a>
@@ -69,7 +69,7 @@
<div class="flex items-center gap-2">
<div class="relative" x-on:click.outside="filterOpen = false">
<button type="button" class="button max-w-64"
:class="activeFilterCount > 0 && 'bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 dark:bg-warning/15! dark:text-warning! dark:ring-warning/25'"
:class="activeFilterCount > 0 && 'button-highlighted'"
x-on:click="filterOpen = !filterOpen" :title="activeFilterCount > 0 ? filterButtonText : 'Filter'">
<svg class="size-3.5 opacity-65" viewBox="0 0 24 24" fill="none" aria-hidden="true">
<path d="M4 6h16M7 12h10M10 18h4" stroke="currentColor" stroke-width="1.7"
@@ -148,7 +148,7 @@
class="flex size-6.5 items-center justify-center rounded-md transition-colors"
:class="viewMode === 'table'
?
'bg-coollabs/10 text-coollabs dark:bg-warning/15 dark:text-warning' :
'control-selected' :
'text-neutral-400 hover:bg-neutral-100 hover:text-black dark:text-fg-faint dark:hover:bg-white/[0.06] dark:hover:text-fg'"
aria-label="Table view" title="Table view">
<x-reicon name="unordered-list" class="size-3.5" />
@@ -157,7 +157,7 @@
class="flex size-6.5 items-center justify-center rounded-md transition-colors"
:class="viewMode === 'grid'
?
'bg-coollabs/10 text-coollabs dark:bg-warning/15 dark:text-warning' :
'control-selected' :
'text-neutral-400 hover:bg-neutral-100 hover:text-black dark:text-fg-faint dark:hover:bg-white/[0.06] dark:hover:text-fg'"
aria-label="Grid view" title="Grid view">
<x-reicon name="grid" class="size-3.5" />
@@ -114,7 +114,7 @@
<button type="button" x-on:click="setViewMode('table')"
class="flex size-6.5 items-center justify-center rounded-md transition-colors"
:class="viewMode === 'table'
? 'bg-coollabs/10 text-coollabs dark:bg-warning/15 dark:text-warning'
? 'control-selected'
: 'text-neutral-400 hover:bg-neutral-100 hover:text-black dark:text-fg-faint dark:hover:bg-white/[0.06] dark:hover:text-fg'"
aria-label="Table view" title="Table view">
<x-reicon name="unordered-list" class="size-3.5" />
@@ -122,7 +122,7 @@
<button type="button" x-on:click="setViewMode('grid')"
class="flex size-6.5 items-center justify-center rounded-md transition-colors"
:class="viewMode === 'grid'
? 'bg-coollabs/10 text-coollabs dark:bg-warning/15 dark:text-warning'
? 'control-selected'
: 'text-neutral-400 hover:bg-neutral-100 hover:text-black dark:text-fg-faint dark:hover:bg-white/[0.06] dark:hover:text-fg'"
aria-label="Grid view" title="Grid view">
<x-reicon name="grid" class="size-3.5" />
@@ -103,7 +103,7 @@
canGate="update" :canResource="$service">
<x-slot:content>
<button type="button"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button button-highlighted">
<x-reicon name="plus" class="size-3.5" />
Add
</button>
@@ -40,7 +40,7 @@
">
<div class="relative" @click.outside="dropdownOpen = false">
<x-forms.button
class="bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!"
class="button-highlighted"
@click="dropdownOpen = !dropdownOpen" aria-haspopup="menu"
x-bind:aria-expanded="dropdownOpen">
<x-reicon name="plus" class="size-3.5" />
@@ -59,7 +59,7 @@
<x-modal-input title="New scheduled backup" :wireIgnore="false">
<x-slot:content>
<button type="button"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button button-highlighted">
<x-reicon name="plus" class="size-3.5" />
Add
</button>
@@ -1,7 +1,15 @@
<div>
@if ($isConfigurationChanged && !is_null($resource->config_hash) && !$resource->isExited())
@php
$compactStoragePrefix = "configuration-warning:{$resource->uuid}:";
$currentConfigurationHash = $resource instanceof \App\Models\Application
? $resource->deploymentConfigurationHash()
: md5((string) $resource->config_hash);
$compactStorageKey = $compactStoragePrefix.$currentConfigurationHash;
@endphp
<div x-data="{ configurationDiffModalOpen: false, expandedRows: {} }">
<x-popup-small>
<x-popup-small :compact-after="5000" :compact-storage-key="$compactStorageKey"
:compact-storage-prefix="$compactStoragePrefix">
<x-slot:title>
The latest configuration has not been applied
</x-slot:title>
@@ -96,7 +96,7 @@
@if ($activeFilterCount > 0) title="{{ $activeFilterText }}" @endif
@class([
'button max-w-80 min-w-0',
'bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 dark:bg-warning/15! dark:text-warning! dark:ring-warning/25' => $activeFilterCount > 0,
'button-highlighted' => $activeFilterCount > 0,
])>
<x-reicon name="filter" class="size-3.5 shrink-0" />
<span class="truncate">{{ $activeFilterCount > 0 ? $activeFilterText : 'Filter' }}</span>
@@ -205,7 +205,7 @@
<x-modal-input title="New Environment Variable" :closeOutside="false">
<x-slot:content>
<button type="button"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button button-highlighted">
<x-reicon name="plus" class="size-3.5" />
Add
</button>
@@ -6,7 +6,7 @@
<x-modal-input title="New scheduled task" :closeOutside="false">
<x-slot:content>
<button type="button"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button button-highlighted">
<x-reicon name="plus" class="size-3.5" />
Add task
</button>
@@ -8,7 +8,7 @@
helper="Existing tags are assigned automatically. New names create team tags."
placeholder="production api customer-a" />
<x-forms.button type="submit"
class="bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button-highlighted">
<x-reicon name="plus" class="size-3.5" />
Add tags
</x-forms.button>
@@ -29,7 +29,7 @@
<x-modal-input title="New Environment">
<x-slot:content>
<button type="button"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button button-highlighted">
<x-reicon name="plus" class="size-3.5" />
New environment
</button>
@@ -41,7 +41,7 @@
<footer
class="flex justify-end border-t border-neutral-200 pt-4 dark:border-white/[0.08]">
<x-forms.button type="submit"
defaultClass="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
defaultClass="button button-highlighted">
Create environment
</x-forms.button>
</footer>
@@ -104,7 +104,7 @@
class="flex size-6.5 items-center justify-center rounded-md transition-colors"
:class="viewMode === 'table'
?
'bg-coollabs/10 text-coollabs dark:bg-warning/15 dark:text-warning' :
'control-selected' :
'text-neutral-400 hover:bg-neutral-100 hover:text-black dark:text-fg-faint dark:hover:bg-white/[0.06] dark:hover:text-fg'"
aria-label="Table view" title="Table view">
<x-reicon name="unordered-list" class="size-3.5" />
@@ -113,7 +113,7 @@
class="flex size-6.5 items-center justify-center rounded-md transition-colors"
:class="viewMode === 'grid'
?
'bg-coollabs/10 text-coollabs dark:bg-warning/15 dark:text-warning' :
'control-selected' :
'text-neutral-400 hover:bg-neutral-100 hover:text-black dark:text-fg-faint dark:hover:bg-white/[0.06] dark:hover:text-fg'"
aria-label="Grid view" title="Grid view">
<x-reicon name="grid" class="size-3.5" />
@@ -35,7 +35,7 @@
<x-application.settings-section title="New API token">
<x-slot:actions>
<button type="submit"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button button-highlighted">
<x-reicon name="plus" class="size-3.5" />
Create token
</button>
@@ -4,7 +4,7 @@
helper="Cloud-config YAML or another script accepted by your provider." required />
<div class="flex justify-end border-t border-neutral-200 pt-4 dark:border-white/[0.08]">
<button type="submit"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button button-highlighted">
{{ $scriptId ? 'Update script' : 'Create script' }}
</button>
</div>
@@ -11,7 +11,7 @@
<x-modal-input title="New Cloud-Init Script">
<x-slot:content>
<button type="button"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button button-highlighted">
<x-reicon name="plus" class="size-3.5" />
New script
</button>
@@ -45,7 +45,7 @@
<div class="flex justify-end border-t border-neutral-200 pt-4 dark:border-white/[0.08]">
<x-forms.button type="submit"
class="bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!"
class="button-highlighted"
wire:target="addToken">
Validate and add
</x-forms.button>
@@ -6,7 +6,7 @@
<x-modal-input title="New Cloud Token">
<x-slot:content>
<button type="button"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button button-highlighted">
<x-reicon name="plus" class="size-3.5" />
New token
</button>
@@ -14,7 +14,7 @@
</div>
<div class="flex justify-end border-t border-neutral-200 pt-4 dark:border-white/[0.08]">
<button type="submit"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button button-highlighted">
Continue
</button>
</div>
@@ -24,7 +24,7 @@
<div x-data="{ dropdownOpen: false }" class="relative"
@click.outside="dropdownOpen = false" @keydown.escape.window="dropdownOpen = false">
<button type="button" @click="dropdownOpen = !dropdownOpen"
class="button whitespace-nowrap bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!"
class="button whitespace-nowrap button-highlighted"
aria-haspopup="menu" :aria-expanded="dropdownOpen">
<x-reicon name="plus" class="size-3.5" />
<span class="max-sm:hidden">New private key</span>
@@ -19,7 +19,7 @@
<x-modal-input title="New {{ $tokenProviderName }} token">
<x-slot:content>
<button type="button"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button button-highlighted">
<x-reicon name="plus" class="size-3.5" />
New token
</button>
@@ -7,7 +7,7 @@
<h1 class="min-w-0 text-[24px]! leading-7! font-semibold! tracking-tight!">Servers</h1>
@can('createAnyResource')
<a href="{{ route('server.create') }}" {{ wireNavigate() }}
class="button w-fit shrink-0 whitespace-nowrap bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button w-fit shrink-0 whitespace-nowrap button-highlighted">
<x-reicon name="plus" class="size-3.5" />
New server
</a>
@@ -78,7 +78,7 @@
<button type="button" x-on:click="setViewMode('table')"
class="flex size-6.5 items-center justify-center rounded-md transition-colors"
:class="viewMode === 'table'
? 'bg-coollabs/10 text-coollabs dark:bg-warning/15 dark:text-warning'
? 'control-selected'
: 'text-neutral-400 hover:bg-neutral-100 hover:text-black dark:text-fg-faint dark:hover:bg-white/[0.06] dark:hover:text-fg'"
aria-label="Table view">
<x-reicon name="unordered-list" class="size-3.5" />
@@ -86,7 +86,7 @@
<button type="button" x-on:click="setViewMode('grid')"
class="flex size-6.5 items-center justify-center rounded-md transition-colors"
:class="viewMode === 'grid'
? 'bg-coollabs/10 text-coollabs dark:bg-warning/15 dark:text-warning'
? 'control-selected'
: 'text-neutral-400 hover:bg-neutral-100 hover:text-black dark:text-fg-faint dark:hover:bg-white/[0.06] dark:hover:text-fg'"
aria-label="Grid view">
<x-reicon name="grid" class="size-3.5" />
@@ -65,7 +65,7 @@
description="Choose the region, size, image, and Coolify SSH key.">
<x-slot:actions>
<button type="submit"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!"
class="button button-highlighted"
@disabled(!$private_key_id)>
Buy and create
@if ($this->selectedDropletPrice)
@@ -72,7 +72,7 @@
description="Choose the location, hardware, operating system, and Coolify SSH key.">
<x-slot:actions>
<button type="submit"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!"
class="button button-highlighted"
@disabled(!$private_key_id)>
Buy and create
@if ($this->selectedServerPrice)
@@ -14,7 +14,7 @@
description="Add an existing Linux server using its SSH connection details.">
<x-slot:actions>
<button type="submit"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button button-highlighted">
Continue
<x-reicon name="arrow-right" class="size-3.5" />
</button>
@@ -64,7 +64,7 @@
description="Choose the region, plan, operating system, and Coolify SSH key.">
<x-slot:actions>
<button type="submit"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!"
class="button button-highlighted"
@disabled(!$private_key_id)>
Buy and create
@if ($this->selectedServerPrice)
@@ -78,7 +78,7 @@
source to an application.
</p>
</div>
<a class="button shrink-0 bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!"
<a class="button shrink-0 button-highlighted"
href="{{ getInstallationPath($github_app) }}">
Install repositories
<x-external-link />
@@ -313,7 +313,7 @@
helper="Write access lets Coolify post deployment status and links on pull requests." />
<button type="button"
class="button mt-auto w-full justify-center bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!"
class="button mt-auto w-full justify-center button-highlighted"
x-on:click.prevent="createGithubApp(webhookEndpoint, useCustomWebhookEndpoint, customWebhookEndpoint, {{ Illuminate\Support\Js::from($preview_deployment_permissions) }}, {{ Illuminate\Support\Js::from($administration) }})">
Register with GitHub
</button>
@@ -307,7 +307,7 @@
<x-application.settings-section title="Step 3 · Authorize with GitLab"
description="Authorize Coolify with your GitLab instance. The redirect URI must match the Callback URL configured in GitLab.">
<a href="{{ $this->getOAuthUrl() }}" wire:key="oauth-url-{{ md5((string) $redirectUri) }}"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button button-highlighted">
Connect to GitLab
<x-external-link />
</a>
@@ -15,7 +15,7 @@
<x-modal-input title="New S3 Storage" :closeOutside="false">
<x-slot:content>
<button type="button"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
class="button button-highlighted">
<x-reicon name="plus" class="size-3.5" />
New storage
</button>
@@ -25,9 +25,6 @@
<button type="button" wire:click="switch_to({{ $team->id }})" @click="open = false"
class="listbox-option {{ $team->id === $currentTeam->id ? 'bg-neutral-100 font-medium dark:bg-white/[0.06]' : '' }}">
<span class="min-w-0 flex-1 truncate">{{ $team->name }}</span>
@if ($team->id === $currentTeam->id)
<x-reicon name="check-circle" class="size-3.5 shrink-0 text-warning" />
@endif
</button>
@endforeach
<div class="mt-1 border-t border-neutral-200 pt-1 dark:border-white/[0.08]">
@@ -72,9 +69,6 @@
<button type="button" wire:click="switch_to({{ $team->id }})" @click="teamOpen = false"
class="listbox-option {{ $team->id === $currentTeam->id ? 'bg-neutral-100 font-medium dark:bg-white/[0.06]' : '' }}">
<span class="min-w-0 flex-1 truncate">{{ $team->name }}</span>
@if ($team->id === $currentTeam->id)
<x-reicon name="check-circle" class="size-3.5 shrink-0 text-warning" />
@endif
</button>
@endforeach
<div class="mt-1 border-t border-neutral-200 pt-1 dark:border-white/[0.08]">
+7 -5
View File
@@ -75,7 +75,7 @@
class="flex size-6.5 items-center justify-center rounded-md transition-colors"
:class="viewMode === 'table'
?
'bg-coollabs/10 text-coollabs dark:bg-warning/15 dark:text-warning' :
'control-selected' :
'text-neutral-400 hover:bg-neutral-100 hover:text-black dark:text-fg-faint dark:hover:bg-white/[0.06] dark:hover:text-fg'"
aria-label="Table view" title="Table view">
<x-reicon name="unordered-list" class="size-3.5" />
@@ -84,7 +84,7 @@
class="flex size-6.5 items-center justify-center rounded-md transition-colors"
:class="viewMode === 'grid'
?
'bg-coollabs/10 text-coollabs dark:bg-warning/15 dark:text-warning' :
'control-selected' :
'text-neutral-400 hover:bg-neutral-100 hover:text-black dark:text-fg-faint dark:hover:bg-white/[0.06] dark:hover:text-fg'"
aria-label="Grid view" title="Grid view">
<x-reicon name="grid" class="size-3.5" />
@@ -408,9 +408,11 @@
<div wire:poll="getDeployments" class="overflow-x-auto">
@if (count($deploymentsPerTagPerServer ?? []) === 0)
<x-empty title="No active deployments"
description="Deployments will appear here while they are queued or running."
icon-name="play-circle" size="sm" />
<div class="p-3">
<x-empty title="No active deployments"
description="Deployments will appear here while they are queued or running."
icon-name="play-circle" size="sm" />
</div>
@else
<div
class="grid min-w-[620px] grid-cols-[minmax(0,1fr)_minmax(10rem,.55fr)_7rem] border-b border-neutral-200 bg-neutral-50 px-4 py-2.5 text-[11px] font-medium text-neutral-500 dark:border-white/[0.08] dark:bg-white/[0.025] dark:text-fg-faint">
@@ -3,7 +3,7 @@
<x-forms.input id="description" label="Description" />
<div class="flex justify-end">
<x-forms.button type="submit"
defaultClass="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
defaultClass="button button-highlighted">
Create team
</x-forms.button>
</div>
@@ -11,7 +11,7 @@
</x-forms.button>
@endif
<x-forms.button type="submit" wire:target="viaLink"
defaultClass="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!">
defaultClass="button button-highlighted">
<x-reicon name="plus" class="size-3.5" />
Generate link
</x-forms.button>
+1 -1
View File
@@ -71,7 +71,7 @@
<div
class="flex items-center gap-2.5 rounded-lg border border-neutral-200 bg-neutral-50 px-3 py-2.5 dark:border-white/[0.08] dark:bg-white/[0.03]">
<template x-if="!upgradeComplete && !upgradeError">
<svg class="size-4 shrink-0 animate-spin text-coollabs dark:text-warning"
<svg class="loading-indicator size-4 shrink-0 animate-spin"
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
aria-hidden="true">
<circle class="opacity-25" cx="12" cy="12" r="10"
+1 -1
View File
@@ -15,7 +15,7 @@
<div x-data="{ dropdownOpen: false }" class="relative w-fit shrink-0"
@click.outside="dropdownOpen = false" @keydown.escape.window="dropdownOpen = false">
<button type="button" @click="dropdownOpen = !dropdownOpen"
class="button bg-coollabs/10! text-coollabs! ring-1 ring-coollabs/25 hover:bg-coollabs/15! dark:bg-warning/15! dark:text-warning! dark:ring-warning/25 dark:hover:bg-warning/20!"
class="button button-highlighted"
aria-haspopup="menu" :aria-expanded="dropdownOpen">
<x-reicon name="plus" class="size-3.5" />
New source
@@ -0,0 +1,14 @@
<?php
test('breadcrumb switchers indicate the current item with background only', function () {
$views = collect([
resource_path('views/components/top-breadcrumb.blade.php'),
resource_path('views/components/breadcrumb-switcher.blade.php'),
resource_path('views/livewire/switch-team.blade.php'),
])->map(fn (string $path): string => file_get_contents($path));
expect($views->implode("\n"))
->not->toContain('name="check-circle"')
->and($views->every(fn (string $view): bool => str_contains($view, 'bg-neutral-100')))
->toBeTrue();
});
@@ -30,6 +30,7 @@ it('shows an add private key button when no private keys exist', function () {
->assertSee('A private key is required')
->assertSee('Add an SSH private key before connecting your first server.')
->assertSee('Add private key')
->assertSeeHtml('button button-highlighted')
->assertSee(route('security.private-key.index'), false);
});
@@ -0,0 +1,20 @@
<?php
test('active deployment log controls use a coollabs background and white icon', function () {
$styles = file_get_contents(resource_path('css/app.css'));
expect($styles)
->toMatch('/\.logs-viewer-btn-active\s*\{[^}]*background:\s*var\(--color-coollabs\);[^}]*color:\s*#fff;/s')
->not->toMatch('/\.logs-viewer-btn-active\s*\{[^}]*var\(--color-warning\)/s');
});
test('deployment logs use light surfaces in light mode and dark surfaces in dark mode', function () {
$view = file_get_contents(resource_path('views/livewire/project/application/deployment/show.blade.php'));
expect($view)
->toContain('bg-white text-neutral-800')
->toContain('dark:bg-[#0d0d0d] dark:text-neutral-100')
->toContain('border-neutral-200 shadow-sm dark:border-neutral-800')
->toContain('border-neutral-200! bg-white!')
->toContain('dark:border-white/[0.08]! dark:bg-white/[0.05]!');
});
@@ -90,6 +90,13 @@ test('shared environment variables table still omits Managed column', function (
expect($show)->toContain('! $isSharedVariable');
});
test('shared environment variables empty state has spacing around the card', function () {
$editor = file_get_contents(resource_path('views/components/shared-variables/editor.blade.php'));
expect($editor)
->toMatch('/<div class="p-3">\s*<x-empty title="No shared variables"/');
});
test('managed environment variables are ordered first', function () {
$component = file_get_contents(app_path('Livewire/Project/Shared/EnvironmentVariable/All.php'));
+1 -1
View File
@@ -9,7 +9,7 @@ test('helper trigger is a button that stops label activation', function () {
expect($contents)
->toContain('type="button"')
->toContain('@click.prevent.stop')
->toContain('aria-label="More information"')
->toContain('aria-label="{{ $label }}"')
->toContain('info-helper-popup')
->toContain('name="info-circle"')
->toContain('class="size-3.5 text-neutral-400')
@@ -0,0 +1,19 @@
<?php
test('highlighted buttons use the shared coollabs style in every color scheme', function () {
$utilities = file_get_contents(resource_path('css/utilities.css'));
$appStyles = file_get_contents(resource_path('css/app.css'));
$views = collect(new RecursiveIteratorIterator(new RecursiveDirectoryIterator(resource_path('views'))))
->filter(fn (SplFileInfo $file): bool => $file->isFile() && $file->getExtension() === 'php')
->map(fn (SplFileInfo $file): string => file_get_contents($file->getPathname()))
->implode("\n");
expect($utilities)
->toContain('@utility button-highlighted')
->toContain('@apply border-2 text-coollabs-200 dark:text-white bg-coollabs-50 dark:bg-coollabs/20 border-coollabs dark:border-coollabs-100 hover:bg-coollabs hover:text-white dark:hover:bg-coollabs-100 dark:hover:text-white;')
->and($appStyles)
->toContain('button[isHighlighted]:not(:disabled)')
->toContain('@apply button-highlighted;')
->and($views)
->not->toContain('dark:bg-warning/15! dark:text-warning! dark:ring-warning/25');
});
@@ -73,10 +73,29 @@ it('renders the changed configuration labels without a second backend request',
$view = file_get_contents(resource_path('views/livewire/project/shared/configuration-checker.blade.php'));
expect($view)
->toContain(':compact-after="5000"')
->toContain(':compact-storage-key="$compactStorageKey"')
->toContain('x-on:click="configurationDiffModalOpen = true"')
->not->toContain('$wire.refreshConfigurationChanges()');
});
it('supports timed compact popup notifications', function () {
$view = file_get_contents(resource_path('views/components/popup-small.blade.php'));
expect($view)
->toContain('compactAfter')
->toContain('compactStorageKey')
->toContain("localStorage.setItem(this.storageKey, 'compact')")
->toContain("localStorage.setItem(this.storageKey, 'icon')")
->toContain('localStorage.removeItem(key)')
->toContain('<template x-teleport="body">')
->toContain('compact = true')
->toContain('@click="restore()"')
->toContain('@click.stop="minimizeToIcon()"')
->toContain('x-show="!iconOnly"')
->toContain('x-show="!compact"');
});
it('refreshes configuration changes when the event is received', function () {
$application = configurationCheckerApplication($this->environment);
markConfigurationCheckerApplicationDeployed($application);
@@ -0,0 +1,22 @@
<?php
test('livewire loading indicators use the shared coollabs purple', function () {
$utilities = file_get_contents(resource_path('css/utilities.css'));
$loading = file_get_contents(resource_path('views/components/loading.blade.php'));
$pageLoading = file_get_contents(resource_path('views/components/page-loading.blade.php'));
$views = collect(new RecursiveIteratorIterator(new RecursiveDirectoryIterator(resource_path('views'))))
->filter(fn (SplFileInfo $file): bool => $file->isFile() && $file->getExtension() === 'php')
->map(fn (SplFileInfo $file): string => file_get_contents($file->getPathname()))
->implode("\n");
expect($utilities)
->toContain('@utility loading-indicator')
->toContain('@apply text-coollabs dark:text-coollabs-100;')
->and($loading)->toContain('loading-indicator')
->and($pageLoading)->toContain('loading-indicator')
->and($views)->not->toMatch('/animate-spin[^"\n]*dark:text-warning|dark:text-warning[^"\n]*animate-spin/');
});
test('livewire navigation progress bar uses coollabs purple', function () {
expect(config('livewire.navigate.progress_bar_color'))->toBe('#6b16ed');
});
@@ -94,7 +94,7 @@ it('keeps application links next to advanced actions on the right', function ()
expect($advancedPosition)->not->toBeFalse()
->and($linksPosition)->not->toBeFalse()
->and($linksPosition)->toBeGreaterThan($advancedPosition)
->and($links)->toContain('listbox-panel top-full! right-0! left-auto!')
->and($links)->toContain("'right-0! left-auto! min-w-60! max-w-96!' => !\$fullWidth")
->and($links)->toContain('listbox-option justify-start! gap-2.5!')
->and($links)->not->toContain('md:left-0 md:right-auto');
@@ -344,6 +344,7 @@ it('shows application and service Links on mobile headings', function () {
$applicationLinks = file_get_contents(resource_path('views/components/applications/links.blade.php'));
expect($applicationLinks)
->toContain("'button w-full justify-between' => \$fullWidth")
->toContain("'left-0! right-0! w-full! min-w-0! max-w-none!' => \$fullWidth")
->toContain('<x-reicon name="chevron-down"');
expect($mobileServiceSection)
@@ -355,6 +356,7 @@ it('shows application and service Links on mobile headings', function () {
$serviceLinks = file_get_contents(resource_path('views/components/services/links.blade.php'));
expect($serviceLinks)
->toContain("'button w-full justify-between' => \$fullWidth")
->toContain("'left-0! right-0! w-full! min-w-0! max-w-none!' => \$fullWidth")
->toContain('<x-reicon name="chevron-down"')
->toContain('No links available');
@@ -36,6 +36,15 @@ test('unsaved bar delays show and hides while loading to avoid instant-save flas
->toContain('delay-0');
});
test('unsaved bar stays above floating notifications so save actions remain accessible', function () {
$unsavedBar = file_get_contents(resource_path('views/components/unsaved-bar.blade.php'));
$popup = file_get_contents(resource_path('views/components/popup-small.blade.php'));
expect($unsavedBar)
->toContain('z-[1000]')
->and($popup)->toContain('z-999');
});
test('settings general unsaved bar excludes auto-saving instance timezone', function () {
$path = resource_path('views/livewire/settings/index.blade.php');
$contents = file_get_contents($path);
@@ -0,0 +1,22 @@
<?php
test('view switchers use the shared coollabs selected state on every page', function () {
$views = collect([
resource_path('views/livewire/server/index.blade.php'),
resource_path('views/livewire/project/index.blade.php'),
resource_path('views/livewire/project/show.blade.php'),
resource_path('views/livewire/project/resource/index.blade.php'),
resource_path('views/livewire/project/service/configuration.blade.php'),
resource_path('views/livewire/tags/show.blade.php'),
])->map(fn (string $path): string => file_get_contents($path));
$utilities = file_get_contents(resource_path('css/utilities.css'));
expect($views->every(fn (string $view): bool => str_contains($view, "viewMode === 'table'")
&& str_contains($view, "viewMode === 'grid'")
&& str_contains($view, 'control-selected')))
->toBeTrue()
->and($views->implode("\n"))->not->toContain('dark:bg-warning/15 dark:text-warning')
->and($utilities)
->toContain('@utility control-selected')
->toContain('@apply bg-coollabs text-white dark:bg-coollabs dark:text-white;');
});
@@ -2,7 +2,7 @@
use Illuminate\Support\Facades\Blade;
it('renders section helper copy as a visible description instead of an info icon', function () {
it('renders section helper copy as a tooltip on the underlined title', function () {
$html = Blade::render(<<<'BLADE'
<x-application.settings-section title="Primary server" helper="The server and network used by this resource.">
Content
@@ -12,5 +12,8 @@ it('renders section helper copy as a visible description instead of an info icon
expect($html)
->toContain('Primary server')
->toContain('The server and network used by this resource.')
->not->toContain('aria-label="More information"');
->toContain('aria-label="More information about Primary server"')
->toContain('class="underline underline-offset-4"')
->toContain('underline-offset-4')
->not->toMatch('/<p[^>]*>\s*The server and network used by this resource\.\s*<\/p>/');
});
@@ -19,19 +19,29 @@ test('shared variables pages hide the family title on desktop', function () {
}
});
test('shared variables editor places the view toggle next to the tabs', function () {
test('shared variables editor places the view toggle in the variables section title', function () {
$editor = file_get_contents(resource_path('views/components/shared-variables/editor.blade.php'));
expect($editor)
->toContain('<x-application.settings-section :title="$title" flush>')
->toContain('<x-slot:actions>')
->toContain('wire:click="switch"')
->toContain('Developer view')
->toContain('Normal view');
->toContain('Normal view')
->toMatch('/settings-section[\s\S]{0,300}<x-slot:actions>[\s\S]{0,300}wire:click="switch"/')
->not->toContain('actionsInTitle');
});
// No longer nested as a section header action above the variable list.
expect($editor)->not->toMatch(
'/settings-section[\s\S]{0,200}<x-slot:actions>[\s\S]{0,200}wire:click="switch"/'
);
test('shared variables navigation uses the standard mobile settings menu', function () {
$navbar = file_get_contents(resource_path('views/components/dashboard/navbar.blade.php'));
expect($navbar)
->toContain("\$stackTabsOnMobile = \$section === 'shared-variables'")
->toContain('grid grid-cols-2 gap-0.5 border-y')
->toContain("'menu-item'")
->toContain("'menu-item-active' => \$item['active']")
->toContain("\$sharedVariableIcons[\$item['label']]")
->toContain('hidden lg:flex');
});
test('shared variables table omits resource-only flag columns', function () {
+1 -1
View File
@@ -52,7 +52,7 @@ it('places the storage name and connection status in breadcrumbs and delete in t
->toContain('@storage-status-changed.window');
expect(file_get_contents(resource_path('views/components/breadcrumb-switcher.blade.php')))
->toContain('{{ $title }}')
->toContain('name="check-circle"');
->not->toContain('name="check-circle"');
expect(file_get_contents(resource_path('views/livewire/storage/index.blade.php')))
->toContain('label="Connected"')
->not->toContain('label="Ready"');
+7
View File
@@ -114,6 +114,13 @@ it('shows empty resource and deployment states for an unused tag', function () {
->assertSee('Queued or running');
});
it('adds spacing around the active deployments empty state', function () {
$view = file_get_contents(resource_path('views/livewire/tags/show.blade.php'));
expect($view)
->toMatch('/<div class="p-3">\s*<x-empty title="No active deployments"/');
});
it('redirects to the overview when the requested tag does not exist', function () {
Tag::create(['name' => 'hello', 'team_id' => $this->team->id]);