refactor: update UI components to shadcn-style
- Refactored select component to align with shadcn design principles. - Updated server status indicator component for consistency with shadcn style. - Modified spinner component to follow shadcn design guidelines. - Enhanced stat card component with shadcn-inspired styles. - Adjusted tab panel component to reflect shadcn design updates. - Revamped table row and table data cell components to match shadcn aesthetics. - Updated table header cell component for improved styling consistency. - Refined table component to align with shadcn design principles. - Enhanced tabs component with shadcn-inspired styles. - Updated toggle component to follow shadcn design guidelines. - Refactored tooltip component to align with shadcn design principles. - Updated server card component to reflect shadcn-inspired design.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Action Toolbar Button Component - TailServer Theme
|
||||
Action Toolbar Button Component - shadcn-style
|
||||
Individual button for VM control actions
|
||||
|
||||
Usage:
|
||||
@@ -24,23 +24,23 @@
|
||||
|
||||
@php
|
||||
$variants = [
|
||||
'default' => 'bg-gradient-to-b from-neutral/5 to-neutral/10 hover:from-neutral/10 hover:to-neutral/15 text-base border border-neutral/20 hover:border-neutral/30 shadow-sm hover:shadow',
|
||||
'primary' => 'bg-gradient-to-b from-primary/10 to-primary/15 hover:from-primary/15 hover:to-primary/20 text-primary border border-primary/20 hover:border-primary/30 shadow-sm hover:shadow-md hover:shadow-primary/10',
|
||||
'success' => 'bg-gradient-to-b from-success/10 to-success/15 hover:from-success/15 hover:to-success/20 text-success border border-success/20 hover:border-success/30 shadow-sm hover:shadow-md hover:shadow-success/10',
|
||||
'warning' => 'bg-gradient-to-b from-warning/10 to-warning/15 hover:from-warning/15 hover:to-warning/20 text-warning border border-warning/20 hover:border-warning/30 shadow-sm hover:shadow-md hover:shadow-warning/10',
|
||||
'danger' => 'bg-gradient-to-b from-danger/10 to-danger/15 hover:from-danger/15 hover:to-danger/20 text-danger border border-danger/20 hover:border-danger/30 shadow-sm hover:shadow-md hover:shadow-danger/10',
|
||||
'default' => 'border bg-background hover:bg-accent hover:text-accent-foreground',
|
||||
'primary' => 'border border-primary/20 bg-primary/10 text-primary hover:bg-primary/20',
|
||||
'success' => 'border border-success/20 bg-success/10 text-success hover:bg-success/20',
|
||||
'warning' => 'border border-warning/20 bg-warning/10 text-warning hover:bg-warning/20',
|
||||
'danger' => 'border border-danger/20 bg-danger/10 text-danger hover:bg-danger/20',
|
||||
];
|
||||
|
||||
$sizes = [
|
||||
'sm' => 'px-2.5 py-1.5 text-xs',
|
||||
'md' => 'px-3.5 py-2 text-sm',
|
||||
'lg' => 'px-5 py-2.5 text-base',
|
||||
'sm' => 'h-8 px-3 text-xs',
|
||||
'md' => 'h-9 px-4 text-sm',
|
||||
'lg' => 'h-10 px-5 text-sm',
|
||||
];
|
||||
|
||||
$iconSizes = [
|
||||
'sm' => 'size-3.5',
|
||||
'md' => 'size-4',
|
||||
'lg' => 'size-5',
|
||||
'lg' => 'size-4',
|
||||
];
|
||||
@endphp
|
||||
|
||||
@@ -53,11 +53,11 @@
|
||||
{{ $attributes->merge([
|
||||
'disabled' => $disabled || $loading,
|
||||
])->class([
|
||||
'inline-flex items-center gap-2 rounded-xl font-medium transition-all duration-200 hover:-translate-y-0.5 active:translate-y-0 active:scale-95',
|
||||
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium ring-offset-background transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
||||
$variants[$variant] ?? $variants['default'],
|
||||
$sizes[$size] ?? $sizes['md'],
|
||||
'opacity-50 cursor-not-allowed hover:translate-y-0' => $disabled,
|
||||
'cursor-wait hover:translate-y-0' => $loading,
|
||||
'pointer-events-none opacity-50' => $disabled,
|
||||
'cursor-wait' => $loading,
|
||||
]) }}
|
||||
>
|
||||
@if($loading)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Alert Component - TailServer Theme
|
||||
Alert Component - TailServer Theme (shadcn-inspired)
|
||||
|
||||
Usage:
|
||||
<x-proxmox::ui.alert>Default alert</x-proxmox::ui.alert>
|
||||
@@ -8,7 +8,7 @@
|
||||
--}}
|
||||
|
||||
@props([
|
||||
'variant' => 'info',
|
||||
'variant' => 'default',
|
||||
'title' => null,
|
||||
'icon' => null,
|
||||
'dismissible' => false,
|
||||
@@ -18,46 +18,36 @@
|
||||
|
||||
@php
|
||||
$variantConfig = [
|
||||
'default' => [
|
||||
'class' => 'bg-card text-card-foreground',
|
||||
'icon' => $icon ?? 'ri-information-line',
|
||||
],
|
||||
'info' => [
|
||||
'bg' => 'bg-gradient-to-r from-info/10 via-info/5 to-transparent',
|
||||
'border' => 'border-info/20',
|
||||
'text' => 'text-info',
|
||||
'class' => 'border-info/20 bg-info/5 text-info [&>svg]:text-info',
|
||||
'icon' => $icon ?? 'ri-information-line',
|
||||
],
|
||||
'success' => [
|
||||
'bg' => 'bg-gradient-to-r from-success/10 via-success/5 to-transparent',
|
||||
'border' => 'border-success/20',
|
||||
'text' => 'text-success',
|
||||
'class' => 'border-success/20 bg-success/5 text-success [&>svg]:text-success',
|
||||
'icon' => $icon ?? 'ri-checkbox-circle-line',
|
||||
],
|
||||
'warning' => [
|
||||
'bg' => 'bg-gradient-to-r from-warning/10 via-warning/5 to-transparent',
|
||||
'border' => 'border-warning/20',
|
||||
'text' => 'text-warning',
|
||||
'class' => 'border-warning/20 bg-warning/5 text-warning [&>svg]:text-warning',
|
||||
'icon' => $icon ?? 'ri-error-warning-line',
|
||||
],
|
||||
'danger' => [
|
||||
'bg' => 'bg-gradient-to-r from-danger/10 via-danger/5 to-transparent',
|
||||
'border' => 'border-danger/20',
|
||||
'text' => 'text-danger',
|
||||
'class' => 'border-danger/20 bg-danger/5 text-danger [&>svg]:text-danger',
|
||||
'icon' => $icon ?? 'ri-error-warning-fill',
|
||||
],
|
||||
'default' => [
|
||||
'bg' => 'bg-gradient-to-r from-neutral/10 via-neutral/5 to-transparent',
|
||||
'border' => 'border-neutral/20',
|
||||
'text' => 'text-base/70',
|
||||
'icon' => $icon ?? 'ri-information-line',
|
||||
],
|
||||
];
|
||||
|
||||
$config = $variantConfig[$variant] ?? $variantConfig['default'];
|
||||
@endphp
|
||||
|
||||
<div
|
||||
role="alert"
|
||||
{{ $attributes->class([
|
||||
'rounded-xl p-4 border',
|
||||
$config['bg'],
|
||||
$config['border'],
|
||||
'relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5',
|
||||
$config['class'],
|
||||
]) }}
|
||||
@if($dismissible || $collapsible)
|
||||
x-data="{
|
||||
@@ -65,56 +55,54 @@
|
||||
@if($collapsible) collapsed: {{ $collapsed ? 'true' : 'false' }} @endif
|
||||
}"
|
||||
x-show="show"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0 scale-95"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-200"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-95"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0"
|
||||
x-transition:enter-end="opacity-100"
|
||||
x-transition:leave="transition ease-in duration-100"
|
||||
x-transition:leave-start="opacity-100"
|
||||
x-transition:leave-end="opacity-0"
|
||||
@endif
|
||||
>
|
||||
<div class="flex items-start gap-3">
|
||||
<x-dynamic-component :component="$config['icon']" class="size-5 {{ $config['text'] }} flex-shrink-0 mt-0.5" />
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
@if($title)
|
||||
<h4 class="text-sm font-semibold text-base">{{ $title }}</h4>
|
||||
@endif
|
||||
<div class="flex items-center gap-1 ml-auto">
|
||||
@if($collapsible)
|
||||
<button
|
||||
type="button"
|
||||
@click="collapsed = !collapsed"
|
||||
class="p-1.5 rounded-lg hover:bg-black/10 transition-all duration-200 {{ $config['text'] }}"
|
||||
>
|
||||
<x-ri-arrow-down-s-line
|
||||
class="size-4 transition-transform duration-300 ease-out"
|
||||
x-bind:class="collapsed ? 'rotate-180' : ''"
|
||||
/>
|
||||
</button>
|
||||
@endif
|
||||
@if($dismissible)
|
||||
<button
|
||||
type="button"
|
||||
@click="show = false"
|
||||
class="p-1.5 rounded-lg hover:bg-black/10 hover:rotate-90 transition-all duration-200 {{ $config['text'] }}"
|
||||
>
|
||||
<x-ri-close-line class="size-4" />
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="text-sm text-base/70 leading-relaxed @if($title) mt-1 @endif"
|
||||
@if($collapsible)
|
||||
x-show="!collapsed"
|
||||
x-transition:enter="transition ease-out duration-200"
|
||||
x-transition:enter-start="opacity-0 -translate-y-2"
|
||||
x-transition:enter-end="opacity-100 translate-y-0"
|
||||
@endif
|
||||
>
|
||||
{{ $slot }}
|
||||
</div>
|
||||
<x-dynamic-component :component="$config['icon']" class="size-4" />
|
||||
<div class="flex-1 min-w-0">
|
||||
@if($title)
|
||||
<h5 class="mb-1 font-medium leading-none tracking-tight">{{ $title }}</h5>
|
||||
@endif
|
||||
<div
|
||||
class="text-sm [&_p]:leading-relaxed opacity-90"
|
||||
@if($collapsible)
|
||||
x-show="!collapsed"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0"
|
||||
x-transition:enter-end="opacity-100"
|
||||
@endif
|
||||
>
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</div>
|
||||
@if($dismissible || $collapsible)
|
||||
<div class="absolute top-3 right-3 flex items-center gap-1">
|
||||
@if($collapsible)
|
||||
<button
|
||||
type="button"
|
||||
@click="collapsed = !collapsed"
|
||||
class="p-1 rounded hover:bg-black/5 transition-colors"
|
||||
>
|
||||
<x-ri-arrow-down-s-line
|
||||
class="size-4 transition-transform duration-150"
|
||||
x-bind:class="collapsed ? 'rotate-180' : ''"
|
||||
/>
|
||||
</button>
|
||||
@endif
|
||||
@if($dismissible)
|
||||
<button
|
||||
type="button"
|
||||
@click="show = false"
|
||||
class="p-1 rounded hover:bg-black/5 transition-colors"
|
||||
>
|
||||
<x-ri-close-line class="size-4" />
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Resource Allocation Card Component - TailServer Theme
|
||||
Resource Allocation Card Component - shadcn-style
|
||||
For displaying CPU, Memory, and other resource allocation vs entitlement
|
||||
|
||||
Usage:
|
||||
@@ -26,26 +26,23 @@
|
||||
'iconColor' => 'text-primary',
|
||||
])
|
||||
|
||||
<div {{ $attributes->class(['group relative border border-neutral/20 rounded-xl p-4 bg-background hover:border-neutral/30 hover:shadow-sm transition-all duration-200']) }}>
|
||||
{{-- Subtle gradient overlay on hover --}}
|
||||
<div class="absolute inset-0 rounded-xl bg-gradient-to-br from-primary/[0.02] to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300 pointer-events-none"></div>
|
||||
|
||||
<div class="relative flex flex-col gap-3">
|
||||
<div {{ $attributes->class(['rounded-xl border bg-card p-4 shadow-sm transition-colors duration-150 hover:bg-accent/50']) }}>
|
||||
<div class="flex flex-col gap-3">
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div class="flex items-start gap-3">
|
||||
@if($icon)
|
||||
<div class="flex-shrink-0 w-9 h-9 rounded-lg bg-primary/10 flex items-center justify-center">
|
||||
<x-dynamic-component :component="$icon" class="size-4.5 {{ $iconColor }}" />
|
||||
<div class="flex-shrink-0 size-9 rounded-lg bg-primary/10 flex items-center justify-center">
|
||||
<x-dynamic-component :component="$icon" class="size-4 {{ $iconColor }}" />
|
||||
</div>
|
||||
@endif
|
||||
<div class="min-w-0">
|
||||
<p class="text-[11px] font-semibold tracking-wider uppercase text-base/50">{{ $label }}</p>
|
||||
<p class="text-xl font-bold text-base leading-tight mt-0.5">{{ $usageSummary }}</p>
|
||||
<p class="text-xs text-base/50 mt-0.5">{{ $entitlementLabel }}</p>
|
||||
<div class="min-w-0 space-y-0.5">
|
||||
<p class="text-xs font-medium text-muted-foreground">{{ $label }}</p>
|
||||
<p class="text-xl font-bold tracking-tight">{{ $usageSummary }}</p>
|
||||
<p class="text-xs text-muted-foreground">{{ $entitlementLabel }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@if($badge)
|
||||
<span class="flex-shrink-0 text-[10px] font-bold px-2 py-1 rounded-full {{ $badge['class'] ?? 'text-base/60 bg-neutral/10' }}">
|
||||
<span class="flex-shrink-0 text-xs font-medium px-2 py-0.5 rounded-full {{ $badge['class'] ?? 'text-muted-foreground bg-muted' }}">
|
||||
{{ $badge['label'] ?? '' }}
|
||||
</span>
|
||||
@endif
|
||||
@@ -54,12 +51,12 @@
|
||||
@if(!is_null($percent))
|
||||
<div class="space-y-1.5">
|
||||
<div class="flex items-center justify-between text-xs">
|
||||
<span class="text-base/50">Usage</span>
|
||||
<span class="font-semibold text-base/70">{{ $percent }}%</span>
|
||||
<span class="text-muted-foreground">Usage</span>
|
||||
<span class="font-medium">{{ $percent }}%</span>
|
||||
</div>
|
||||
<div class="h-1.5 rounded-full bg-neutral/10 overflow-hidden">
|
||||
<div class="h-1.5 rounded-full bg-muted overflow-hidden">
|
||||
<div
|
||||
class="h-full rounded-full transition-all duration-500 ease-out"
|
||||
class="h-full rounded-full transition-all duration-300"
|
||||
style="width: {{ min($percent, 130) }}%; background-image: {{ $gradient }};"
|
||||
></div>
|
||||
</div>
|
||||
@@ -67,7 +64,7 @@
|
||||
@endif
|
||||
|
||||
@if($description)
|
||||
<p class="text-[11px] text-base/40 leading-relaxed">{{ $description }}</p>
|
||||
<p class="text-xs text-muted-foreground/70">{{ $description }}</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Badge Component - TailServer Theme
|
||||
Badge Component - TailServer Theme (shadcn-inspired)
|
||||
|
||||
Usage:
|
||||
<x-proxmox::ui.badge>Default</x-proxmox::ui.badge>
|
||||
@@ -18,31 +18,31 @@
|
||||
])
|
||||
|
||||
@php
|
||||
$baseClasses = 'inline-flex items-center font-semibold rounded-full transition-colors';
|
||||
$baseClasses = 'inline-flex items-center rounded-md border font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2';
|
||||
|
||||
$variantClasses = [
|
||||
'default' => $outline ? 'bg-transparent border border-neutral/30 text-base/70' : 'bg-neutral/10 text-base/70',
|
||||
'primary' => $outline ? 'bg-transparent border border-primary/40 text-primary' : 'bg-primary/10 text-primary',
|
||||
'secondary' => $outline ? 'bg-transparent border border-secondary/40 text-secondary' : 'bg-secondary/10 text-secondary',
|
||||
'success' => $outline ? 'bg-transparent border border-success/40 text-success' : 'bg-success/10 text-success',
|
||||
'warning' => $outline ? 'bg-transparent border border-warning/40 text-warning' : 'bg-warning/10 text-warning',
|
||||
'danger' => $outline ? 'bg-transparent border border-danger/40 text-danger' : 'bg-danger/10 text-danger',
|
||||
'info' => $outline ? 'bg-transparent border border-info/40 text-info' : 'bg-info/10 text-info',
|
||||
'muted' => 'bg-neutral/5 text-base/40',
|
||||
'default' => 'border-transparent bg-primary text-primary-foreground shadow',
|
||||
'secondary' => 'border-transparent bg-secondary text-secondary-foreground',
|
||||
'success' => $outline ? 'border-success/40 text-success bg-transparent' : 'border-transparent bg-success/15 text-success',
|
||||
'warning' => $outline ? 'border-warning/40 text-warning bg-transparent' : 'border-transparent bg-warning/15 text-warning',
|
||||
'danger' => $outline ? 'border-danger/40 text-danger bg-transparent' : 'border-transparent bg-danger/15 text-danger',
|
||||
'info' => $outline ? 'border-info/40 text-info bg-transparent' : 'border-transparent bg-info/15 text-info',
|
||||
'outline' => 'text-foreground border-neutral/20',
|
||||
'muted' => 'border-transparent bg-muted text-muted-foreground',
|
||||
];
|
||||
|
||||
$sizeClasses = [
|
||||
'xs' => 'px-1.5 py-0.5 text-[10px] leading-tight',
|
||||
'sm' => 'px-2 py-0.5 text-[11px] leading-tight',
|
||||
'md' => 'px-2.5 py-1 text-xs',
|
||||
'lg' => 'px-3 py-1.5 text-sm',
|
||||
'xs' => 'px-1.5 py-0.5 text-[10px]',
|
||||
'sm' => 'px-2 py-0.5 text-[11px]',
|
||||
'md' => 'px-2.5 py-0.5 text-xs',
|
||||
'lg' => 'px-3 py-1 text-sm',
|
||||
];
|
||||
|
||||
$iconSizes = [
|
||||
'xs' => 'size-2.5',
|
||||
'sm' => 'size-3',
|
||||
'md' => 'size-3.5',
|
||||
'lg' => 'size-4',
|
||||
'md' => 'size-3',
|
||||
'lg' => 'size-3.5',
|
||||
];
|
||||
|
||||
$dotSizes = [
|
||||
@@ -62,7 +62,7 @@
|
||||
$dotSize = $dotSizes[$size] ?? $dotSizes['md'];
|
||||
@endphp
|
||||
|
||||
<span {{ $attributes->class([$classes, 'gap-1.5' => $icon || $dot]) }}>
|
||||
<span {{ $attributes->class([$classes, 'gap-1' => $icon || $dot]) }}>
|
||||
@if($dot)
|
||||
<span class="relative flex {{ $dotSize }}">
|
||||
@if($pulse)
|
||||
@@ -72,7 +72,7 @@
|
||||
</span>
|
||||
@endif
|
||||
@if($icon)
|
||||
<x-dynamic-component :component="$icon" class="{{ $iconSize }} opacity-80" />
|
||||
<x-dynamic-component :component="$icon" class="{{ $iconSize }}" />
|
||||
@endif
|
||||
{{ $slot }}
|
||||
</span>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Button Component - TailServer Theme
|
||||
Button Component - TailServer Theme (shadcn-inspired)
|
||||
|
||||
Usage:
|
||||
<x-proxmox::ui.button>Click me</x-proxmox::ui.button>
|
||||
@@ -24,33 +24,36 @@
|
||||
@php
|
||||
$tag = $href ? 'a' : 'button';
|
||||
|
||||
$baseClasses = 'group relative inline-flex items-center justify-center gap-2 rounded-lg font-semibold transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-background disabled:opacity-50 disabled:cursor-not-allowed overflow-hidden';
|
||||
$baseClasses = 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50';
|
||||
|
||||
$variantClasses = [
|
||||
'primary' => 'bg-primary text-white border border-primary hover:bg-primary/90 hover:shadow-md hover:shadow-primary/20 active:scale-[0.98] focus:ring-primary/50',
|
||||
'secondary' => 'bg-background-secondary text-base border border-neutral/30 hover:bg-background-secondary/80 hover:border-neutral/50 active:scale-[0.98] focus:ring-neutral/30',
|
||||
'outline' => 'bg-transparent text-base border border-neutral/30 hover:bg-neutral/10 hover:border-neutral/50 focus:ring-neutral/30',
|
||||
'ghost' => 'bg-transparent text-base hover:bg-neutral/10 focus:ring-neutral/20',
|
||||
'danger' => 'bg-danger text-white border border-danger hover:bg-danger/90 hover:shadow-md hover:shadow-danger/20 active:scale-[0.98] focus:ring-danger/50',
|
||||
'success' => 'bg-success text-white border border-success hover:bg-success/90 hover:shadow-md hover:shadow-success/20 active:scale-[0.98] focus:ring-success/50',
|
||||
'warning' => 'bg-warning text-white border border-warning hover:bg-warning/90 hover:shadow-md hover:shadow-warning/20 active:scale-[0.98] focus:ring-warning/50',
|
||||
'info' => 'bg-info text-white border border-info hover:bg-info/90 hover:shadow-md hover:shadow-info/20 active:scale-[0.98] focus:ring-info/50',
|
||||
'primary' => 'bg-primary text-primary-foreground shadow hover:bg-primary/90',
|
||||
'secondary' => 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
|
||||
'outline' => 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
|
||||
'ghost' => 'hover:bg-accent hover:text-accent-foreground',
|
||||
'danger' => 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
|
||||
'success' => 'bg-success text-white shadow-sm hover:bg-success/90',
|
||||
'warning' => 'bg-warning text-white shadow-sm hover:bg-warning/90',
|
||||
'info' => 'bg-info text-white shadow-sm hover:bg-info/90',
|
||||
'link' => 'text-primary underline-offset-4 hover:underline',
|
||||
];
|
||||
|
||||
$sizeClasses = [
|
||||
'xs' => 'px-2 py-1 text-xs',
|
||||
'sm' => 'px-2.5 py-1.5 text-sm',
|
||||
'md' => 'px-3.5 py-2 text-sm',
|
||||
'lg' => 'px-4 py-2.5 text-base',
|
||||
'xl' => 'px-5 py-3 text-lg',
|
||||
'xs' => 'h-7 px-2 text-xs',
|
||||
'sm' => 'h-8 px-3 text-xs',
|
||||
'md' => 'h-9 px-4 py-2',
|
||||
'lg' => 'h-10 px-8',
|
||||
'xl' => 'h-11 px-10 text-base',
|
||||
'icon' => 'h-9 w-9',
|
||||
];
|
||||
|
||||
$iconSizes = [
|
||||
'xs' => 'size-3',
|
||||
'sm' => 'size-3.5',
|
||||
'md' => 'size-4',
|
||||
'lg' => 'size-5',
|
||||
'xl' => 'size-6',
|
||||
'lg' => 'size-4',
|
||||
'xl' => 'size-5',
|
||||
'icon' => 'size-4',
|
||||
];
|
||||
|
||||
$classes = collect([
|
||||
@@ -69,24 +72,19 @@
|
||||
@if($disabled || $loading) disabled @endif
|
||||
{{ $attributes->class([$classes]) }}
|
||||
>
|
||||
{{-- Subtle shine effect on hover --}}
|
||||
<span class="absolute inset-0 -translate-x-full group-hover:translate-x-full transition-transform duration-700 bg-gradient-to-r from-transparent via-white/10 to-transparent pointer-events-none"></span>
|
||||
|
||||
<span class="relative inline-flex items-center gap-2">
|
||||
@if($loading)
|
||||
<svg class="animate-spin {{ $iconSize }}" 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" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
<span>{{ $loadingText ?? $slot }}</span>
|
||||
@else
|
||||
@if($icon)
|
||||
<x-dynamic-component :component="$icon" class="{{ $iconSize }} opacity-80 group-hover:opacity-100 transition-opacity" />
|
||||
@endif
|
||||
{{ $slot }}
|
||||
@if($iconRight)
|
||||
<x-dynamic-component :component="$iconRight" class="{{ $iconSize }} opacity-80 group-hover:opacity-100 transition-opacity" />
|
||||
@endif
|
||||
@if($loading)
|
||||
<svg class="animate-spin {{ $iconSize }}" 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" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
<span>{{ $loadingText ?? $slot }}</span>
|
||||
@else
|
||||
@if($icon)
|
||||
<x-dynamic-component :component="$icon" class="{{ $iconSize }}" />
|
||||
@endif
|
||||
</span>
|
||||
{{ $slot }}
|
||||
@if($iconRight)
|
||||
<x-dynamic-component :component="$iconRight" class="{{ $iconSize }}" />
|
||||
@endif
|
||||
@endif
|
||||
</{{ $tag }}>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Card Component - TailServer Theme
|
||||
Card Component - TailServer Theme (shadcn-inspired)
|
||||
|
||||
Usage:
|
||||
<x-proxmox::ui.card>Content</x-proxmox::ui.card>
|
||||
@@ -22,42 +22,35 @@
|
||||
'collapsed' => false,
|
||||
])
|
||||
|
||||
@php
|
||||
$cardClasses = 'relative flex flex-col rounded-xl border border-neutral/20 bg-background-secondary shadow-sm';
|
||||
if ($hover) {
|
||||
$cardClasses .= ' hover:bg-background-secondary/90 hover:border-neutral/30 hover:shadow-md transition-all duration-200';
|
||||
}
|
||||
if ($clickable) {
|
||||
$cardClasses .= ' cursor-pointer';
|
||||
}
|
||||
@endphp
|
||||
|
||||
<div
|
||||
{{ $attributes->class([$cardClasses]) }}
|
||||
{{ $attributes->class([
|
||||
'flex flex-col gap-6 rounded-xl border border-neutral/15 bg-card text-card-foreground shadow-sm',
|
||||
'hover:shadow-md hover:border-neutral/25 transition-shadow duration-150' => $hover || $clickable,
|
||||
'cursor-pointer' => $clickable,
|
||||
]) }}
|
||||
@if($collapsible)
|
||||
x-data="{ isCollapsed: {{ $collapsed ? 'true' : 'false' }} }"
|
||||
@endif
|
||||
>
|
||||
{{-- Subtle gradient overlay for depth --}}
|
||||
<div class="absolute inset-0 rounded-xl bg-gradient-to-b from-white/[0.02] to-transparent pointer-events-none"></div>
|
||||
@if($title || $actions)
|
||||
<div class="flex flex-col items-start justify-between gap-4 border-b border-neutral/20 p-5 sm:flex-row sm:items-center">
|
||||
<div class="@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6">
|
||||
<div class="flex items-center gap-3">
|
||||
@if($icon)
|
||||
<div class="flex items-center justify-center w-10 h-10 rounded-lg bg-primary/10">
|
||||
<x-dynamic-component :component="$icon" class="size-5 {{ $iconColor }}" />
|
||||
<div class="flex items-center justify-center size-9 rounded-lg bg-primary/10">
|
||||
<x-dynamic-component :component="$icon" class="size-4 {{ $iconColor }}" />
|
||||
</div>
|
||||
@endif
|
||||
<div>
|
||||
<div class="space-y-1">
|
||||
@if($title)
|
||||
<h3 class="text-lg font-semibold text-base">{{ $title }}</h3>
|
||||
<h3 class="leading-none font-semibold">{{ $title }}</h3>
|
||||
@endif
|
||||
@if($subtitle)
|
||||
<p class="text-sm text-base/60 mt-0.5">{{ $subtitle }}</p>
|
||||
<p class="text-sm text-muted-foreground">{{ $subtitle }}</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
@if($actions || $collapsible)
|
||||
<div class="col-start-2 row-span-2 row-start-1 self-start justify-self-end flex items-center gap-2">
|
||||
@if($actions)
|
||||
{{ $actions }}
|
||||
@endif
|
||||
@@ -65,38 +58,39 @@
|
||||
<button
|
||||
type="button"
|
||||
@click="isCollapsed = !isCollapsed"
|
||||
class="p-2 rounded-lg hover:bg-neutral/10 transition-colors"
|
||||
class="p-1.5 rounded-md hover:bg-neutral/10 transition-colors"
|
||||
>
|
||||
<x-ri-arrow-down-s-line
|
||||
class="size-5 text-base/50 transition-transform duration-200"
|
||||
class="size-4 text-muted-foreground transition-transform duration-150"
|
||||
x-bind:class="isCollapsed ? 'rotate-180' : ''"
|
||||
/>
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div
|
||||
@class([
|
||||
'flex-1',
|
||||
'p-5' => $padding,
|
||||
'px-6',
|
||||
'py-6' => $padding && !$title,
|
||||
])
|
||||
@if($collapsible)
|
||||
x-show="!isCollapsed"
|
||||
x-transition:enter="transition ease-out duration-200"
|
||||
x-transition:enter-start="opacity-0 -translate-y-2"
|
||||
x-transition:enter-end="opacity-100 translate-y-0"
|
||||
x-transition:leave="transition ease-in duration-150"
|
||||
x-transition:leave-start="opacity-100 translate-y-0"
|
||||
x-transition:leave-end="opacity-0 -translate-y-2"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0"
|
||||
x-transition:enter-end="opacity-100"
|
||||
x-transition:leave="transition ease-in duration-100"
|
||||
x-transition:leave-start="opacity-100"
|
||||
x-transition:leave-end="opacity-0"
|
||||
@endif
|
||||
>
|
||||
{{ $slot }}
|
||||
</div>
|
||||
|
||||
@if($footer)
|
||||
<div class="border-t border-neutral/20 px-5 py-3 {{ $footerClass }}">
|
||||
<div class="flex items-center px-6 [.border-t]:pt-6 {{ $footerClass }}">
|
||||
{{ $footer }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Confirm Dialog Component - TailServer Theme
|
||||
Confirm Dialog Component - shadcn-style
|
||||
For dangerous actions like delete, stop, etc.
|
||||
|
||||
Usage:
|
||||
@@ -28,19 +28,19 @@
|
||||
'icon' => 'ri-error-warning-line',
|
||||
'iconBg' => 'bg-danger/10',
|
||||
'iconColor' => 'text-danger',
|
||||
'buttonBg' => 'bg-danger hover:bg-danger/90',
|
||||
'buttonClass' => 'bg-danger text-danger-foreground hover:bg-danger/90',
|
||||
],
|
||||
'warning' => [
|
||||
'icon' => 'ri-alert-line',
|
||||
'iconBg' => 'bg-warning/10',
|
||||
'iconColor' => 'text-warning',
|
||||
'buttonBg' => 'bg-warning hover:bg-warning/90',
|
||||
'buttonClass' => 'bg-warning text-warning-foreground hover:bg-warning/90',
|
||||
],
|
||||
'primary' => [
|
||||
'icon' => 'ri-question-line',
|
||||
'iconBg' => 'bg-primary/10',
|
||||
'iconColor' => 'text-primary',
|
||||
'buttonBg' => 'bg-primary hover:bg-primary/90',
|
||||
'buttonClass' => 'bg-primary text-primary-foreground hover:bg-primary/90',
|
||||
],
|
||||
];
|
||||
|
||||
@@ -59,45 +59,45 @@
|
||||
<!-- Backdrop -->
|
||||
<div
|
||||
x-show="open"
|
||||
x-transition:enter="ease-out duration-300"
|
||||
x-transition:enter="ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0"
|
||||
x-transition:enter-end="opacity-100"
|
||||
x-transition:leave="ease-in duration-200"
|
||||
x-transition:leave="ease-in duration-100"
|
||||
x-transition:leave-start="opacity-100"
|
||||
x-transition:leave-end="opacity-0"
|
||||
@click="open = false"
|
||||
class="fixed inset-0 bg-black/60 backdrop-blur-md"
|
||||
class="fixed inset-0 bg-black/50 backdrop-blur-sm"
|
||||
></div>
|
||||
|
||||
<!-- Dialog -->
|
||||
<div
|
||||
x-show="open"
|
||||
x-transition:enter="ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0 scale-95 translate-y-4"
|
||||
x-transition:enter-end="opacity-100 scale-100 translate-y-0"
|
||||
x-transition:leave="ease-in duration-200"
|
||||
x-transition:leave-start="opacity-100 scale-100 translate-y-0"
|
||||
x-transition:leave-end="opacity-0 scale-95 translate-y-4"
|
||||
class="relative bg-background rounded-2xl shadow-2xl ring-1 ring-neutral/10 w-full max-w-md p-6"
|
||||
x-transition:enter="ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 scale-95"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="ease-in duration-100"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-95"
|
||||
class="relative rounded-lg border bg-background shadow-lg w-full max-w-md p-6"
|
||||
>
|
||||
<div class="flex flex-col items-center text-center">
|
||||
<!-- Icon -->
|
||||
<div class="p-4 rounded-2xl {{ $config['iconBg'] }} mb-4 shadow-lg">
|
||||
<x-dynamic-component :component="$config['icon']" class="size-8 {{ $config['iconColor'] }}" />
|
||||
<div class="p-3 rounded-full {{ $config['iconBg'] }} mb-4">
|
||||
<x-dynamic-component :component="$config['icon']" class="size-6 {{ $config['iconColor'] }}" />
|
||||
</div>
|
||||
|
||||
<!-- Title -->
|
||||
<h3 class="text-lg font-semibold text-base mb-2">{{ $title }}</h3>
|
||||
<h3 class="text-lg font-semibold leading-none tracking-tight mb-2">{{ $title }}</h3>
|
||||
|
||||
<!-- Message -->
|
||||
<p class="text-sm text-base/60 mb-6">{{ $message }}</p>
|
||||
<p class="text-sm text-muted-foreground mb-6">{{ $message }}</p>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="flex gap-3 w-full">
|
||||
<div class="flex gap-2 w-full">
|
||||
<button
|
||||
type="button"
|
||||
@click="open = false"
|
||||
class="flex-1 px-4 py-2.5 bg-neutral/5 hover:bg-neutral/15 rounded-xl text-sm font-medium text-base transition-all duration-200 hover:shadow-sm active:scale-95"
|
||||
class="flex-1 inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors duration-150 h-9 px-4 py-2 border bg-background hover:bg-accent hover:text-accent-foreground"
|
||||
>
|
||||
{{ $cancelLabel }}
|
||||
</button>
|
||||
@@ -105,7 +105,7 @@
|
||||
type="button"
|
||||
@click="open = false"
|
||||
{{ $attributes->whereStartsWith('wire:click') }}
|
||||
class="flex-1 px-4 py-2.5 {{ $config['buttonBg'] }} rounded-xl text-sm font-medium text-white transition-all duration-200 shadow-md hover:shadow-lg active:scale-95"
|
||||
class="flex-1 inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors duration-150 h-9 px-4 py-2 {{ $config['buttonClass'] }}"
|
||||
>
|
||||
{{ $confirmLabel }}
|
||||
</button>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Copy Button Component - TailServer Theme
|
||||
Copy Button Component - shadcn-style
|
||||
Small inline button to copy text to clipboard
|
||||
|
||||
Usage:
|
||||
@@ -17,7 +17,7 @@
|
||||
copyToClipboard() {
|
||||
navigator.clipboard.writeText(this.copyText);
|
||||
this.copied = true;
|
||||
setTimeout(() => this.copied = false, 3000);
|
||||
setTimeout(() => this.copied = false, 2000);
|
||||
}
|
||||
}"
|
||||
class="inline-flex items-center"
|
||||
@@ -26,7 +26,7 @@
|
||||
<button
|
||||
@click="copyToClipboard()"
|
||||
type="button"
|
||||
class="text-base/50 hover:text-base/70 transition-colors relative inline-flex items-center justify-center size-4"
|
||||
class="text-muted-foreground hover:text-foreground transition-colors duration-150 relative inline-flex items-center justify-center size-4"
|
||||
title="Copy to clipboard"
|
||||
>
|
||||
<x-ri-check-fill x-show="copied" x-transition x-cloak class="absolute size-4 text-success" />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Copyable Text Component - TailServer Theme
|
||||
Copyable Text Component - shadcn-style
|
||||
Text field with copy to clipboard functionality
|
||||
|
||||
Usage:
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
<div class="space-y-2">
|
||||
@if($label)
|
||||
<label class="text-sm font-semibold text-base/70">{{ $label }}</label>
|
||||
<label class="text-sm font-medium leading-none">{{ $label }}</label>
|
||||
@endif
|
||||
<div
|
||||
x-data="{
|
||||
@@ -24,17 +24,17 @@
|
||||
copyToClipboard() {
|
||||
navigator.clipboard.writeText(this.copyText);
|
||||
this.copied = true;
|
||||
setTimeout(() => this.copied = false, 3000);
|
||||
setTimeout(() => this.copied = false, 2000);
|
||||
}
|
||||
}"
|
||||
class="group flex items-center gap-2 border border-neutral/15 rounded-xl bg-gradient-to-r from-neutral/5 to-transparent px-3.5 py-2.5 text-sm text-base hover:border-neutral/30 transition-all duration-200 shadow-sm hover:shadow"
|
||||
class="flex items-center gap-2 rounded-md border bg-transparent px-3 py-2 text-sm transition-colors duration-150"
|
||||
>
|
||||
@if($masked)
|
||||
<span class="truncate flex-1 font-mono" x-text="showValue ? copyText : '••••••••••••'"></span>
|
||||
<button
|
||||
@click="showValue = !showValue"
|
||||
type="button"
|
||||
class="text-base/40 hover:text-primary transition-colors duration-200"
|
||||
class="text-muted-foreground hover:text-foreground transition-colors duration-150"
|
||||
:aria-label="showValue ? 'Hide value' : 'Show value'"
|
||||
>
|
||||
<x-ri-eye-line x-show="!showValue" class="size-4" />
|
||||
@@ -46,7 +46,7 @@
|
||||
<button
|
||||
@click="copyToClipboard()"
|
||||
type="button"
|
||||
class="text-base/40 hover:text-primary transition-all duration-200 relative inline-flex items-center justify-center size-5 hover:scale-110 active:scale-95"
|
||||
class="text-muted-foreground hover:text-foreground transition-colors duration-150 relative inline-flex items-center justify-center size-4"
|
||||
aria-label="Copy to clipboard"
|
||||
>
|
||||
<x-ri-check-fill x-show="copied" x-transition x-cloak class="absolute size-4 text-success" />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Dropdown Divider Component - TailServer Theme
|
||||
Dropdown Divider Component - shadcn-style
|
||||
--}}
|
||||
|
||||
<hr class="my-2 border-neutral/20" />
|
||||
<div class="-mx-1 my-1 h-px bg-border"></div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Dropdown Item Component - TailServer Theme
|
||||
Dropdown Item Component - shadcn-style
|
||||
--}}
|
||||
|
||||
@props([
|
||||
@@ -13,9 +13,9 @@
|
||||
$tag = $href ? 'a' : 'button';
|
||||
|
||||
$variantClasses = [
|
||||
'default' => 'text-base/70 hover:bg-gradient-to-r hover:from-neutral/10 hover:to-transparent hover:text-base',
|
||||
'danger' => 'text-danger hover:bg-gradient-to-r hover:from-danger/10 hover:to-transparent',
|
||||
'success' => 'text-success hover:bg-gradient-to-r hover:from-success/10 hover:to-transparent',
|
||||
'default' => 'text-foreground hover:bg-accent hover:text-accent-foreground',
|
||||
'danger' => 'text-danger focus:bg-danger/10 focus:text-danger',
|
||||
'success' => 'text-success focus:bg-success/10 focus:text-success',
|
||||
];
|
||||
|
||||
$variantClass = $variantClasses[$variant] ?? $variantClasses['default'];
|
||||
@@ -26,13 +26,13 @@
|
||||
@if($tag === 'button') type="button" @endif
|
||||
@if($disabled) disabled @endif
|
||||
{{ $attributes->class([
|
||||
'group flex w-full items-center gap-2.5 px-4 py-2.5 text-sm font-medium transition-all duration-150',
|
||||
'relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors duration-100 w-full',
|
||||
$variantClass,
|
||||
'opacity-50 cursor-not-allowed' => $disabled,
|
||||
'pointer-events-none opacity-50' => $disabled,
|
||||
]) }}
|
||||
>
|
||||
@if($icon)
|
||||
<x-dynamic-component :component="$icon" class="size-4 opacity-50 group-hover:opacity-80 transition-opacity" />
|
||||
<x-dynamic-component :component="$icon" class="size-4" />
|
||||
@endif
|
||||
<span class="flex-1 text-left">{{ $slot }}</span>
|
||||
</{{ $tag }}>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Dropdown Component - TailServer Theme
|
||||
Dropdown Component - shadcn-style
|
||||
|
||||
Usage:
|
||||
<x-proxmox::ui.dropdown>
|
||||
@@ -44,17 +44,17 @@
|
||||
|
||||
<div
|
||||
x-show="open"
|
||||
x-transition:enter="transition ease-out duration-200"
|
||||
x-transition:enter-start="opacity-0 scale-95 -translate-y-1"
|
||||
x-transition:enter-end="opacity-100 scale-100 translate-y-0"
|
||||
x-transition:leave="transition ease-in duration-100"
|
||||
x-transition:leave-start="opacity-100 scale-100 translate-y-0"
|
||||
x-transition:leave-end="opacity-0 scale-95 -translate-y-1"
|
||||
class="absolute z-50 mt-2 {{ $widthClasses[$width] ?? 'w-48' }} {{ $alignClasses[$align] ?? $alignClasses['right'] }}"
|
||||
x-transition:enter="transition ease-out duration-100"
|
||||
x-transition:enter-start="opacity-0 scale-95"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-75"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-95"
|
||||
class="absolute z-50 mt-1.5 {{ $widthClasses[$width] ?? 'w-48' }} {{ $alignClasses[$align] ?? $alignClasses['right'] }}"
|
||||
style="display: none;"
|
||||
@click="open = false"
|
||||
>
|
||||
<div class="rounded-xl bg-background-secondary py-2 ring-1 ring-neutral/10 shadow-2xl border border-neutral/5 backdrop-blur-xl {{ $contentClasses }}">
|
||||
<div class="rounded-md border bg-popover p-1 text-popover-foreground shadow-md {{ $contentClasses }}">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Empty State Component - TailServer Theme
|
||||
Empty State Component - shadcn-style
|
||||
|
||||
Usage:
|
||||
<x-proxmox::ui.empty-state title="No servers found" />
|
||||
@@ -29,25 +29,23 @@
|
||||
>
|
||||
@if($icon)
|
||||
<div @class([
|
||||
'relative flex items-center justify-center rounded-full',
|
||||
'mb-4 w-16 h-16 bg-neutral/5' => !$compact,
|
||||
'mb-3 w-12 h-12 bg-neutral/5' => $compact,
|
||||
'flex items-center justify-center rounded-full bg-muted',
|
||||
'mb-4 size-14' => !$compact,
|
||||
'mb-3 size-10' => $compact,
|
||||
])>
|
||||
{{-- Decorative ring --}}
|
||||
<div class="absolute inset-0 rounded-full border border-dashed border-neutral/20"></div>
|
||||
<x-dynamic-component
|
||||
:component="$icon"
|
||||
@class([
|
||||
$iconClass ?? 'text-base/25',
|
||||
'size-8' => !$compact,
|
||||
'size-6' => $compact,
|
||||
$iconClass ?? 'text-muted-foreground',
|
||||
'size-7' => !$compact,
|
||||
'size-5' => $compact,
|
||||
])
|
||||
/>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<h3 @class([
|
||||
'font-semibold text-base/80',
|
||||
'font-semibold tracking-tight',
|
||||
'text-base' => !$compact,
|
||||
'text-sm' => $compact,
|
||||
])>
|
||||
@@ -56,7 +54,7 @@
|
||||
|
||||
@if($description)
|
||||
<p @class([
|
||||
'text-base/50 max-w-sm leading-relaxed',
|
||||
'text-muted-foreground max-w-sm',
|
||||
'mt-1.5 text-sm' => !$compact,
|
||||
'mt-1 text-xs' => $compact,
|
||||
])>
|
||||
@@ -65,7 +63,7 @@
|
||||
@endif
|
||||
|
||||
@if($slot->isNotEmpty())
|
||||
<div @class(['mt-5' => !$compact, 'mt-3' => $compact])>
|
||||
<div @class(['mt-4' => !$compact, 'mt-3' => $compact])>
|
||||
{{ $slot }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Help Modal Trigger Component - TailServer Theme
|
||||
Help Modal Trigger Component - shadcn-style
|
||||
Question mark button that opens an info modal
|
||||
|
||||
Usage:
|
||||
@@ -19,17 +19,17 @@
|
||||
])
|
||||
|
||||
<div class="relative" x-data="{ open: false }" x-cloak {{ $attributes }}>
|
||||
<button type="button" @click="open = true" class="text-base/50 hover:text-base/70 transition-colors">
|
||||
<button type="button" @click="open = true" class="text-muted-foreground hover:text-foreground transition-colors duration-150">
|
||||
<x-ri-question-line class="size-4" />
|
||||
</button>
|
||||
|
||||
<!-- Modal Backdrop -->
|
||||
<div
|
||||
x-show="open"
|
||||
x-transition:enter="transition ease-out duration-200"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0"
|
||||
x-transition:enter-end="opacity-100"
|
||||
x-transition:leave="transition ease-in duration-150"
|
||||
x-transition:leave="transition ease-in duration-100"
|
||||
x-transition:leave-start="opacity-100"
|
||||
x-transition:leave-end="opacity-0"
|
||||
@click="open = false"
|
||||
@@ -41,38 +41,38 @@
|
||||
<!-- Modal Content -->
|
||||
<div
|
||||
x-show="open"
|
||||
x-transition:enter="transition ease-out duration-200"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 scale-95"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-150"
|
||||
x-transition:leave="transition ease-in duration-100"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-95"
|
||||
@click.away="open = false"
|
||||
class="fixed inset-0 z-50 flex items-center justify-center p-4"
|
||||
style="display: none;"
|
||||
>
|
||||
<div class="relative w-full max-w-2xl max-h-[90vh] overflow-y-auto bg-background rounded-xl border border-neutral/20 shadow-2xl p-6">
|
||||
<button @click="open = false" class="absolute top-4 right-4 text-base/50 hover:text-base transition-colors">
|
||||
<x-ri-close-line class="size-5" />
|
||||
<div class="relative w-full max-w-2xl max-h-[90vh] overflow-y-auto rounded-xl border bg-background shadow-lg p-6">
|
||||
<button @click="open = false" class="absolute top-4 right-4 rounded-sm opacity-70 ring-offset-background transition-opacity duration-150 hover:opacity-100">
|
||||
<x-ri-close-line class="size-4" />
|
||||
</button>
|
||||
|
||||
<div class="flex items-center gap-3 mb-4 pr-8">
|
||||
<div class="flex items-center justify-center w-10 h-10 rounded-lg bg-primary/10">
|
||||
<x-dynamic-component :component="$icon" class="size-5 text-primary" />
|
||||
<div class="flex items-center justify-center size-9 rounded-lg bg-primary/10">
|
||||
<x-dynamic-component :component="$icon" class="size-4 text-primary" />
|
||||
</div>
|
||||
<h6 class="font-sans text-lg font-semibold leading-tight text-base">
|
||||
<h3 class="text-lg font-semibold leading-none tracking-tight">
|
||||
{{ $title }}
|
||||
</h6>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
@if(isset($description))
|
||||
<div class="block font-sans text-sm font-normal leading-relaxed text-base/90 mb-4">
|
||||
<div class="text-sm text-muted-foreground mb-4">
|
||||
{{ $description }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(isset($details))
|
||||
<div class="pt-4 mt-4 border-t border-neutral/20 space-y-2.5 text-xs leading-relaxed text-base/75">
|
||||
<div class="pt-4 mt-4 border-t space-y-2 text-sm text-muted-foreground">
|
||||
{{ $details }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Info Row / Key-Value Pair Component - TailServer Theme
|
||||
Info Row / Key-Value Pair Component - shadcn-style
|
||||
|
||||
Usage:
|
||||
<x-proxmox::ui.info-row label="IP Address" value="192.168.1.1" />
|
||||
@@ -16,8 +16,8 @@
|
||||
'muted' => false,
|
||||
])
|
||||
|
||||
<div {{ $attributes->class(['flex items-center justify-between py-2 border-b border-neutral/10 last:border-b-0']) }}>
|
||||
<div class="flex items-center gap-2 text-sm text-base/60">
|
||||
<div {{ $attributes->class(['flex items-center justify-between py-2 border-b last:border-b-0']) }}>
|
||||
<div class="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
@if($icon)
|
||||
<x-dynamic-component :component="$icon" class="size-4" />
|
||||
@endif
|
||||
@@ -26,8 +26,7 @@
|
||||
<div class="flex items-center gap-2">
|
||||
<span @class([
|
||||
'text-sm font-medium',
|
||||
'text-base/50' => $muted,
|
||||
'text-base' => !$muted,
|
||||
'text-muted-foreground' => $muted,
|
||||
])>
|
||||
{{ $value ?? $slot }}
|
||||
</span>
|
||||
@@ -40,7 +39,7 @@
|
||||
copied = true;
|
||||
setTimeout(() => copied = false, 2000);
|
||||
"
|
||||
class="p-1 rounded hover:bg-neutral/10 transition-colors text-base/40 hover:text-base/70"
|
||||
class="rounded-sm p-1 text-muted-foreground hover:text-foreground transition-colors duration-150"
|
||||
title="Copy to clipboard"
|
||||
>
|
||||
<x-ri-file-copy-line x-show="!copied" class="size-4" />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Input Component - TailServer Theme
|
||||
Input Component - shadcn-style
|
||||
|
||||
Usage:
|
||||
<x-proxmox::ui.input name="email" label="Email" />
|
||||
@@ -24,36 +24,32 @@
|
||||
|
||||
@php
|
||||
$sizeClasses = [
|
||||
'sm' => 'py-1.5 text-sm',
|
||||
'md' => 'py-2 text-sm',
|
||||
'lg' => 'py-2.5 text-base',
|
||||
'sm' => 'h-8 text-sm',
|
||||
'md' => 'h-9 text-sm',
|
||||
'lg' => 'h-10 text-base',
|
||||
];
|
||||
$sizeClass = $sizeClasses[$size] ?? $sizeClasses['md'];
|
||||
|
||||
$inputClasses = 'block w-full rounded-xl border bg-background leading-6 placeholder-base/40 shadow-sm transition-all duration-200 focus:ring-2 focus:ring-offset-0 focus:shadow-md hover:border-neutral/50 hover:shadow';
|
||||
$inputClasses = 'flex w-full rounded-md border bg-transparent px-3 py-1 text-sm transition-colors duration-150 file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50';
|
||||
|
||||
if ($error) {
|
||||
$inputClasses .= ' border-danger/50 focus:border-danger focus:ring-danger/25 focus:shadow-danger/10';
|
||||
$inputClasses .= ' border-danger focus-visible:ring-danger';
|
||||
} else {
|
||||
$inputClasses .= ' border-neutral/30 focus:border-primary focus:ring-primary/25 focus:shadow-primary/10';
|
||||
$inputClasses .= ' border-input';
|
||||
}
|
||||
|
||||
if ($icon) {
|
||||
$inputClasses .= ' ps-10';
|
||||
} else {
|
||||
$inputClasses .= ' ps-3';
|
||||
$inputClasses .= ' ps-9';
|
||||
}
|
||||
|
||||
if ($iconRight) {
|
||||
$inputClasses .= ' pe-10';
|
||||
} else {
|
||||
$inputClasses .= ' pe-3';
|
||||
$inputClasses .= ' pe-9';
|
||||
}
|
||||
@endphp
|
||||
|
||||
<div {{ $attributes->only('class')->class(['']) }}>
|
||||
<div {{ $attributes->only('class')->class(['space-y-2']) }}>
|
||||
@if($label)
|
||||
<label @if($name) for="{{ $name }}" @endif class="block text-sm font-medium text-base/80 mb-1.5">
|
||||
<label @if($name) for="{{ $name }}" @endif class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
|
||||
{{ $label }}
|
||||
@if($required)
|
||||
<span class="text-danger">*</span>
|
||||
@@ -61,10 +57,10 @@
|
||||
</label>
|
||||
@endif
|
||||
|
||||
<div class="relative group/input">
|
||||
<div class="relative">
|
||||
@if($icon)
|
||||
<div class="pointer-events-none absolute inset-y-0 start-0 flex items-center ps-3 text-base/40 transition-colors duration-200 group-focus-within/input:text-primary">
|
||||
<x-dynamic-component :component="$icon" class="size-5" />
|
||||
<div class="pointer-events-none absolute inset-y-0 start-0 flex items-center ps-3 text-muted-foreground">
|
||||
<x-dynamic-component :component="$icon" class="size-4" />
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@@ -79,15 +75,15 @@
|
||||
/>
|
||||
|
||||
@if($iconRight)
|
||||
<div class="pointer-events-none absolute inset-y-0 end-0 flex items-center pe-3 text-base/40 transition-colors duration-200 group-focus-within/input:text-primary">
|
||||
<x-dynamic-component :component="$iconRight" class="size-5" />
|
||||
<div class="pointer-events-none absolute inset-y-0 end-0 flex items-center pe-3 text-muted-foreground">
|
||||
<x-dynamic-component :component="$iconRight" class="size-4" />
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if($error)
|
||||
<p class="mt-1.5 text-xs text-danger">{{ $error }}</p>
|
||||
<p class="text-[0.8rem] font-medium text-danger">{{ $error }}</p>
|
||||
@elseif($hint)
|
||||
<p class="mt-1.5 text-xs text-base/50">{{ $hint }}</p>
|
||||
<p class="text-[0.8rem] text-muted-foreground">{{ $hint }}</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Modal Component - TailServer Theme
|
||||
Modal Component - shadcn-style Dialog
|
||||
|
||||
Usage:
|
||||
<x-proxmox::ui.modal name="my-modal" title="Modal Title">Content</x-proxmox::ui.modal>
|
||||
@@ -47,66 +47,67 @@
|
||||
{{-- Backdrop --}}
|
||||
<div
|
||||
x-show="show"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0"
|
||||
x-transition:enter-end="opacity-100"
|
||||
x-transition:leave="transition ease-in duration-200"
|
||||
x-transition:leave="transition ease-in duration-100"
|
||||
x-transition:leave-start="opacity-100"
|
||||
x-transition:leave-end="opacity-0"
|
||||
@if($closeable) @click="show = false" @endif
|
||||
class="fixed inset-0 bg-black/60 backdrop-blur-md"
|
||||
class="fixed inset-0 bg-black/50 backdrop-blur-sm"
|
||||
></div>
|
||||
|
||||
{{-- Modal Panel --}}
|
||||
<div class="fixed inset-0 flex items-center justify-center p-4">
|
||||
<div
|
||||
x-show="show"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0 scale-95 translate-y-4"
|
||||
x-transition:enter-end="opacity-100 scale-100 translate-y-0"
|
||||
x-transition:leave="transition ease-in duration-200"
|
||||
x-transition:leave-start="opacity-100 scale-100 translate-y-0"
|
||||
x-transition:leave-end="opacity-0 scale-95 translate-y-4"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 scale-95"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-100"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-95"
|
||||
@click.stop
|
||||
class="relative w-full {{ $maxWidthClass }} max-h-[90vh] overflow-y-auto bg-background-secondary rounded-2xl border border-neutral/20 shadow-2xl ring-1 ring-black/5"
|
||||
class="relative w-full {{ $maxWidthClass }} max-h-[90vh] overflow-y-auto rounded-xl border bg-background-secondary shadow-lg"
|
||||
>
|
||||
{{-- Header --}}
|
||||
@if($title || $closeable)
|
||||
<div class="flex items-start justify-between gap-4 p-6 border-b border-neutral/10">
|
||||
<div class="flex items-start justify-between gap-4 px-6 pt-6 pb-4">
|
||||
<div class="flex items-center gap-3">
|
||||
@if($icon)
|
||||
<div class="flex-shrink-0 flex items-center justify-center w-10 h-10 rounded-xl bg-gradient-to-br from-primary/20 to-primary/5 shadow-sm">
|
||||
<x-dynamic-component :component="$icon" class="size-5 {{ $iconColor }}" /> />
|
||||
<div class="flex-shrink-0 flex items-center justify-center size-9 rounded-lg bg-primary/10">
|
||||
<x-dynamic-component :component="$icon" class="size-4 {{ $iconColor }}" />
|
||||
</div>
|
||||
@endif
|
||||
<div>
|
||||
<div class="space-y-1">
|
||||
@if($title)
|
||||
<h3 class="text-lg font-semibold text-base">{{ $title }}</h3>
|
||||
<h3 class="text-lg font-semibold leading-none tracking-tight">{{ $title }}</h3>
|
||||
@endif
|
||||
@if($subtitle)
|
||||
<p class="text-sm text-base/60 mt-0.5">{{ $subtitle }}</p>
|
||||
<p class="text-sm text-muted-foreground">{{ $subtitle }}</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@if($closeable)
|
||||
<button
|
||||
@click="show = false"
|
||||
class="flex-shrink-0 p-2 -m-2 text-base/40 hover:text-base transition-all duration-200 rounded-xl hover:bg-neutral/10 hover:rotate-90"
|
||||
class="rounded-sm opacity-70 ring-offset-background transition-opacity duration-150 hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
|
||||
>
|
||||
<x-ri-close-line class="size-5" />
|
||||
<x-ri-close-line class="size-4" />
|
||||
<span class="sr-only">Close</span>
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Content --}}
|
||||
<div class="p-6">
|
||||
<div class="px-6 pb-6">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
|
||||
{{-- Footer --}}
|
||||
@if($footer)
|
||||
<div class="flex items-center justify-end gap-3 px-6 py-4 border-t border-neutral/10 bg-gradient-to-t from-neutral/5 to-transparent">
|
||||
<div class="flex items-center justify-end gap-2 px-6 py-4 border-t">
|
||||
{{ $footer }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Info Notice / Alert Banner Component - TailServer Theme
|
||||
Info Notice / Alert Banner Component - shadcn-style
|
||||
Collapsible notice banners for important information
|
||||
|
||||
Usage:
|
||||
@@ -26,25 +26,25 @@
|
||||
$variants = [
|
||||
'info' => [
|
||||
'bg' => 'bg-info/10',
|
||||
'border' => 'border-info/30',
|
||||
'border' => 'border-info/20',
|
||||
'icon' => $icon ?? 'ri-information-line',
|
||||
'iconColor' => 'text-info',
|
||||
],
|
||||
'warning' => [
|
||||
'bg' => 'bg-warning/10',
|
||||
'border' => 'border-warning/30',
|
||||
'border' => 'border-warning/20',
|
||||
'icon' => $icon ?? 'ri-alert-line',
|
||||
'iconColor' => 'text-warning',
|
||||
],
|
||||
'danger' => [
|
||||
'bg' => 'bg-danger/10',
|
||||
'border' => 'border-danger/30',
|
||||
'border' => 'border-danger/20',
|
||||
'icon' => $icon ?? 'ri-error-warning-line',
|
||||
'iconColor' => 'text-danger',
|
||||
],
|
||||
'success' => [
|
||||
'bg' => 'bg-success/10',
|
||||
'border' => 'border-success/30',
|
||||
'border' => 'border-success/20',
|
||||
'icon' => $icon ?? 'ri-checkbox-circle-line',
|
||||
'iconColor' => 'text-success',
|
||||
],
|
||||
@@ -77,20 +77,20 @@
|
||||
]) }}
|
||||
>
|
||||
<div class="flex items-start gap-3">
|
||||
<x-dynamic-component :component="$config['icon']" class="size-5 {{ $config['iconColor'] }} flex-shrink-0 mt-0.5" />
|
||||
<x-dynamic-component :component="$config['icon']" class="size-4 {{ $config['iconColor'] }} flex-shrink-0 mt-0.5" />
|
||||
<div class="flex-1 min-w-0">
|
||||
@if($title)
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h3 class="text-sm font-semibold text-base">{{ $title }}</h3>
|
||||
<h3 class="text-sm font-medium">{{ $title }}</h3>
|
||||
@if($collapsible)
|
||||
<button @click="toggle()" type="button" class="text-base/60 hover:text-base transition-colors">
|
||||
<button @click="toggle()" type="button" class="text-muted-foreground hover:text-foreground transition-colors duration-150">
|
||||
<x-ri-arrow-down-s-line x-bind:class="isCollapsed ? 'size-4 transition-transform rotate-180' : 'size-4 transition-transform'" />
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div @if($collapsible) x-show="!isCollapsed" x-transition @endif>
|
||||
<div @if($collapsible) x-show="!isCollapsed" x-transition @endif class="text-sm text-muted-foreground">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Progress Bar Component - TailServer Theme
|
||||
Progress Bar Component - shadcn-style
|
||||
|
||||
Usage:
|
||||
<x-proxmox::ui.progress :value="75" />
|
||||
@@ -57,18 +57,16 @@
|
||||
<div {{ $attributes->class(['w-full']) }}>
|
||||
@if($showLabel || $label)
|
||||
<div class="flex items-center justify-between text-xs mb-1.5">
|
||||
<span class="text-base/50 font-medium">{{ $label ?? 'Usage' }}</span>
|
||||
<span class="font-semibold text-base/70">{{ number_format($percent, 1) }}%</span>
|
||||
<span class="text-muted-foreground font-medium">{{ $label ?? 'Usage' }}</span>
|
||||
<span class="font-medium">{{ number_format($percent, 1) }}%</span>
|
||||
</div>
|
||||
@endif
|
||||
<div class="{{ $sizeClass }} rounded-full bg-neutral/10 overflow-hidden">
|
||||
<div class="relative {{ $sizeClass }} w-full overflow-hidden rounded-full bg-primary/20">
|
||||
<div
|
||||
@class([
|
||||
$sizeClass,
|
||||
'rounded-full transition-all duration-500 ease-out',
|
||||
'flex-1 w-full transition-all duration-300',
|
||||
$colorClass => !$gradient,
|
||||
'animate-pulse' => $animated,
|
||||
'bg-[length:1rem_1rem] bg-gradient-to-r from-white/20 via-transparent to-transparent' => $striped,
|
||||
])
|
||||
@if($gradient)
|
||||
style="width: {{ $percent }}%; background-image: {{ $gradient }};"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Resource Gauge Component - TailServer Theme
|
||||
Resource Gauge Component - shadcn-style
|
||||
Shows a circular or linear gauge for resource usage
|
||||
|
||||
Usage:
|
||||
@@ -65,39 +65,37 @@
|
||||
};
|
||||
@endphp
|
||||
|
||||
<div {{ $attributes->class(['border border-neutral/15 rounded-2xl p-5 bg-gradient-to-br from-background to-neutral/5 shadow-sm hover:shadow-md transition-shadow duration-300 flex flex-col gap-4']) }}>
|
||||
<div {{ $attributes->class(['rounded-xl border bg-card p-4 shadow-sm flex flex-col gap-4']) }}>
|
||||
<div class="flex items-start justify-between gap-3">
|
||||
<div>
|
||||
<p class="text-xs font-semibold tracking-wide uppercase text-base/50">{{ $label }}</p>
|
||||
<div class="space-y-0.5">
|
||||
<p class="text-xs font-medium text-muted-foreground">{{ $label }}</p>
|
||||
<p class="text-xl font-bold tracking-tight">
|
||||
@if($prefix){{ $prefix }}@endif{{ is_numeric($value) ? number_format($value, 1) : $value }}@if($suffix){{ $suffix }}@endif
|
||||
</p>
|
||||
@if($sublabel)
|
||||
<p class="text-xs text-base/50 mt-0.5">{{ $sublabel }}</p>
|
||||
<p class="text-xs text-muted-foreground">{{ $sublabel }}</p>
|
||||
@endif
|
||||
</div>
|
||||
<span class="text-[10px] font-semibold px-2.5 py-1 rounded-full {{ $badgeClass }} shadow-sm">
|
||||
<span class="text-xs font-medium px-2 py-0.5 rounded-full {{ $badgeClass }}">
|
||||
{{ $badgeLabel }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="flex items-center justify-between text-xs text-base/50 mb-1.5">
|
||||
<span>Usage</span>
|
||||
<div class="space-y-1.5">
|
||||
<div class="flex items-center justify-between text-xs">
|
||||
<span class="text-muted-foreground">Usage</span>
|
||||
<span class="font-medium">{{ number_format($percent, 1) }}%</span>
|
||||
</div>
|
||||
<div class="h-2.5 rounded-full bg-neutral/10 overflow-hidden shadow-inner">
|
||||
<div class="h-2 rounded-full bg-primary/20 overflow-hidden">
|
||||
<div
|
||||
class="h-full rounded-full transition-all duration-700 ease-out relative overflow-hidden"
|
||||
class="h-full rounded-full transition-all duration-300"
|
||||
@if($gradient)
|
||||
style="width: {{ min($percent, 130) }}%; background-image: {{ $gradient }};"
|
||||
@else
|
||||
style="width: {{ min($percent, 130) }}%;"
|
||||
class="{{ $bgColor }}"
|
||||
@endif
|
||||
>
|
||||
<div class="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent -translate-x-full animate-shimmer"></div>
|
||||
</div>
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Section Header Component - TailServer Theme
|
||||
Section Header Component - shadcn-style
|
||||
|
||||
Usage:
|
||||
<x-proxmox::ui.section-header title="Section Title" />
|
||||
@@ -24,8 +24,8 @@
|
||||
$titleSizes = [
|
||||
'sm' => 'text-base font-semibold',
|
||||
'md' => 'text-lg font-semibold',
|
||||
'lg' => 'text-xl font-semibold',
|
||||
'xl' => 'text-2xl font-bold',
|
||||
'lg' => 'text-xl font-semibold tracking-tight',
|
||||
'xl' => 'text-2xl font-bold tracking-tight',
|
||||
];
|
||||
$titleClass = $titleSizes[$size] ?? $titleSizes['lg'];
|
||||
@endphp
|
||||
@@ -33,29 +33,29 @@
|
||||
<div {{ $attributes->class(['flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between']) }}>
|
||||
<div class="flex items-start gap-3">
|
||||
@if($icon)
|
||||
<div class="flex-shrink-0 flex items-center justify-center w-10 h-10 rounded-lg bg-primary/10">
|
||||
<x-dynamic-component :component="$icon" class="size-5 {{ $iconColor }}" />
|
||||
<div class="flex-shrink-0 flex items-center justify-center size-9 rounded-lg bg-primary/10">
|
||||
<x-dynamic-component :component="$icon" class="size-4 {{ $iconColor }}" />
|
||||
</div>
|
||||
@endif
|
||||
<div>
|
||||
<div class="space-y-1">
|
||||
<div class="flex items-center gap-2 min-w-0">
|
||||
<h2 class="{{ $titleClass }} text-base break-words">{{ $title }}</h2>
|
||||
<h2 class="{{ $titleClass }}">{{ $title }}</h2>
|
||||
@if($helpText)
|
||||
<div class="relative" x-data="{ open: false }" x-cloak>
|
||||
<button
|
||||
type="button"
|
||||
@click="open = true"
|
||||
class="text-base/50 hover:text-base/70 transition-colors"
|
||||
class="text-muted-foreground hover:text-foreground transition-colors duration-150"
|
||||
>
|
||||
<x-ri-question-line class="size-4" />
|
||||
</button>
|
||||
{{-- Help Modal --}}
|
||||
<div
|
||||
x-show="open"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0"
|
||||
x-transition:enter-end="opacity-100"
|
||||
x-transition:leave="transition ease-in duration-200"
|
||||
x-transition:leave="transition ease-in duration-100"
|
||||
x-transition:leave-start="opacity-100"
|
||||
x-transition:leave-end="opacity-0"
|
||||
@click="open = false"
|
||||
@@ -65,29 +65,29 @@
|
||||
></div>
|
||||
<div
|
||||
x-show="open"
|
||||
x-transition:enter="transition ease-out duration-300"
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 scale-95"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-200"
|
||||
x-transition:leave="transition ease-in duration-100"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-95"
|
||||
@click.away="open = false"
|
||||
class="fixed inset-0 z-50 flex items-center justify-center p-4"
|
||||
style="display: none;"
|
||||
>
|
||||
<div class="relative w-full max-w-lg max-h-[90vh] overflow-y-auto bg-background-secondary rounded-xl border border-neutral/20 shadow-2xl p-6">
|
||||
<button @click="open = false" class="absolute top-4 right-4 text-base/50 hover:text-base transition-colors">
|
||||
<x-ri-close-line class="size-5" />
|
||||
<div class="relative w-full max-w-lg max-h-[90vh] overflow-y-auto rounded-lg border bg-background shadow-lg p-6">
|
||||
<button @click="open = false" class="absolute top-4 right-4 rounded-sm opacity-70 ring-offset-background transition-opacity duration-150 hover:opacity-100">
|
||||
<x-ri-close-line class="size-4" />
|
||||
</button>
|
||||
<div class="flex items-center gap-3 mb-4 pr-8">
|
||||
@if($icon)
|
||||
<div class="flex items-center justify-center w-10 h-10 rounded-lg bg-primary/10">
|
||||
<x-dynamic-component :component="$icon" class="size-5 {{ $iconColor }}" />
|
||||
<div class="flex items-center justify-center size-9 rounded-lg bg-primary/10">
|
||||
<x-dynamic-component :component="$icon" class="size-4 {{ $iconColor }}" />
|
||||
</div>
|
||||
@endif
|
||||
<h6 class="text-lg font-semibold">{{ $title }}</h6>
|
||||
<h3 class="text-lg font-semibold leading-none tracking-tight">{{ $title }}</h3>
|
||||
</div>
|
||||
<div class="text-sm text-base/70 leading-relaxed">
|
||||
<div class="text-sm text-muted-foreground">
|
||||
{{ $helpText }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -96,7 +96,7 @@
|
||||
@endif
|
||||
</div>
|
||||
@if($subtitle)
|
||||
<p class="text-sm text-base/60 mt-0.5">{{ $subtitle }}</p>
|
||||
<p class="text-sm text-muted-foreground">{{ $subtitle }}</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Select Component - TailServer Theme
|
||||
Select Component - shadcn-style
|
||||
|
||||
Usage:
|
||||
<x-proxmox::ui.select name="country" label="Country">
|
||||
@@ -20,24 +20,24 @@
|
||||
|
||||
@php
|
||||
$sizeClasses = [
|
||||
'sm' => 'py-1.5 text-sm',
|
||||
'md' => 'py-2 text-sm',
|
||||
'lg' => 'py-2.5 text-base',
|
||||
'sm' => 'h-8 text-sm',
|
||||
'md' => 'h-9 text-sm',
|
||||
'lg' => 'h-10 text-base',
|
||||
];
|
||||
$sizeClass = $sizeClasses[$size] ?? $sizeClasses['md'];
|
||||
|
||||
$selectClasses = 'block w-full rounded-xl border bg-background px-3 leading-6 shadow-sm transition-all duration-200 focus:ring-2 focus:ring-offset-0 focus:shadow-md hover:border-neutral/50 hover:shadow appearance-none cursor-pointer';
|
||||
$selectClasses = 'flex w-full items-center justify-between whitespace-nowrap rounded-md border bg-transparent px-3 py-2 text-sm ring-offset-background transition-colors duration-150 placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 appearance-none cursor-pointer';
|
||||
|
||||
if ($error) {
|
||||
$selectClasses .= ' border-danger/50 focus:border-danger focus:ring-danger/25 focus:shadow-danger/10';
|
||||
$selectClasses .= ' border-danger focus:ring-danger';
|
||||
} else {
|
||||
$selectClasses .= ' border-neutral/30 focus:border-primary focus:ring-primary/25 focus:shadow-primary/10';
|
||||
$selectClasses .= ' border-input';
|
||||
}
|
||||
@endphp
|
||||
|
||||
<div {{ $attributes->only('class')->class(['']) }}>
|
||||
<div {{ $attributes->only('class')->class(['space-y-2']) }}>
|
||||
@if($label)
|
||||
<label @if($name) for="{{ $name }}" @endif class="block text-sm font-medium text-base/80 mb-1.5">
|
||||
<label @if($name) for="{{ $name }}" @endif class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
|
||||
{{ $label }}
|
||||
@if($required)
|
||||
<span class="text-danger">*</span>
|
||||
@@ -45,7 +45,7 @@
|
||||
</label>
|
||||
@endif
|
||||
|
||||
<div class="relative group/select">
|
||||
<div class="relative">
|
||||
<select
|
||||
@if($name) name="{{ $name }}" id="{{ $name }}" @endif
|
||||
@if($disabled) disabled @endif
|
||||
@@ -54,14 +54,14 @@
|
||||
>
|
||||
{{ $slot }}
|
||||
</select>
|
||||
<div class="pointer-events-none absolute inset-y-0 end-0 flex items-center pe-3 text-base/40 transition-all duration-200 group-focus-within/select:text-primary group-hover/select:text-base/60">
|
||||
<x-ri-arrow-down-s-line class="size-5 transition-transform duration-200 group-focus-within/select:rotate-180" />
|
||||
<div class="pointer-events-none absolute inset-y-0 end-0 flex items-center pe-3 text-muted-foreground">
|
||||
<x-ri-arrow-down-s-line class="size-4 opacity-50" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($error)
|
||||
<p class="mt-1.5 text-xs text-danger">{{ $error }}</p>
|
||||
<p class="text-[0.8rem] font-medium text-danger">{{ $error }}</p>
|
||||
@elseif($hint)
|
||||
<p class="mt-1.5 text-xs text-base/50">{{ $hint }}</p>
|
||||
<p class="text-[0.8rem] text-muted-foreground">{{ $hint }}</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Server Status Indicator Component - TailServer Theme
|
||||
Server Status Indicator Component - shadcn-style
|
||||
|
||||
Usage:
|
||||
<x-proxmox::ui.server-status status="running" />
|
||||
@@ -65,7 +65,7 @@
|
||||
'warning' => 'text-warning',
|
||||
'danger' => 'text-danger',
|
||||
'info' => 'text-info',
|
||||
'muted' => 'text-base/40',
|
||||
'muted' => 'text-muted-foreground',
|
||||
];
|
||||
|
||||
$bgClasses = [
|
||||
@@ -73,7 +73,7 @@
|
||||
'warning' => 'bg-warning/10',
|
||||
'danger' => 'bg-danger/10',
|
||||
'info' => 'bg-info/10',
|
||||
'muted' => 'bg-neutral/10',
|
||||
'muted' => 'bg-muted',
|
||||
];
|
||||
|
||||
$dotColorClasses = [
|
||||
@@ -81,7 +81,7 @@
|
||||
'warning' => 'bg-warning',
|
||||
'danger' => 'bg-danger',
|
||||
'info' => 'bg-info',
|
||||
'muted' => 'bg-base/40',
|
||||
'muted' => 'bg-muted-foreground',
|
||||
];
|
||||
|
||||
$sizeClasses = [
|
||||
@@ -91,9 +91,9 @@
|
||||
];
|
||||
|
||||
$dotSizes = [
|
||||
'sm' => 'h-1.5 w-1.5',
|
||||
'md' => 'h-2 w-2',
|
||||
'lg' => 'h-2.5 w-2.5',
|
||||
'sm' => 'size-1.5',
|
||||
'md' => 'size-2',
|
||||
'lg' => 'size-2.5',
|
||||
];
|
||||
|
||||
$textSizes = [
|
||||
@@ -120,7 +120,7 @@
|
||||
{{-- Full badge variant --}}
|
||||
<div
|
||||
{{ $attributes->class([
|
||||
'inline-flex items-center font-semibold rounded-lg',
|
||||
'inline-flex items-center font-medium rounded-md',
|
||||
$bgClasses[$config['color']] ?? $bgClasses['muted'],
|
||||
$colorClasses[$config['color']] ?? $colorClasses['muted'],
|
||||
$sizeClasses[$size] ?? $sizeClasses['md'],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Spinner Component - TailServer Theme
|
||||
Spinner Component - shadcn-style
|
||||
|
||||
Usage:
|
||||
<x-proxmox::ui.spinner />
|
||||
@@ -15,9 +15,9 @@
|
||||
$sizeClasses = [
|
||||
'xs' => 'size-3',
|
||||
'sm' => 'size-4',
|
||||
'md' => 'size-6',
|
||||
'lg' => 'size-8',
|
||||
'xl' => 'size-12',
|
||||
'md' => 'size-5',
|
||||
'lg' => 'size-6',
|
||||
'xl' => 'size-8',
|
||||
];
|
||||
|
||||
$colorClasses = [
|
||||
@@ -28,7 +28,7 @@
|
||||
'warning' => 'text-warning',
|
||||
'danger' => 'text-danger',
|
||||
'info' => 'text-info',
|
||||
'muted' => 'text-base/50',
|
||||
'muted' => 'text-muted-foreground',
|
||||
];
|
||||
|
||||
$sizeClass = $sizeClasses[$size] ?? $sizeClasses['md'];
|
||||
@@ -36,11 +36,11 @@
|
||||
@endphp
|
||||
|
||||
<svg
|
||||
{{ $attributes->class(['animate-spin drop-shadow-sm', $sizeClass, $colorClass]) }}
|
||||
{{ $attributes->class(['animate-spin', $sizeClass, $colorClass]) }}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle class="opacity-20" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-80" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Stat Card Component - TailServer Theme
|
||||
Stat Card Component - TailServer Theme (shadcn-inspired)
|
||||
|
||||
Usage:
|
||||
<x-proxmox::ui.stat-card value="12" label="CPU %" />
|
||||
@@ -13,7 +13,7 @@
|
||||
'suffix' => null,
|
||||
'prefix' => null,
|
||||
'icon' => null,
|
||||
'iconClass' => 'text-primary/30',
|
||||
'iconClass' => 'text-muted-foreground',
|
||||
'status' => null,
|
||||
'statusText' => null,
|
||||
'href' => null,
|
||||
@@ -21,54 +21,61 @@
|
||||
'progressColor' => 'bg-primary',
|
||||
'clickable' => false,
|
||||
'compact' => false,
|
||||
'change' => null,
|
||||
'changeLabel' => null,
|
||||
])
|
||||
|
||||
@php
|
||||
$tag = $href ? 'a' : 'div';
|
||||
$statusClasses = [
|
||||
'success' => 'text-success bg-success/10',
|
||||
'warning' => 'text-warning bg-warning/10',
|
||||
'danger' => 'text-danger bg-danger/10',
|
||||
'info' => 'text-info bg-info/10',
|
||||
'muted' => 'text-base/50 bg-neutral/10',
|
||||
'success' => 'text-success',
|
||||
'warning' => 'text-warning',
|
||||
'danger' => 'text-danger',
|
||||
'info' => 'text-info',
|
||||
'muted' => 'text-muted-foreground',
|
||||
];
|
||||
$statusClass = $statusClasses[$status] ?? $statusClasses['muted'];
|
||||
$statusClass = $statusClasses[$status] ?? '';
|
||||
@endphp
|
||||
|
||||
<{{ $tag }}
|
||||
@if($href) href="{{ $href }}" @endif
|
||||
{{ $attributes->class([
|
||||
'group relative flex flex-col rounded-xl border border-neutral/20 bg-background-secondary overflow-hidden',
|
||||
'hover:bg-background-secondary/80 hover:border-neutral/30 hover:shadow-sm cursor-pointer transition-all duration-200' => $href || $clickable,
|
||||
'flex flex-col gap-6 rounded-xl border border-neutral/15 bg-card py-6 text-card-foreground shadow-sm',
|
||||
'hover:shadow-md transition-shadow duration-150 cursor-pointer' => $href || $clickable,
|
||||
]) }}
|
||||
>
|
||||
{{-- Subtle gradient overlay --}}
|
||||
<div class="absolute inset-0 bg-gradient-to-br from-primary/[0.02] to-transparent opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none"></div>
|
||||
|
||||
<div @class(['relative flex grow items-center justify-between', 'p-5' => !$compact, 'p-4' => $compact])>
|
||||
<dl class="min-w-0">
|
||||
<dt @class(['font-bold text-base', 'text-2xl' => !$compact, 'text-xl' => $compact])>
|
||||
@if($prefix)<span class="text-base/40">{{ $prefix }}</span>@endif{{ $value }}@if($suffix)<span @class(['text-base/40', 'text-lg' => !$compact, 'text-base' => $compact])>{{ $suffix }}</span>@endif
|
||||
</dt>
|
||||
<dd @class(['font-medium text-base/50', 'text-sm' => !$compact, 'text-xs' => $compact])>{{ $label }}</dd>
|
||||
</dl>
|
||||
<div class="flex flex-row items-center justify-between space-y-0 px-6 pb-2">
|
||||
<h3 @class(['text-sm font-medium text-muted-foreground'])>{{ $label }}</h3>
|
||||
@if($icon)
|
||||
<div @class(['flex items-center', $iconClass, 'opacity-60 group-hover:opacity-80 transition-opacity'])>
|
||||
<x-dynamic-component :component="$icon" @class(['size-10' => !$compact, 'size-8' => $compact]) />
|
||||
</div>
|
||||
<x-dynamic-component :component="$icon" class="size-4 {{ $iconClass }}" />
|
||||
@endif
|
||||
</div>
|
||||
<div class="px-6">
|
||||
<div @class(['font-bold', 'text-2xl' => !$compact, 'text-xl' => $compact])>
|
||||
@if($prefix)<span class="text-muted-foreground">{{ $prefix }}</span>@endif{{ $value }}@if($suffix)<span class="text-muted-foreground text-lg">{{ $suffix }}</span>@endif
|
||||
</div>
|
||||
@if($change !== null || $changeLabel)
|
||||
<p class="text-xs text-muted-foreground mt-1">
|
||||
@if($change !== null)
|
||||
<span @class(['font-medium', $change >= 0 ? 'text-success' : 'text-danger'])>
|
||||
{{ $change >= 0 ? '+' : '' }}{{ $change }}%
|
||||
</span>
|
||||
@endif
|
||||
{{ $changeLabel ?? 'from last period' }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if($progress !== null)
|
||||
<div class="px-5 pb-4">
|
||||
<div class="px-6 pb-2">
|
||||
<div class="h-1.5 w-full rounded-full bg-neutral/10 overflow-hidden">
|
||||
<div class="h-full {{ $progressColor }} rounded-full transition-all duration-500" style="width: {{ min(100, $progress) }}%"></div>
|
||||
<div class="h-full {{ $progressColor }} rounded-full transition-all duration-300" style="width: {{ min(100, $progress) }}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($statusText)
|
||||
<div class="border-t border-neutral/20 px-5 py-2.5 text-xs font-semibold {{ $statusClass }}">
|
||||
<div class="border-t border-neutral/10 px-6 pt-4 text-xs font-medium {{ $statusClass }}">
|
||||
{{ $statusText }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Tab Panel Component - TailServer Theme
|
||||
Tab Panel Component - shadcn-style
|
||||
--}}
|
||||
|
||||
@props([
|
||||
@@ -8,10 +8,10 @@
|
||||
|
||||
<div
|
||||
x-show="activeTab === '{{ $name }}'"
|
||||
x-transition:enter="transition ease-out duration-200"
|
||||
x-transition:enter-start="opacity-0 translate-y-1"
|
||||
x-transition:enter-end="opacity-100 translate-y-0"
|
||||
{{ $attributes }}
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0"
|
||||
x-transition:enter-end="opacity-100"
|
||||
{{ $attributes->class(['ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2']) }}
|
||||
>
|
||||
{{ $slot }}
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Table Row Component - TailServer Theme
|
||||
Table Row Component - shadcn-style
|
||||
--}}
|
||||
|
||||
@props([
|
||||
@@ -16,8 +16,9 @@
|
||||
@if($href)
|
||||
<tr
|
||||
{{ $attributes->class([
|
||||
'border-b border-neutral/10 cursor-pointer transition-colors duration-150',
|
||||
'hover:bg-primary/5' => $isHoverable,
|
||||
'border-b transition-colors cursor-pointer',
|
||||
'hover:bg-muted/50' => $isHoverable,
|
||||
'data-[state=selected]:bg-muted',
|
||||
]) }}
|
||||
onclick="window.location='{{ $href }}'"
|
||||
>
|
||||
@@ -26,9 +27,10 @@
|
||||
@else
|
||||
<tr
|
||||
{{ $attributes->class([
|
||||
'border-b border-neutral/10 transition-colors duration-150',
|
||||
'hover:bg-neutral/5' => $isHoverable,
|
||||
'cursor-pointer hover:bg-primary/5' => $clickable,
|
||||
'border-b transition-colors',
|
||||
'hover:bg-muted/50' => $isHoverable,
|
||||
'cursor-pointer' => $clickable,
|
||||
'data-[state=selected]:bg-muted',
|
||||
]) }}
|
||||
>
|
||||
{{ $slot }}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Table Data Cell Component - TailServer Theme
|
||||
Table Data Cell Component - shadcn-style
|
||||
--}}
|
||||
|
||||
@props([
|
||||
@@ -11,20 +11,20 @@
|
||||
|
||||
@php
|
||||
$alignClasses = [
|
||||
'start' => 'text-start',
|
||||
'start' => 'text-left',
|
||||
'center' => 'text-center',
|
||||
'end' => 'text-end',
|
||||
'end' => 'text-right',
|
||||
];
|
||||
$alignClass = $alignClasses[$align] ?? $alignClasses['start'];
|
||||
@endphp
|
||||
|
||||
<td
|
||||
{{ $attributes->class([
|
||||
'px-3 py-3',
|
||||
'p-4 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
|
||||
$alignClass,
|
||||
'whitespace-nowrap' => $nowrap,
|
||||
'text-base/50' => $muted,
|
||||
'font-semibold text-base' => $strong,
|
||||
'text-muted-foreground' => $muted,
|
||||
'font-medium' => $strong,
|
||||
]) }}
|
||||
>
|
||||
{{ $slot }}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Table Header Cell Component - TailServer Theme
|
||||
Table Header Cell Component - shadcn-style
|
||||
--}}
|
||||
|
||||
@props([
|
||||
@@ -12,9 +12,9 @@
|
||||
|
||||
@php
|
||||
$alignClasses = [
|
||||
'start' => 'text-start',
|
||||
'start' => 'text-left',
|
||||
'center' => 'text-center',
|
||||
'end' => 'text-end',
|
||||
'end' => 'text-right',
|
||||
];
|
||||
$alignClass = $alignClasses[$align] ?? $alignClasses['start'];
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
|
||||
<th
|
||||
{{ $attributes->class([
|
||||
'px-3 py-3 text-xs font-semibold tracking-wider text-base/60 uppercase',
|
||||
'h-10 px-4 font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
|
||||
$alignClass,
|
||||
'cursor-pointer hover:text-base hover:bg-neutral/5 select-none transition-colors duration-150' => $sortable,
|
||||
'cursor-pointer select-none hover:text-foreground transition-colors duration-150' => $sortable,
|
||||
]) }}
|
||||
@if($sortable && $sortKey)
|
||||
wire:click="sort('{{ $sortKey }}')"
|
||||
@@ -34,7 +34,7 @@
|
||||
<div class="flex items-center gap-1.5 @if($align === 'end') justify-end @elseif($align === 'center') justify-center @endif">
|
||||
<span>{{ $slot }}</span>
|
||||
@if($sortable)
|
||||
<span class="text-base/40">
|
||||
<span class="text-muted-foreground">
|
||||
@if($isSorted)
|
||||
@if($currentDirection === 'asc')
|
||||
<x-ri-arrow-up-s-line class="size-4" />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Table Component - TailServer Theme
|
||||
Table Component - shadcn-style
|
||||
|
||||
Usage:
|
||||
<x-proxmox::ui.table>
|
||||
@@ -21,31 +21,27 @@
|
||||
'compact' => false,
|
||||
])
|
||||
|
||||
@php
|
||||
$wrapperClasses = $responsive ? 'min-w-full overflow-x-auto rounded-xl border border-neutral/10' : '';
|
||||
@endphp
|
||||
|
||||
<div class="{{ $wrapperClasses }}">
|
||||
<div @class(['relative w-full overflow-auto' => $responsive])>
|
||||
<table
|
||||
{{ $attributes->class([
|
||||
'min-w-full align-middle text-sm',
|
||||
'[&_tbody_tr:nth-child(even)]:bg-neutral/3' => $striped,
|
||||
'w-full caption-bottom text-sm',
|
||||
'[&_tbody_tr:nth-child(even)]:bg-muted/50' => $striped,
|
||||
]) }}
|
||||
data-hoverable="{{ $hoverable ? 'true' : 'false' }}"
|
||||
data-compact="{{ $compact ? 'true' : 'false' }}"
|
||||
>
|
||||
@if(isset($head))
|
||||
<thead class="bg-gradient-to-b from-neutral/5 to-transparent">
|
||||
<tr class="border-b border-neutral/15">
|
||||
<thead class="[&_tr]:border-b">
|
||||
<tr class="border-b transition-colors">
|
||||
{{ $head }}
|
||||
</tr>
|
||||
</thead>
|
||||
@endif
|
||||
<tbody>
|
||||
<tbody class="[&_tr:last-child]:border-0">
|
||||
{{ $slot }}
|
||||
</tbody>
|
||||
@if(isset($foot))
|
||||
<tfoot>
|
||||
<tfoot class="border-t bg-muted/50 font-medium [&>tr]:last:border-b-0">
|
||||
{{ $foot }}
|
||||
</tfoot>
|
||||
@endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Tabs Component - TailServer Theme
|
||||
Tabs Component - shadcn-style
|
||||
|
||||
Usage:
|
||||
<x-proxmox::ui.tabs :tabs="['Overview', 'Settings', 'Logs']" active="Overview">
|
||||
@@ -18,22 +18,22 @@
|
||||
@php
|
||||
$variantClasses = [
|
||||
'default' => [
|
||||
'container' => 'border-b border-neutral/15',
|
||||
'tab' => 'px-4 py-3 text-sm font-medium border-b-2 -mb-px transition-all duration-200',
|
||||
'active' => 'text-primary border-primary',
|
||||
'inactive' => 'text-base/50 border-transparent hover:text-base hover:border-neutral/30',
|
||||
'container' => 'inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground',
|
||||
'tab' => 'inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
|
||||
'active' => 'bg-background text-foreground shadow',
|
||||
'inactive' => 'hover:bg-background/50 hover:text-foreground',
|
||||
],
|
||||
'pills' => [
|
||||
'container' => 'flex gap-1.5 p-1.5 bg-neutral/5 rounded-xl border border-neutral/10',
|
||||
'tab' => 'px-4 py-2 text-sm font-medium rounded-lg transition-all duration-200',
|
||||
'active' => 'bg-background-secondary text-base shadow-md',
|
||||
'inactive' => 'text-base/50 hover:text-base hover:bg-neutral/10',
|
||||
'container' => 'inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground',
|
||||
'tab' => 'inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium transition-all duration-150',
|
||||
'active' => 'bg-background text-foreground shadow',
|
||||
'inactive' => 'hover:bg-background/50 hover:text-foreground',
|
||||
],
|
||||
'buttons' => [
|
||||
'container' => 'flex gap-2',
|
||||
'tab' => 'px-4 py-2.5 text-sm font-medium rounded-xl border transition-all duration-200',
|
||||
'active' => 'bg-primary text-white border-primary shadow-md shadow-primary/25',
|
||||
'inactive' => 'bg-background-secondary text-base/60 border-neutral/20 hover:border-neutral/40 hover:text-base hover:shadow-sm',
|
||||
'tab' => 'inline-flex items-center justify-center whitespace-nowrap rounded-md px-4 py-2 text-sm font-medium transition-colors duration-150',
|
||||
'active' => 'bg-primary text-primary-foreground shadow',
|
||||
'inactive' => 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
||||
],
|
||||
];
|
||||
|
||||
@@ -47,32 +47,30 @@
|
||||
>
|
||||
{{-- Tab Navigation --}}
|
||||
<div class="{{ $styles['container'] }}">
|
||||
<nav class="flex @if($variant === 'default') -mb-px @endif">
|
||||
@foreach($tabs as $tab)
|
||||
@php
|
||||
$tabName = is_array($tab) ? ($tab['name'] ?? $tab['label'] ?? '') : $tab;
|
||||
$tabLabel = is_array($tab) ? ($tab['label'] ?? $tab['name'] ?? '') : $tab;
|
||||
$tabIcon = is_array($tab) ? ($tab['icon'] ?? null) : null;
|
||||
$tabBadge = is_array($tab) ? ($tab['badge'] ?? null) : null;
|
||||
@endphp
|
||||
<button
|
||||
type="button"
|
||||
@click="activeTab = '{{ $tabName }}'"
|
||||
:class="activeTab === '{{ $tabName }}' ? '{{ $styles['active'] }}' : '{{ $styles['inactive'] }}'"
|
||||
class="{{ $styles['tab'] }} flex items-center gap-2"
|
||||
>
|
||||
@if($tabIcon)
|
||||
<x-dynamic-component :component="$tabIcon" class="size-4" />
|
||||
@endif
|
||||
<span>{{ $tabLabel }}</span>
|
||||
@if($tabBadge)
|
||||
<span class="px-1.5 py-0.5 text-xs font-semibold rounded-full bg-primary/10 text-primary">
|
||||
{{ $tabBadge }}
|
||||
</span>
|
||||
@endif
|
||||
</button>
|
||||
@endforeach
|
||||
</nav>
|
||||
@foreach($tabs as $tab)
|
||||
@php
|
||||
$tabName = is_array($tab) ? ($tab['name'] ?? $tab['label'] ?? '') : $tab;
|
||||
$tabLabel = is_array($tab) ? ($tab['label'] ?? $tab['name'] ?? '') : $tab;
|
||||
$tabIcon = is_array($tab) ? ($tab['icon'] ?? null) : null;
|
||||
$tabBadge = is_array($tab) ? ($tab['badge'] ?? null) : null;
|
||||
@endphp
|
||||
<button
|
||||
type="button"
|
||||
@click="activeTab = '{{ $tabName }}'"
|
||||
:class="activeTab === '{{ $tabName }}' ? '{{ $styles['active'] }}' : '{{ $styles['inactive'] }}'"
|
||||
class="{{ $styles['tab'] }} gap-2"
|
||||
>
|
||||
@if($tabIcon)
|
||||
<x-dynamic-component :component="$tabIcon" class="size-4" />
|
||||
@endif
|
||||
<span>{{ $tabLabel }}</span>
|
||||
@if($tabBadge)
|
||||
<span class="ml-1.5 rounded-full bg-primary/10 px-1.5 py-0.5 text-xs font-medium text-primary">
|
||||
{{ $tabBadge }}
|
||||
</span>
|
||||
@endif
|
||||
</button>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
{{-- Tab Panels --}}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Toggle/Switch Component - TailServer Theme
|
||||
Toggle/Switch Component - shadcn-style
|
||||
|
||||
Usage:
|
||||
<x-proxmox::ui.toggle wire:model="enabled" label="Enable Feature" />
|
||||
@@ -11,14 +11,14 @@
|
||||
'disabled' => false,
|
||||
])
|
||||
|
||||
<label {{ $attributes->only('class')->class(['flex items-center justify-between gap-4 cursor-pointer group']) }}>
|
||||
<label {{ $attributes->only('class')->class(['flex items-center justify-between gap-4 cursor-pointer']) }}>
|
||||
@if($label || $description)
|
||||
<div class="flex flex-col">
|
||||
<div class="space-y-0.5">
|
||||
@if($label)
|
||||
<span class="text-sm font-medium text-base transition-colors duration-200 group-hover:text-primary">{{ $label }}</span>
|
||||
<span class="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">{{ $label }}</span>
|
||||
@endif
|
||||
@if($description)
|
||||
<span class="text-xs text-base/50 transition-colors duration-200 group-hover:text-base/70">{{ $description }}</span>
|
||||
<p class="text-[0.8rem] text-muted-foreground">{{ $description }}</p>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
@@ -30,16 +30,14 @@
|
||||
class="sr-only peer"
|
||||
/>
|
||||
<div @class([
|
||||
'w-11 h-6 rounded-full transition-all duration-300 ease-out',
|
||||
'bg-neutral/20 peer-checked:bg-primary peer-checked:shadow-md peer-checked:shadow-primary/30',
|
||||
'peer-focus:ring-2 peer-focus:ring-primary/25 peer-focus:ring-offset-2 peer-focus:ring-offset-background',
|
||||
'peer-disabled:opacity-50 peer-disabled:cursor-not-allowed',
|
||||
'group-hover:bg-neutral/30 peer-checked:group-hover:bg-primary/90',
|
||||
'inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors duration-150',
|
||||
'bg-input peer-checked:bg-primary',
|
||||
'peer-focus-visible:outline-none peer-focus-visible:ring-2 peer-focus-visible:ring-ring peer-focus-visible:ring-offset-2 peer-focus-visible:ring-offset-background',
|
||||
'peer-disabled:cursor-not-allowed peer-disabled:opacity-50',
|
||||
])></div>
|
||||
<div @class([
|
||||
'absolute top-0.5 left-0.5 size-5 bg-white rounded-full shadow-md transition-all duration-300 ease-out',
|
||||
'peer-checked:translate-x-5 peer-checked:shadow-lg',
|
||||
'group-hover:scale-105 peer-active:scale-95',
|
||||
'pointer-events-none absolute top-0.5 left-0.5 block size-4 rounded-full bg-background shadow-lg ring-0 transition-transform duration-150',
|
||||
'peer-checked:translate-x-4',
|
||||
])></div>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{--
|
||||
Tooltip Component - TailServer Theme
|
||||
Tooltip Component - shadcn-style
|
||||
|
||||
Usage:
|
||||
<x-proxmox::ui.tooltip text="This is a tooltip">
|
||||
@@ -19,13 +19,6 @@
|
||||
'left' => 'right-full top-1/2 -translate-y-1/2 mr-2',
|
||||
'right' => 'left-full top-1/2 -translate-y-1/2 ml-2',
|
||||
];
|
||||
|
||||
$arrows = [
|
||||
'top' => 'top-full left-1/2 -translate-x-1/2 border-t-zinc-800 border-x-transparent border-b-transparent border-4',
|
||||
'bottom' => 'bottom-full left-1/2 -translate-x-1/2 border-b-zinc-800 border-x-transparent border-t-transparent border-4',
|
||||
'left' => 'left-full top-1/2 -translate-y-1/2 border-l-zinc-800 border-y-transparent border-r-transparent border-4',
|
||||
'right' => 'right-full top-1/2 -translate-y-1/2 border-r-zinc-800 border-y-transparent border-l-transparent border-4',
|
||||
];
|
||||
@endphp
|
||||
|
||||
<div
|
||||
@@ -40,17 +33,16 @@
|
||||
<div
|
||||
x-show="show"
|
||||
x-cloak
|
||||
x-transition:enter="transition ease-out duration-150"
|
||||
x-transition:enter-start="opacity-0 scale-90"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-100"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-90"
|
||||
x-transition:enter="transition ease-out duration-100"
|
||||
x-transition:enter-start="opacity-0"
|
||||
x-transition:enter-end="opacity-100"
|
||||
x-transition:leave="transition ease-in duration-75"
|
||||
x-transition:leave-start="opacity-100"
|
||||
x-transition:leave-end="opacity-0"
|
||||
class="absolute z-50 {{ $positions[$position] ?? $positions['top'] }} pointer-events-none"
|
||||
>
|
||||
<div class="relative bg-zinc-900 text-white text-xs font-medium rounded-lg px-2.5 py-1.5 whitespace-nowrap shadow-xl ring-1 ring-white/10">
|
||||
<div class="overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground">
|
||||
{{ $text }}
|
||||
<div class="absolute {{ $arrows[$position] ?? $arrows['top'] }}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,46 +1,46 @@
|
||||
{{-- Server Card Component - TailServer Theme --}}
|
||||
{{-- Server Card Component - TailServer Theme (shadcn-inspired) --}}
|
||||
<div class="space-y-4">
|
||||
@if($status)
|
||||
<a href="{{ route('proxmox.server.overview', ['server' => $server->id]) }}" wire:navigate>
|
||||
<x-proxmox::ui.card class="hover:border-primary/30 transition-colors cursor-pointer">
|
||||
<div class="flex flex-col rounded-xl border border-neutral/15 bg-card text-card-foreground shadow-sm hover:shadow-md hover:border-neutral/25 transition-shadow duration-150 cursor-pointer">
|
||||
{{-- Header: Server Name & Status --}}
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="flex items-center justify-between px-6 py-4">
|
||||
<div class="flex items-center gap-3">
|
||||
@if($server->user_set_icon)
|
||||
@php
|
||||
$iconColor = $server->user_set_icon_color ?? '#6366f1';
|
||||
$bgColor = $iconColor . '33';
|
||||
$bgColor = $iconColor . '20';
|
||||
$iconPrefix = $server->user_set_icon_prefix ?? 'ri';
|
||||
$iconName = $server->user_set_icon;
|
||||
$componentName = $iconPrefix . '-' . $iconName;
|
||||
@endphp
|
||||
<div class="p-2 rounded-lg" style="background-color: {{ $bgColor }};">
|
||||
<x-dynamic-component :component="$componentName" class="size-5" style="color: {{ $iconColor }};" />
|
||||
<div class="flex items-center justify-center size-9 rounded-lg" style="background-color: {{ $bgColor }};">
|
||||
<x-dynamic-component :component="$componentName" class="size-4" style="color: {{ $iconColor }};" />
|
||||
</div>
|
||||
@else
|
||||
<div class="bg-primary/10 p-2 rounded-lg">
|
||||
<x-ri-server-line class="size-5 text-primary" />
|
||||
<div class="flex items-center justify-center size-9 rounded-lg bg-primary/10">
|
||||
<x-ri-server-line class="size-4 text-primary" />
|
||||
</div>
|
||||
@endif
|
||||
<span class="font-semibold text-base">{{ $status->name }}</span>
|
||||
<span class="font-semibold">{{ $status->name }}</span>
|
||||
</div>
|
||||
<x-proxmox::ui.server-status :status="$status->status" showLabel />
|
||||
</div>
|
||||
|
||||
{{-- Quick Info Row --}}
|
||||
<div class="flex flex-row flex-wrap gap-6 py-3 border-y border-neutral/10">
|
||||
<div class="flex flex-row flex-wrap gap-6 px-6 py-3 border-y border-neutral/10">
|
||||
<div class="flex flex-col min-w-[120px]">
|
||||
<span class="text-xs font-medium text-base/50 uppercase tracking-wide">IPv4 Address</span>
|
||||
<span class="text-sm text-base mt-0.5">{{ $server->primaryIpv4->ip ?? 'None' }}</span>
|
||||
<span class="text-xs font-medium text-muted-foreground uppercase tracking-wide">IPv4 Address</span>
|
||||
<span class="text-sm mt-0.5">{{ $server->primaryIpv4->ip ?? 'None' }}</span>
|
||||
</div>
|
||||
<div class="flex flex-col min-w-[120px]">
|
||||
<span class="text-xs font-medium text-base/50 uppercase tracking-wide">IPv6 Address</span>
|
||||
<span class="text-sm text-base mt-0.5">{{ $server->primaryIpv6->ip ?? 'None' }}</span>
|
||||
<span class="text-xs font-medium text-muted-foreground uppercase tracking-wide">IPv6 Address</span>
|
||||
<span class="text-sm mt-0.5">{{ $server->primaryIpv6->ip ?? 'None' }}</span>
|
||||
</div>
|
||||
@if($status->status == 'running' && isset($status->uptime))
|
||||
<div class="flex flex-col min-w-[100px]">
|
||||
<span class="text-xs font-medium text-base/50 uppercase tracking-wide">Uptime</span>
|
||||
<span class="text-sm text-base mt-0.5">
|
||||
<span class="text-xs font-medium text-muted-foreground uppercase tracking-wide">Uptime</span>
|
||||
<span class="text-sm mt-0.5">
|
||||
@php
|
||||
$uptime = $status->uptime;
|
||||
$days = floor($uptime / 86400);
|
||||
@@ -57,8 +57,8 @@
|
||||
</div>
|
||||
@endif
|
||||
<div class="hidden sm:flex flex-col min-w-[100px]">
|
||||
<span class="text-xs font-medium text-base/50 uppercase tracking-wide">Memory</span>
|
||||
<span class="text-sm text-base mt-0.5">
|
||||
<span class="text-xs font-medium text-muted-foreground uppercase tracking-wide">Memory</span>
|
||||
<span class="text-sm mt-0.5">
|
||||
@if($status->status == 'running')
|
||||
{{ Illuminate\Support\Number::fileSize(bytes: $status->mem) }}/
|
||||
@endif
|
||||
@@ -66,72 +66,69 @@
|
||||
</span>
|
||||
</div>
|
||||
<div class="hidden sm:flex flex-col min-w-[80px]">
|
||||
<span class="text-xs font-medium text-base/50 uppercase tracking-wide">Disk</span>
|
||||
<span class="text-sm text-base mt-0.5">{{ Illuminate\Support\Number::fileSize(bytes: $status->maxdisk) }}</span>
|
||||
<span class="text-xs font-medium text-muted-foreground uppercase tracking-wide">Disk</span>
|
||||
<span class="text-sm mt-0.5">{{ Illuminate\Support\Number::fileSize(bytes: $status->maxdisk) }}</span>
|
||||
</div>
|
||||
@if($status->status == 'running')
|
||||
<div class="hidden sm:flex flex-col min-w-[60px]">
|
||||
<span class="text-xs font-medium text-base/50 uppercase tracking-wide">CPU</span>
|
||||
<span class="text-sm text-base mt-0.5">{{ round($status->cpu * 100, 2) }}%</span>
|
||||
<span class="text-xs font-medium text-muted-foreground uppercase tracking-wide">CPU</span>
|
||||
<span class="text-sm mt-0.5">{{ round($status->cpu * 100, 2) }}%</span>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- Server Configuration Grid --}}
|
||||
<div class="mt-4">
|
||||
<div class="text-xs font-semibold text-base/60 uppercase tracking-wide mb-3">Server Configuration</div>
|
||||
<div class="px-6 py-4">
|
||||
<div class="text-xs font-medium text-muted-foreground uppercase tracking-wide mb-3">Server Configuration</div>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<x-proxmox::ui.stat-card
|
||||
label="CPU Cores"
|
||||
:value="($status->cpus ?? 'N/A') . ' ' . (($status->cpus ?? 0) > 1 ? 'Cores' : 'Core')"
|
||||
icon="ri-cpu-line"
|
||||
variant="default"
|
||||
/>
|
||||
<x-proxmox::ui.stat-card
|
||||
label="RAM Allocation"
|
||||
:value="round(($status->maxmem ?? 0) / (1024 * 1024 * 1024), 2) . ' GiB'"
|
||||
icon="ri-ram-line"
|
||||
variant="default"
|
||||
/>
|
||||
<x-proxmox::ui.stat-card
|
||||
label="Disk Capacity"
|
||||
:value="Illuminate\Support\Number::fileSize(bytes: $status->maxdisk ?? 0)"
|
||||
icon="ri-hard-drive-2-line"
|
||||
variant="default"
|
||||
/>
|
||||
<div class="flex flex-col p-3 bg-neutral/5 rounded-lg">
|
||||
<div class="flex items-center gap-2 mb-1">
|
||||
<x-ri-speed-line class="size-4 text-base/50" />
|
||||
<span class="text-xs text-base/50">Bandwidth</span>
|
||||
<div class="flex flex-col gap-6 rounded-xl border border-neutral/15 bg-card py-6 text-card-foreground shadow-sm">
|
||||
<div class="flex flex-row items-center justify-between space-y-0 px-6 pb-2">
|
||||
<h3 class="text-sm font-medium text-muted-foreground">Bandwidth</h3>
|
||||
<x-ri-speed-line class="size-4 text-muted-foreground" />
|
||||
</div>
|
||||
<div class="px-6">
|
||||
<div class="text-xl font-bold">{{ $this->monthlyTraffic }} GiB</div>
|
||||
<p class="text-xs text-muted-foreground mt-1">
|
||||
of {{ $this->bandwidthThreshold95 }} GiB (95% threshold)
|
||||
</p>
|
||||
</div>
|
||||
<span class="text-sm font-medium text-base">
|
||||
{{ $this->monthlyTraffic }} GiB / {{ $this->bandwidthThreshold95 }} GiB
|
||||
</span>
|
||||
<span class="text-xs text-base/50 mt-1 leading-relaxed">
|
||||
95% threshold or 10 GiB min. Reduced to 1 MiB/s if exceeded.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</x-proxmox::ui.card>
|
||||
</div>
|
||||
</a>
|
||||
@else
|
||||
{{-- Error State: Status unavailable --}}
|
||||
<a href="{{ route('proxmox.server.overview', ['server' => $server->id]) }}" wire:navigate>
|
||||
<x-proxmox::ui.card class="border-danger/30 hover:border-danger/50 transition-colors cursor-pointer">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="flex flex-col rounded-xl border border-danger/20 bg-card text-card-foreground shadow-sm hover:shadow-md hover:border-danger/30 transition-shadow duration-150 cursor-pointer">
|
||||
<div class="flex items-center justify-between px-6 py-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="bg-danger/10 p-2 rounded-lg">
|
||||
<x-ri-server-line class="size-5 text-danger" />
|
||||
<div class="flex items-center justify-center size-9 rounded-lg bg-danger/10">
|
||||
<x-ri-server-line class="size-4 text-danger" />
|
||||
</div>
|
||||
<span class="font-semibold text-base">{{ $server->hostname ?? 'Server #' . $server->id }}</span>
|
||||
<span class="font-semibold">{{ $server->hostname ?? 'Server #' . $server->id }}</span>
|
||||
</div>
|
||||
<x-proxmox::ui.badge variant="danger" icon="ri-error-warning-fill">
|
||||
Unavailable
|
||||
</x-proxmox::ui.badge>
|
||||
</div>
|
||||
<p class="text-sm text-base/60 mt-2">Unable to retrieve server status. Click to view details or try again.</p>
|
||||
</x-proxmox::ui.card>
|
||||
<p class="text-sm text-muted-foreground px-6 pb-4">Unable to retrieve server status. Click to view details or try again.</p>
|
||||
</div>
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user