clusev/resources/views/livewire/release/index.blade.php

217 lines
13 KiB
PHP

@php
$isBeta = str_contains($current, '-beta');
$tiles = [];
if (isset($targets['continueBeta'])) {
$tiles[] = ['target' => $targets['continueBeta'], 'label' => __('release.kind_continue'), 'icon' => 'rotate', 'primary' => true];
}
$tiles[] = ['target' => $targets['patch'], 'label' => __('release.kind_patch'), 'icon' => 'tag', 'primary' => false];
$tiles[] = ['target' => $targets['minor'], 'label' => __('release.kind_minor'), 'icon' => 'tag', 'primary' => false];
$tiles[] = ['target' => $targets['major'], 'label' => __('release.kind_major'), 'icon' => 'git-branch', 'primary' => false];
@endphp
<div class="space-y-5">
{{-- Header --}}
<div class="flex flex-wrap items-end justify-between gap-3">
<div>
<p class="font-mono text-[11px] uppercase tracking-[0.2em] text-accent-text">{{ __('release.eyebrow') }}</p>
<h2 class="mt-0.5 font-display text-xl font-semibold text-ink sm:text-2xl">{{ __('release.heading') }}</h2>
<p class="mt-1 font-mono text-xs text-ink-3">{{ __('release.subtitle') }}</p>
</div>
<x-badge tone="cyan">{{ __('release.dev_instance') }}</x-badge>
</div>
<div class="grid gap-5 xl:grid-cols-[minmax(0,1fr)_22rem]">
{{-- Left: current version + action --}}
<div class="space-y-5">
{{-- Current version hero --}}
<x-panel :padded="false">
<div class="flex flex-col gap-5 p-5 sm:flex-row sm:items-center sm:justify-between">
<div class="flex items-center gap-4">
<div class="grid h-12 w-12 shrink-0 place-items-center rounded-lg border border-accent/25 bg-accent/10">
<x-icon name="tag" class="h-5 w-5 text-accent" />
</div>
<div>
<p class="font-mono text-[11px] uppercase tracking-wider text-ink-3">{{ __('release.current') }}</p>
<p class="font-mono text-2xl font-semibold leading-tight text-ink">{{ $current }}</p>
</div>
</div>
<dl class="flex gap-7 font-mono text-[11px]">
<div>
<dt class="uppercase tracking-wider text-ink-4">{{ __('release.channel') }}</dt>
<dd class="mt-1 {{ $isBeta ? 'text-warning' : 'text-online' }}">{{ $isBeta ? 'beta' : 'stable' }}</dd>
</div>
<div>
<dt class="uppercase tracking-wider text-ink-4">{{ __('release.source') }}</dt>
<dd class="mt-1 text-ink-2">Gitea</dd>
</div>
</dl>
</div>
</x-panel>
@if ($pendingId !== null)
{{-- Running --}}
<div class="flex items-center gap-4 rounded-lg border border-warning/30 bg-warning/5 px-5 py-4" wire:poll.5s="pollResult">
<div class="grid h-10 w-10 shrink-0 place-items-center rounded-lg border border-warning/30 bg-warning/10">
<x-icon name="rotate" class="h-5 w-5 animate-spin text-warning" />
</div>
<div class="min-w-0">
<p class="font-display text-sm font-semibold text-ink">{{ __('release.running_title') }}</p>
<p class="mt-0.5 truncate font-mono text-[11px] text-ink-3">{{ __('release.running', ['target' => $pendingTarget]) }}</p>
</div>
<x-status-pill status="pending" class="ml-auto shrink-0">{{ __('release.in_progress') }}</x-status-pill>
</div>
@else
@if ($lastTag !== null)
{{-- Last successful push --}}
<div class="flex flex-wrap items-center gap-2.5 rounded-lg border border-online/25 bg-online/10 px-5 py-3.5">
<x-status-dot status="online" />
<span class="font-mono text-[11px] text-ink-3">{{ __('release.staged_label') }}</span>
<span class="font-mono text-xs font-semibold text-online">{{ $lastTag }}</span>
<span class="font-mono text-[11px] text-ink-4"> Mirror CI Staging</span>
</div>
@endif
{{-- Option tiles --}}
<div>
<p class="mb-2.5 font-mono text-[11px] uppercase tracking-wider text-ink-3">{{ __('release.choose') }}</p>
<div class="grid gap-3 sm:grid-cols-2">
@foreach ($tiles as $t)
<button type="button"
wire:key="tile-{{ $t['target'] }}"
wire:click="confirmDeployStaging('{{ $t['target'] }}')"
wire:loading.attr="disabled"
@class([
'group flex flex-col gap-3 rounded-lg border p-4 text-left transition-colors focus:outline-none focus-visible:border-accent/50',
'border-accent/25 bg-accent/10 hover:bg-accent/15' => $t['primary'],
'border-line bg-inset hover:border-line-strong hover:bg-raised' => ! $t['primary'],
])>
<div class="flex items-center justify-between gap-2">
<span @class([
'font-display text-sm font-semibold',
'text-accent-text' => $t['primary'],
'text-ink' => ! $t['primary'],
])>{{ $t['label'] }}</span>
<x-icon :name="$t['icon']" @class([
'h-4 w-4 shrink-0',
'text-accent' => $t['primary'],
'text-ink-4 transition-colors group-hover:text-ink-2' => ! $t['primary'],
]) />
</div>
<div class="flex items-center gap-2 font-mono text-xs">
<span class="text-ink-4">{{ $current }}</span>
<x-icon name="chevron-right" class="h-3.5 w-3.5 shrink-0 text-ink-4" />
<span @class([
'font-semibold',
'text-accent-text' => $t['primary'],
'text-ink' => ! $t['primary'],
])>{{ $t['target'] }}</span>
<x-badge tone="{{ $t['primary'] ? 'accent' : 'neutral' }}" class="ml-auto">{{ __('release.tile_beta') }}</x-badge>
</div>
</button>
@endforeach
</div>
<p class="mt-3 font-mono text-[11px] leading-relaxed text-ink-4">{{ __('release.hint') }}</p>
</div>
@endif
@if ($isBeta)
{{-- Publish the current beta --}}
<div>
<p class="mb-2.5 font-mono text-[11px] uppercase tracking-wider text-ink-3">{{ __('release.publish') }}</p>
<div class="grid gap-3">
<x-btn variant="primary" wire:click="confirmDeployPublic" wire:loading.attr="disabled">
{{ __('release.deploy_public', ['tag' => $current]) }}
</x-btn>
</div>
<p class="mt-3 font-mono text-[11px] leading-relaxed text-ink-4">{{ __('release.publish_hint') }}</p>
</div>
@endif
{{-- Yank a public release --}}
<details class="group rounded-lg border border-line bg-surface">
<summary class="flex cursor-pointer list-none items-center justify-between px-4 py-3 font-mono text-[11px] uppercase tracking-wider text-ink-3">
{{ __('release.yank_title') }}
<x-icon name="chevron-right" class="h-4 w-4 shrink-0 transition-transform group-open:rotate-90" />
</summary>
<div class="border-t border-line px-4 py-3">
@forelse ($publicTags as $tag)
<div class="flex items-center justify-between gap-3 py-1.5" wire:key="yank-{{ $tag }}">
<span class="font-mono text-xs text-ink-2">{{ $tag }}</span>
<x-btn variant="danger-soft" wire:click="confirmYank('{{ $tag }}')" wire:loading.attr="disabled">{{ __('release.yank') }}</x-btn>
</div>
@empty
<p class="font-mono text-[11px] text-ink-4">{{ __('release.yank_empty') }}</p>
@endforelse
</div>
</details>
</div>
{{-- Right: live pipeline rail --}}
<x-panel :title="__('release.pipeline')">
@if ($pipeline === null)
<p class="font-mono text-[11px] text-ink-4">{{ __('release.no_beta_running') }}</p>
@else
<ol>
@foreach ($pipeline['steps'] as $s)
@php
$names = [
'tag' => 'Gitea', 'mirror' => 'GitHub-privat', 'ci' => 'CI',
];
$sub = [
'tag' => __('release.pipe_sub_gitea'), 'mirror' => __('release.pipe_sub_mirror'),
'ci' => __('release.pipe_sub_ci'),
];
$detail = match ($s['state']) {
'done' => $s['detail'] ?? __('release.state_done'),
'running' => __('release.state_running'),
'failed' => __('release.state_failed'),
'pending' => __('release.state_pending'),
default => __('release.state_unknown'),
};
$detailClass = match ($s['state']) {
'done' => 'text-online',
'running' => 'text-warning',
'failed' => 'text-offline',
default => 'text-ink-4',
};
@endphp
<li class="flex gap-3" wire:key="pstep-{{ $s['key'] }}">
<div class="flex flex-col items-center">
<span class="grid h-6 w-6 shrink-0 place-items-center rounded-full border border-line bg-inset">
@if ($s['state'] === 'running')
<x-icon name="rotate" class="h-3 w-3 animate-spin text-warning" />
@elseif ($s['state'] === 'done')
<x-status-dot status="online" />
@elseif ($s['state'] === 'failed')
<x-status-dot status="offline" />
@elseif ($s['state'] === 'pending')
<x-status-dot status="pending" />
@else
<span class="h-1.5 w-1.5 rounded-full bg-ink-4"></span>
@endif
</span>
@unless ($loop->last)
<span class="my-1 w-px flex-1 bg-line" aria-hidden="true"></span>
@endunless
</div>
<div @class(['min-w-0', 'pb-5' => ! $loop->last])>
<p class="font-mono text-xs text-ink">{{ $names[$s['key']] }}</p>
<p class="mt-0.5 font-mono text-[11px] text-ink-4">{{ $sub[$s['key']] }}</p>
@if ($s['key'] === 'ci' && $s['url'])
<a href="{{ $s['url'] }}" target="_blank" rel="noopener" class="mt-1 block font-mono text-[11px] text-cyan hover:text-cyan-bright">{{ $detail }}</a>
@else
<p class="mt-1 font-mono text-[11px] {{ $detailClass }}">{{ $detail }}</p>
@endif
</div>
</li>
@endforeach
</ol>
@if ($pipeline['ciRunning'])
<span class="hidden" wire:poll.5s="pollPipeline"></span>
@endif
@endif
</x-panel>
</div>
</div>