coolify/resources/views/livewire/server/validate-and-install.blade.php
Aditya Tripathi d91d2fa35d feat(ui): WCAG contrast, neutral oklch surface system, and mobile nav overhaul
Design tokens
- Fix WCAG AA text failures: darken light muted text and lighten dark
  tertiary text so 13-14px copy clears 4.5:1 on every surface.
- Rebuild the dark surface ladder in correct values (oklch compresses to
  near-black below ~15%), giving visible steps: content, chrome, cards.
- Establish a 3-layer shell (deep content, lighter sidebar/topbar chrome,
  lifted cards) with crisp hairline rings and a restrained card shadow.
- Unify every surface to one neutral temperature (pure gray); neutralize
  the cool-tinted sidebar text and legacy surface tokens.
- Convert all color tokens to oklch (neutrals + brand).
- Unify inputs to the recessed token; fix invisible dark placeholders.
- One shared --shadow-dropdown for all menus/listboxes/command palette.

Settings sidebar
- Sticky contained-card rail with subtle scrollbar; collapsible sub-section
  groups (active open by default), cross-page section links.
- Mobile: collapsible disclosure with animated open, outside/Escape dismiss,
  press feedback, and reduced-motion support.

Mobile
- Consolidate the resource header (title + status + links on one row).
- Tighten settings-page vertical rhythm.
- Rebuild the main sidebar as an animated shadcn-style sheet (slide/fade,
  Escape, scroll lock, in-panel close, close-on-navigate).

Fixes
- #11532: selected server invisible in the light-mode terminal toolbar.

Docs: update DESIGN.md tokens, shell layering, and temperature rules.
2026-09-08 07:04:48 +02:00

143 lines
7.1 KiB
PHP

@php
$resolveStatus = function (mixed $value, bool $visible, bool $hasError): string {
if (! $visible) {
return 'pending';
}
if ($value === true) {
return 'success';
}
if ($hasError) {
return 'error';
}
return 'running';
};
$showUptime = true;
$showOs = (bool) $uptime;
$showPrerequisites = (bool) ($uptime && $supported_os_type);
$showDocker = (bool) ($uptime && $supported_os_type && $prerequisites_installed);
$showCompose = $showDocker;
$showVersion = (bool) ($showDocker && $docker_compose_installed);
$validationComplete = (bool) ($uptime
&& $supported_os_type
&& $prerequisites_installed
&& $docker_installed
&& $docker_compose_installed
&& $docker_version);
$checkpoints = [
[
'title' => 'Server is reachable',
'description' => 'Verify SSH connectivity and key-based authentication',
'status' => $resolveStatus($uptime === null ? null : (bool) $uptime, $showUptime, (bool) $error && ! $uptime),
'visible' => $showUptime,
],
[
'title' => 'Supported OS type',
'description' => 'Confirm a supported Linux distribution',
'status' => $resolveStatus($supported_os_type === null ? null : (bool) $supported_os_type, $showOs, (bool) $error && $showOs && ! $supported_os_type),
'visible' => $showOs,
],
[
'title' => 'Prerequisites are installed',
'description' => 'Install required system packages when missing',
'status' => $resolveStatus($prerequisites_installed === null ? null : (bool) $prerequisites_installed, $showPrerequisites, (bool) $error && $showPrerequisites && ! $prerequisites_installed),
'visible' => $showPrerequisites,
],
[
'title' => 'Docker is installed',
'description' => 'Install or detect Docker Engine',
'status' => $resolveStatus($docker_installed === null ? null : (bool) $docker_installed, $showDocker, (bool) $error && $showDocker && ! $docker_installed),
'visible' => $showDocker,
],
[
'title' => 'Docker Compose is installed',
'description' => 'Install or detect Docker Compose',
'status' => $resolveStatus($docker_compose_installed === null ? null : (bool) $docker_compose_installed, $showCompose, (bool) $error && $showCompose && ! $docker_compose_installed),
'visible' => $showCompose,
],
[
'title' => 'Minimum Docker version',
'description' => 'Require Docker Engine '.str(config('constants.docker.minimum_required_version'))->before('.').' or newer',
'status' => $resolveStatus(
isset($docker_version) ? (bool) $docker_version : null,
$showVersion,
(bool) $error && $showVersion && isset($docker_version) && ! $docker_version
),
'visible' => $showVersion,
],
];
@endphp
<div class="flex h-full min-h-0 flex-col gap-4 overflow-y-auto scrollbar">
@if ($ask)
<div
class="rounded-[10px] border border-neutral-200 bg-neutral-50 px-4 py-3 text-[13px] leading-5 text-neutral-600 dark:border-white/[0.08] dark:bg-white/[0.05] dark:text-fg-dim">
This will revalidate the server, install or update Docker Engine, Docker Compose, and related
configuration. Docker Engine will restart, so running containers may be briefly unreachable.
</div>
<x-forms.button isHighlighted wire:click="startValidatingAfterAsking">
Continue
</x-forms.button>
@else
<div data-validation-checkpoints
class="shrink-0 overflow-hidden rounded-[10px] border border-neutral-200 dark:border-white/[0.08]">
<div class="border-b border-neutral-200 px-4 py-2.5 dark:border-white/[0.08]">
<h3 class="text-[13px] font-medium text-neutral-600 dark:text-fg-dim">Validation checkpoints</h3>
</div>
<div class="checkpoint-scroll-fade relative min-w-0" x-data="{
observer: null,
scrollToRunning() {
this.$refs.track.querySelector('[data-checkpoint-status=running]')?.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' })
}
}"
x-init="$nextTick(() => scrollToRunning()); observer = new MutationObserver(() => $nextTick(() => scrollToRunning())); observer.observe($refs.track, { subtree: true, childList: true, attributes: true, attributeFilter: ['data-checkpoint-status'] })"
x-destroy="observer?.disconnect()">
<div class="flex min-w-0 snap-x snap-mandatory overflow-x-auto overscroll-x-contain scroll-smooth scrollbar divide-x divide-neutral-200 dark:divide-white/[0.07]"
x-ref="track">
@foreach ($checkpoints as $checkpoint)
<x-checkpoint-item :title="$checkpoint['title']" :description="$checkpoint['description']"
:status="$checkpoint['status']" data-checkpoint-status="{{ $checkpoint['status'] }}"
class="basis-[88%] shrink-0 snap-start sm:basis-72 lg:basis-80" />
@endforeach
</div>
</div>
</div>
@if ($validationComplete)
<div class="mt-auto flex shrink-0 items-center justify-between gap-3 rounded-[10px] border border-emerald-500/20 bg-emerald-500/[0.06] px-4 py-3">
<div class="flex items-center gap-2 text-[13px] font-medium text-emerald-700 dark:text-emerald-300">
<x-reicon name="check-circle" class="size-4 shrink-0" />
Validation complete
</div>
<x-forms.button type="button" @click="processDialogOpen = false">
Close
</x-forms.button>
</div>
@elseif ($isInstalling)
<section class="application-settings-section validation-installation-logs">
<div class="application-settings-section-body">
<livewire:activity-monitor :header="$installationStep.' installation logs'" :showWaiting="false" />
</div>
</section>
@endif
@isset($error)
<div
class="rounded-[10px] border border-red-500/20 bg-red-500/[0.06] px-4 py-3 text-[13px] leading-5 text-red-700 dark:text-red-300">
<div class="mb-1 flex items-center gap-2 text-[12px] font-semibold uppercase tracking-[0.06em]">
<x-reicon name="alert-circle" class="size-3.5 shrink-0" />
Validation failed
</div>
<div class="font-mono text-[12px] leading-5 whitespace-pre-line">{!! $error !!}</div>
</div>
<x-forms.button canGate="update" :canResource="$server" wire:click="retry">
<x-reicon name="refresh" class="size-3.5" />
Retry validation
</x-forms.button>
@endisset
@endif
</div>