feat: Enhance UI components with improved styles and responsiveness across various views

This commit is contained in:
2026-04-22 10:56:39 -04:00
parent 8baf7d6c7d
commit 71b031ca64
14 changed files with 108 additions and 103 deletions

View File

@@ -24,7 +24,9 @@ Alpine.store('notifications', {
}, },
addNotification(notification) { addNotification(notification) {
notification = notification[0]; // Livewire dispatches pass args as an array; unwrap if needed.
if (Array.isArray(notification)) notification = notification[0];
if (!notification || typeof notification !== 'object') return;
notification.show = false; notification.show = false;
notification.id = Date.now() + Math.floor(Math.random() * 1000); notification.id = Date.now() + Math.floor(Math.random() * 1000);
notification.type = notification.type || 'success'; notification.type = notification.type || 'success';

View File

@@ -1,6 +1,6 @@
<div class="bg-background-secondary/50 bg-background-secondary/50 backdrop-blur-md grid w-full max-w-6xl mx-auto overflow-hidden text-color-base rounded-2xl shadow-2xl md:grid-cols-2" style="margin: 50px auto;"> <div class="bg-background-secondary/50 bg-background-secondary/50 backdrop-blur-md grid w-full max-w-6xl mx-auto overflow-hidden text-color-base rounded-2xl shadow-floating md:grid-cols-2 relative" style="margin: 50px auto;">
<div class="hidden md:flex flex-col justify-between p-10 animated-gradient bg-gradient-to-br from-primary to-secondary"> <div class="hidden md:flex flex-col justify-between p-10 animated-gradient bg-gradient-to-br from-primary to-secondary relative overflow-hidden noise-overlay">
<div> <div class="relative z-10">
<div class="mt-8 text-white"> <div class="mt-8 text-white">
<h2 class="text-4xl font-extrabold leading-tight tracking-tight"> <h2 class="text-4xl font-extrabold leading-tight tracking-tight">
Manage Your Billing with Ease Manage Your Billing with Ease
@@ -11,7 +11,7 @@
</p> </p>
</div> </div>
</div> </div>
<p class="text-sm text-white/70">© {{ date('Y') }} {{ config('app.name') }} | All rights reserved.</p> <p class="text-sm text-white/70 relative z-10">© {{ date('Y') }} {{ config('app.name') }} | All rights reserved.</p>
</div> </div>
<div class="p-8 sm:p-12 form-fade-in"> <div class="p-8 sm:p-12 form-fade-in">

View File

@@ -1,5 +1,5 @@
<div class="bg-background-secondary/50 bg-background-secondary/50 backdrop-blur-md grid w-full max-w-6xl mx-auto overflow-hidden text-color-base rounded-2xl shadow-2xl md:grid-cols-2"> <div class="bg-background-secondary/50 bg-background-secondary/50 backdrop-blur-md grid w-full max-w-6xl mx-auto overflow-hidden text-color-base rounded-2xl shadow-floating md:grid-cols-2">
<div class="hidden md:flex flex-col justify-between p-10 animated-gradient bg-gradient-to-br from-primary to-secondary"> <div class="hidden md:flex flex-col justify-between p-10 animated-gradient bg-gradient-to-br from-primary to-secondary relative overflow-hidden noise-overlay">
<div> <div>
<div class="mt-8 text-white"> <div class="mt-8 text-white">
<h2 class="text-4xl font-extrabold leading-tight tracking-tight"> <h2 class="text-4xl font-extrabold leading-tight tracking-tight">

View File

@@ -10,7 +10,7 @@
<div class="space-y-4"> <div class="space-y-4">
@foreach (Auth::user()->sessions->filter(fn ($session) => !$session->impersonating()) as $session) @foreach (Auth::user()->sessions->filter(fn ($session) => !$session->impersonating()) as $session)
<div class="flex flex-col sm:flex-row items-start sm:items-center justify-between py-4 border-b border-neutral-700 last:border-b-0"> <div class="flex flex-col sm:flex-row items-start sm:items-center justify-between py-4 border-b border-neutral last:border-b-0">
<div> <div>
<p class="text-lg font-semibold text-color-base mb-1">{{ $session->ip_address }}</p> <p class="text-lg font-semibold text-color-base mb-1">{{ $session->ip_address }}</p>
<p class="text-sm text-color-muted">{{ $session->formatted_device }} - {{ $session->last_activity->diffForHumans() }}</p> <p class="text-sm text-color-muted">{{ $session->formatted_device }} - {{ $session->last_activity->diffForHumans() }}</p>
@@ -65,7 +65,7 @@
<x-modal :title="__('account.two_factor_authentication_enable')" open="true"> <x-modal :title="__('account.two_factor_authentication_enable')" open="true">
<p class="text-color-base mb-4">{{ __('account.two_factor_authentication_enable_description') }}</p> <p class="text-color-base mb-4">{{ __('account.two_factor_authentication_enable_description') }}</p>
<div class="flex flex-col items-center mt-4 mb-6"> <div class="flex flex-col items-center mt-4 mb-6">
<img src="{{ $twoFactorData['image'] }}" alt="QR code" class="w-64 h-64 border border-neutral-700 rounded-lg p-2 bg-background-tertiary" /> <img src="{{ $twoFactorData['image'] }}" alt="QR code" class="w-64 h-64 border border-neutral rounded-lg p-2 bg-background-tertiary" />
<p class="text-color-muted mt-4 text-sm text-center font-mono break-all"> <p class="text-color-muted mt-4 text-sm text-center font-mono break-all">
{{ __('account.two_factor_authentication_secret') }}<br /> {{ __('account.two_factor_authentication_secret') }}<br />
<span class="text-color-base text-base font-bold">{{ $twoFactorData['secret'] }}</span> <span class="text-color-base text-base font-bold">{{ $twoFactorData['secret'] }}</span>

View File

@@ -6,6 +6,7 @@
'trend' => null, 'trend' => null,
'icon' => null, 'icon' => null,
'href' => null, 'href' => null,
'tone' => 'primary', // primary | success | warning | info | error | secondary
]) ])
@php @php
$trendClass = match($trend) { $trendClass = match($trend) {
@@ -19,10 +20,26 @@
'down' => 'ri-arrow-down-line', 'down' => 'ri-arrow-down-line',
default => null, default => null,
}; };
$toneBg = match($tone) {
'success' => 'bg-success/10 group-hover:bg-success/20',
'warning' => 'bg-warning/10 group-hover:bg-warning/20',
'info' => 'bg-info/10 group-hover:bg-info/20',
'error' => 'bg-error/10 group-hover:bg-error/20',
'secondary' => 'bg-secondary/10 group-hover:bg-secondary/20',
default => 'bg-primary/10 group-hover:bg-primary/20',
};
$toneText = match($tone) {
'success' => 'text-success',
'warning' => 'text-warning',
'info' => 'text-info',
'error' => 'text-error',
'secondary' => 'text-secondary',
default => 'text-primary',
};
$tag = $href ? 'a' : 'div'; $tag = $href ? 'a' : 'div';
@endphp @endphp
<{{ $tag }} @if($href) href="{{ $href }}" wire:navigate @endif <{{ $tag }} @if($href) href="{{ $href }}" wire:navigate @endif
{{ $attributes->merge(['class' => 'card-glass card-glass-hover block p-5 group']) }}> {{ $attributes->merge(['class' => 'card-glass card-glass-hover hover-lift block p-5 group']) }}>
<div class="flex items-start justify-between gap-3"> <div class="flex items-start justify-between gap-3">
<div class="min-w-0 flex-1"> <div class="min-w-0 flex-1">
<p class="text-xs font-semibold tracking-wider uppercase text-base/60">{{ $label }}</p> <p class="text-xs font-semibold tracking-wider uppercase text-base/60">{{ $label }}</p>
@@ -38,7 +55,7 @@
@endif @endif
</div> </div>
@if($icon) @if($icon)
<div class="size-10 rounded-lg bg-primary/10 text-primary flex items-center justify-center shrink-0 group-hover:bg-primary/20 transition-colors"> <div class="size-10 rounded-lg {{ $toneBg }} {{ $toneText }} flex items-center justify-center shrink-0 transition-colors">
<x-dynamic-component :component="$icon" class="size-5" /> <x-dynamic-component :component="$icon" class="size-5" />
</div> </div>
@endif @endif

View File

@@ -107,60 +107,30 @@
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 lg:gap-6 mb-6"> <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 lg:gap-6 mb-6">
@endif @endif
<div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral/50 rounded-xl p-6 shadow-lg transform transition-all duration-300 hover:scale-[1.02] hover:shadow-xl animate-fade-in-up" <x-card.stat
style="animation-delay: 0.1s;"> :label="__('dashboard.active_services')"
<div class="flex items-center justify-between"> :value="Auth::user()->services()->where('status', 'active')->count()"
<div> icon="ri-archive-stack-fill"
<div class="bg-primary/10 text-primary p-2 rounded-full inline-flex mb-3"> :href="route('services')"
<x-ri-archive-stack-fill class="size-5" /> tone="primary"
</div> data-tilt />
<h3 class="text-sm font-medium text-color-muted mb-1">{{ __('dashboard.active_services') }}</h3>
<p class="text-2xl font-bold text-color-base">{{ Auth::user()->services()->where('status',
'active')->count() }}</p>
</div>
<a href="{{ route('services') }}"
class="text-primary hover:text-blue-700 transition-colors duration-300">
<x-ri-arrow-right-line class="size-5" />
</a>
</div>
</div>
<div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral/50 rounded-xl p-6 shadow-lg transform transition-all duration-300 hover:scale-[1.02] hover:shadow-xl animate-fade-in-up" <x-card.stat
style="animation-delay: 0.2s;"> :label="__('dashboard.unpaid_invoices')"
<div class="flex items-center justify-between"> :value="Auth::user()->invoices()->where('status', 'pending')->count()"
<div> icon="ri-receipt-fill"
<div class="bg-orange-500/10 text-orange-500 p-2 rounded-full inline-flex mb-3"> :href="route('invoices')"
<x-ri-receipt-fill class="size-5" /> tone="warning"
</div> data-tilt />
<h3 class="text-sm font-medium text-color-muted mb-1">{{ __('dashboard.unpaid_invoices') }}</h3>
<p class="text-2xl font-bold text-color-base">{{ Auth::user()->invoices()->where('status',
'pending')->count() }}</p>
</div>
<a href="{{ route('invoices') }}"
class="text-orange-500 hover:text-orange-700 transition-colors duration-300">
<x-ri-arrow-right-line class="size-5" />
</a>
</div>
</div>
@if(!config('settings.tickets_disabled', false)) @if(!config('settings.tickets_disabled', false))
<div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral/50 rounded-xl p-6 shadow-lg transform transition-all duration-300 hover:scale-[1.02] hover:shadow-xl animate-fade-in-up" <x-card.stat
style="animation-delay: 0.3s;"> :label="__('dashboard.open_tickets')"
<div class="flex items-center justify-between"> :value="Auth::user()->tickets()->where('status', '!=', 'closed')->count()"
<div> icon="ri-customer-service-fill"
<div class="bg-green-500/10 text-green-500 p-2 rounded-full inline-flex mb-3"> :href="route('tickets')"
<x-ri-customer-service-fill class="size-5" /> tone="info"
</div> data-tilt />
<h3 class="text-sm font-medium text-color-muted mb-1">{{ __('dashboard.open_tickets') }}</h3>
<p class="text-2xl font-bold text-color-base">{{ Auth::user()->tickets()->where('status', '!=',
'closed')->count() }}</p>
</div>
<a href="{{ route('tickets') }}"
class="text-green-500 hover:text-green-700 transition-colors duration-300">
<x-ri-arrow-right-line class="size-5" />
</a>
</div>
</div>
@endif @endif
</div> </div>
@@ -202,13 +172,13 @@
style="animation-delay: 0.6s;"> style="animation-delay: 0.6s;">
<div class="flex items-center justify-between mb-6 border-b border-dashed border-neutral/50 pb-4"> <div class="flex items-center justify-between mb-6 border-b border-dashed border-neutral/50 pb-4">
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<div class="bg-orange-500/10 text-orange-500 p-3 rounded-full flex-shrink-0 shadow-md"> <div class="bg-warning/10 text-warning p-3 rounded-full flex-shrink-0 shadow-md">
<x-ri-receipt-fill class="size-6" /> <x-ri-receipt-fill class="size-6" />
</div> </div>
<h2 class="text-2xl font-bold text-color-base">{{ __('dashboard.unpaid_invoices') }}</h2> <h2 class="text-2xl font-bold text-color-base">{{ __('dashboard.unpaid_invoices') }}</h2>
</div> </div>
<span <span
class="bg-orange-500 text-white text-lg font-bold rounded-full size-10 flex items-center justify-center shadow-lg transform transition-transform duration-300 hover:scale-110"> class="bg-warning text-white tnum text-lg font-bold rounded-full size-10 flex items-center justify-center shadow-lg transform transition-transform duration-300 hover:scale-110">
{{ Auth::user()->invoices()->where('status', 'pending')->count() }} {{ Auth::user()->invoices()->where('status', 'pending')->count() }}
</span> </span>
</div> </div>
@@ -308,13 +278,13 @@
style="animation-delay: 0.7s;"> style="animation-delay: 0.7s;">
<div class="flex items-center justify-between mb-6 border-b border-dashed border-neutral/50 pb-4"> <div class="flex items-center justify-between mb-6 border-b border-dashed border-neutral/50 pb-4">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<div class="bg-green-500/10 text-green-500 p-2 rounded-full flex-shrink-0"> <div class="bg-info/10 text-info p-2 rounded-full flex-shrink-0">
<x-ri-customer-service-fill class="size-5" /> <x-ri-customer-service-fill class="size-5" />
</div> </div>
<h2 class="text-xl font-bold text-color-base">{{ __('dashboard.open_tickets') }}</h2> <h2 class="text-xl font-bold text-color-base">{{ __('dashboard.open_tickets') }}</h2>
</div> </div>
<a href="{{ route('tickets.create') }}" wire:navigate <a href="{{ route('tickets.create') }}" wire:navigate
class="bg-green-500/10 text-green-500 p-1.5 rounded-lg hover:bg-green-500 hover:text-white transition-colors"> class="bg-info/10 text-info p-1.5 rounded-lg hover:bg-info hover:text-white transition-colors">
<x-ri-add-line class="size-5" /> <x-ri-add-line class="size-5" />
</a> </a>
</div> </div>

View File

@@ -11,6 +11,8 @@
SECTION 1 — HERO (full viewport, always visible) SECTION 1 — HERO (full viewport, always visible)
═══════════════════════════════════════════════════════════════ --}} ═══════════════════════════════════════════════════════════════ --}}
<section class="relative flex items-center overflow-hidden" style="min-height: calc(100dvh - var(--nav-h, 4rem));"> <section class="relative flex items-center overflow-hidden" style="min-height: calc(100dvh - var(--nav-h, 4rem));">
<!-- Depth grid overlay -->
<div class="absolute inset-0 grid-bg opacity-40 pointer-events-none"></div>
<!-- Parallax ambient blobs --> <!-- Parallax ambient blobs -->
<div class="absolute inset-0 pointer-events-none overflow-hidden"> <div class="absolute inset-0 pointer-events-none overflow-hidden">
<div class="absolute -top-40 -right-40 w-[500px] h-[500px] bg-primary/20 rounded-full blur-[120px] animate-pulse" <div class="absolute -top-40 -right-40 w-[500px] h-[500px] bg-primary/20 rounded-full blur-[120px] animate-pulse"

View File

@@ -40,18 +40,18 @@
@forelse ($invoices as $invoice) @forelse ($invoices as $invoice)
<a href="{{ route('invoices.show', $invoice) }}" wire:navigate class="block"> <a href="{{ route('invoices.show', $invoice) }}" wire:navigate class="block">
<div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 hover:bg-secondary/25 border border-neutral/50 p-6 rounded-xl shadow-lg <div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 hover:bg-secondary/25 border border-neutral/50 p-6 rounded-xl shadow-lg hover-lift
transition-all duration-300 hover:scale-[1.01] hover:shadow-xl hover:border-primary/50 transition-all duration-300 hover:shadow-xl hover:border-primary/50
flex flex-col md:flex-row items-start md:items-center justify-between gap-4"> flex flex-col md:flex-row items-start md:items-center justify-between gap-4">
<div class="flex items-start md:items-center gap-4 flex-grow"> <div class="flex items-start md:items-center gap-4 flex-grow">
<div class="bg-blue-500/10 p-3 rounded-full flex-shrink-0 shadow-sm"> <div class="bg-primary/10 p-3 rounded-full flex-shrink-0 shadow-sm">
<x-ri-bill-line class="size-6 text-blue-500" /> <x-ri-bill-line class="size-6 text-primary" />
</div> </div>
<div class="flex flex-col"> <div class="flex flex-col">
<span class="text-xl font-bold text-color-base leading-tight">{{ __('Invoice') }} #{{ $invoice->number }}</span> <span class="text-xl font-bold text-color-base leading-tight">{{ __('Invoice') }} #{{ $invoice->number }}</span>
<p class="text-color-muted text-sm mt-1"> <p class="text-color-muted text-sm mt-1">
{{ __('Total') }}: <span class="font-bold text-primary">{{ $invoice->formattedTotal }}</span> {{ __('Total') }}: <span class="font-bold tnum text-primary">{{ $invoice->formattedTotal }}</span>
<span class="mx-2 text-color-muted/50">•</span> <span class="mx-2 text-color-muted/50">•</span>
{{ __('invoices.invoice_date') }}: {{ $invoice->created_at->format('d M Y') }} {{ __('invoices.invoice_date') }}: {{ $invoice->created_at->format('d M Y') }}
</p> </p>
@@ -86,8 +86,8 @@
</a> </a>
@empty @empty
<div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 rounded-xl shadow-lg p-8 lg:p-12 border border-neutral/50 text-center group"> <div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 rounded-xl shadow-lg p-8 lg:p-12 border border-neutral/50 text-center group">
<div class="bg-blue-500/10 rounded-full p-6 w-fit mx-auto mb-6 transition-transform duration-300 group-hover:scale-110 empty-state-float"> <div class="bg-primary/10 rounded-full p-6 w-fit mx-auto mb-6 transition-transform duration-300 group-hover:scale-110 empty-state-float">
<x-ri-file-list-3-line class="size-12 text-blue-500" /> <x-ri-file-list-3-line class="size-12 text-primary" />
</div> </div>
<h3 class="text-2xl font-bold text-color-base mb-3">No invoices yet</h3> <h3 class="text-2xl font-bold text-color-base mb-3">No invoices yet</h3>
<p class="text-color-muted mb-6 max-w-md mx-auto">Your invoices will appear here once you make a purchase. Browse our products to get started.</p> <p class="text-color-muted mb-6 max-w-md mx-auto">Your invoices will appear here once you make a purchase. Browse our products to get started.</p>

View File

@@ -1,7 +1,7 @@
<div class="space-y-4"> <div class="space-y-4">
@foreach ($invoices as $invoice) @foreach ($invoices as $invoice)
<a href="{{ route('invoices.show', $invoice) }}" wire:navigate> <a href="{{ route('invoices.show', $invoice) }}" wire:navigate>
<div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral/50 p-4 rounded-lg mb-4"> <div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral/50 p-4 rounded-lg mb-4 hover-lift">
<div class="flex items-center justify-between mb-2"> <div class="flex items-center justify-between mb-2">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<div class="bg-secondary/10 p-2 rounded-full"> <div class="bg-secondary/10 p-2 rounded-full">
@@ -11,7 +11,7 @@
<span class="text-base/50 font-semibold"> <span class="text-base/50 font-semibold">
<x-ri-circle-fill class="size-1 text-base/20" /> <x-ri-circle-fill class="size-1 text-base/20" />
</span> </span>
<span class="text-base text-sm">{{ $invoice->formattedTotal }}</span> <span class="text-base text-sm tnum">{{ $invoice->formattedTotal }}</span>
</div> </div>
<div class="size-5 rounded-full p-0.5 <div class="size-5 rounded-full p-0.5
@if ($invoice->status == 'paid') text-success bg-success/20 @if ($invoice->status == 'paid') text-success bg-success/20

View File

@@ -8,8 +8,11 @@
<div class="p-6 lg:p-8"> <div class="p-6 lg:p-8">
<div class="flex items-start justify-between mb-6"> <div class="flex items-start justify-between mb-6">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<div class="size-2 bg-green-500 rounded-full animate-pulse"></div> <span class="relative flex size-2.5">
<span class="text-xs font-medium text-green-600 uppercase tracking-wide">Available</span> <span class="absolute inline-flex h-full w-full rounded-full bg-success opacity-60 animate-ping"></span>
<span class="relative inline-flex size-2.5 rounded-full bg-success"></span>
</span>
<span class="text-xs font-medium text-success uppercase tracking-wide">Available</span>
</div> </div>
</div> </div>
@@ -269,12 +272,12 @@
&& auth()->check() && empty(auth()->user()->tfa_secret) && auth()->check() && empty(auth()->user()->tfa_secret)
) )
{{-- TFA Required --}} {{-- TFA Required --}}
<div class="p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-xl"> <div class="p-4 bg-error/10 border border-error/30 rounded-xl">
<div class="flex items-center gap-2 text-red-600 dark:text-red-400"> <div class="flex items-center gap-2 text-error">
<x-ri-error-warning-fill class="size-5" /> <x-ri-error-warning-fill class="size-5" />
<span class="font-medium">Two-Factor Authentication Required</span> <span class="font-medium">Two-Factor Authentication Required</span>
</div> </div>
<p class="text-sm text-red-500 dark:text-red-300 mt-1"> <p class="text-sm text-error/80 mt-1">
You must enable two-factor authentication to order this product. You must enable two-factor authentication to order this product.
</p> </p>
</div> </div>
@@ -286,24 +289,24 @@
&& !auth()->check() && !auth()->check()
) )
{{-- Login Required --}} {{-- Login Required --}}
<div class="p-4 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-xl"> <div class="p-4 bg-info/10 border border-info/30 rounded-xl">
<div class="flex items-center gap-2 text-blue-600 dark:text-blue-400"> <div class="flex items-center gap-2 text-info">
<x-ri-information-fill class="size-5" /> <x-ri-information-fill class="size-5" />
<span class="font-medium">Login and Two-Factor Authentication Required</span> <span class="font-medium">Login and Two-Factor Authentication Required</span>
</div> </div>
<p class="text-sm text-blue-500 dark:text-blue-300 mt-1"> <p class="text-sm text-info/80 mt-1">
You must be logged in to order this product and enable two-factor authentication. You must be logged in to order this product and enable two-factor authentication.
</p> </p>
</div> </div>
@else @else
{{-- Currently Unavailable --}} {{-- Currently Unavailable --}}
<div class="p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-xl"> <div class="p-4 bg-error/10 border border-error/30 rounded-xl">
<div class="flex items-center gap-2 text-red-600 dark:text-red-400"> <div class="flex items-center gap-2 text-error">
<x-ri-error-warning-fill class="size-5" /> <x-ri-error-warning-fill class="size-5" />
<span class="font-medium">Currently Unavailable</span> <span class="font-medium">Currently Unavailable</span>
</div> </div>
<p class="text-sm text-red-500 dark:text-red-300 mt-1"> <p class="text-sm text-error/80 mt-1">
This product is currently out of stock or not available for purchase. This product is currently out of stock or not available for purchase.
</p> </p>
</div> </div>

View File

@@ -9,11 +9,15 @@
@endif @endif
<div class="flex flex-col gap-8"> <div class="flex flex-col gap-8">
@if ($product->stock === 0) @if ($product->stock === 0)
<span class="text-xs font-semibold px-3 py-1 rounded-full bg-red-800 text-red-100 w-fit"> <span class="text-xs font-semibold px-3 py-1 rounded-full bg-error/15 text-error border border-error/30 w-fit">
{{ __('product.out_of_stock', ['product' => $product->name]) }} {{ __('product.out_of_stock', ['product' => $product->name]) }}
</span> </span>
@elseif($product->stock > 0) @elseif($product->stock > 0)
<span class="text-xs font-semibold px-3 py-1 rounded-full bg-green-800 text-green-100 w-fit"> <span class="inline-flex items-center gap-1.5 text-xs font-semibold px-3 py-1 rounded-full bg-success/15 text-success border border-success/30 w-fit">
<span class="relative flex size-2">
<span class="absolute inline-flex h-full w-full rounded-full bg-success opacity-60 animate-ping"></span>
<span class="relative inline-flex size-2 rounded-full bg-success"></span>
</span>
{{ __('product.in_stock') }} {{ __('product.in_stock') }}
</span> </span>
@endif @endif

View File

@@ -37,7 +37,7 @@
<div class="bg-success/10 border border-success/50 rounded-xl p-4 text-center status-card-hover"> <div class="bg-success/10 border border-success/50 rounded-xl p-4 text-center status-card-hover">
<div class="flex items-center justify-center mb-2"> <div class="flex items-center justify-center mb-2">
<x-ri-checkbox-circle-fill class="size-6 text-success mr-2" /> <x-ri-checkbox-circle-fill class="size-6 text-success mr-2" />
<span class="text-2xl font-bold text-success">{{ $services->where('status', 'active')->count() }}</span> <span class="text-2xl font-bold tnum text-success">{{ $services->where('status', 'active')->count() }}</span>
</div> </div>
<p class="text-sm font-medium text-success">{{ __('Active') }}</p> <p class="text-sm font-medium text-success">{{ __('Active') }}</p>
</div> </div>
@@ -45,23 +45,23 @@
<div class="bg-warning/10 border border-warning/50 rounded-xl p-4 text-center status-card-hover"> <div class="bg-warning/10 border border-warning/50 rounded-xl p-4 text-center status-card-hover">
<div class="flex items-center justify-center mb-2"> <div class="flex items-center justify-center mb-2">
<x-ri-error-warning-fill class="size-6 text-warning mr-2" /> <x-ri-error-warning-fill class="size-6 text-warning mr-2" />
<span class="text-2xl font-bold text-warning">{{ $services->where('status', 'pending')->count() }}</span> <span class="text-2xl font-bold tnum text-warning">{{ $services->where('status', 'pending')->count() }}</span>
</div> </div>
<p class="text-sm font-medium text-warning">{{ __('Pending') }}</p> <p class="text-sm font-medium text-warning">{{ __('Pending') }}</p>
</div> </div>
<div class="bg-error/10 border border-error/50 rounded-xl p-4 text-center status-card-hover"> <div class="bg-error/10 border border-error/50 rounded-xl p-4 text-center status-card-hover">
<div class="flex items-center justify-center mb-2"> <div class="flex items-center justify-center mb-2">
<x-ri-forbid-fill class="size-6 text-red-500 mr-2" /> <x-ri-forbid-fill class="size-6 text-error mr-2" />
<span class="text-2xl font-bold text-red-500">{{ $services->where('status', 'suspended')->count() }}</span> <span class="text-2xl font-bold tnum text-error">{{ $services->where('status', 'suspended')->count() }}</span>
</div> </div>
<p class="text-sm font-medium text-red-500">{{ __('Suspended') }}</p> <p class="text-sm font-medium text-error">{{ __('Suspended') }}</p>
</div> </div>
<div class="bg-inactive/10 border border-inactive/50 rounded-xl p-4 text-center status-card-hover"> <div class="bg-inactive/10 border border-inactive/50 rounded-xl p-4 text-center status-card-hover">
<div class="flex items-center justify-center mb-2"> <div class="flex items-center justify-center mb-2">
<x-ri-close-circle-fill class="size-6 text-inactive mr-2" /> <x-ri-close-circle-fill class="size-6 text-inactive mr-2" />
<span class="text-2xl font-bold text-inactive">{{ $services->where('status', 'cancelled')->count() }}</span> <span class="text-2xl font-bold tnum text-inactive">{{ $services->where('status', 'cancelled')->count() }}</span>
</div> </div>
<p class="text-sm font-medium text-inactive">{{ __('Cancelled') }}</p> <p class="text-sm font-medium text-inactive">{{ __('Cancelled') }}</p>
</div> </div>
@@ -86,8 +86,8 @@
@forelse ($services as $service) @forelse ($services as $service)
<a href="{{ route('services.show', $service) }}" wire:navigate class="block"> <a href="{{ route('services.show', $service) }}" wire:navigate class="block">
<div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 hover:bg-secondary/25 border border-neutral/50 p-6 rounded-xl shadow-lg <div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 hover:bg-secondary/25 border border-neutral/50 p-6 rounded-xl shadow-lg hover-lift
transition-all duration-300 hover:scale-[1.02] hover:shadow-xl hover:border-primary/50 transition-all duration-300 hover:shadow-xl hover:border-primary/50
flex flex-col md:flex-row items-start md:items-center justify-between gap-4"> flex flex-col md:flex-row items-start md:items-center justify-between gap-4">
<div class="flex items-start md:items-center gap-4 flex-grow"> <div class="flex items-start md:items-center gap-4 flex-grow">
@@ -140,7 +140,7 @@
<div class="flex items-center gap-3 flex-shrink-0 mt-3 md:mt-0"> <div class="flex items-center gap-3 flex-shrink-0 mt-3 md:mt-0">
<div class="text-sm font-semibold px-3 py-1 rounded-full flex items-center gap-1.5 transition-all duration-200 <div class="text-sm font-semibold px-3 py-1 rounded-full flex items-center gap-1.5 transition-all duration-200
@if ($service->status == 'active') text-success bg-success/20 status-pulse @if ($service->status == 'active') text-success bg-success/20 status-pulse
@elseif($service->status == 'suspended') text-red-500 bg-red-500/20 @elseif($service->status == 'suspended') text-error bg-error/20
@elseif($service->status == 'cancelled') text-inactive bg-inactive/20 @elseif($service->status == 'cancelled') text-inactive bg-inactive/20
@else text-warning bg-warning/20 @else text-warning bg-warning/20
@endif"> @endif">

View File

@@ -1,7 +1,7 @@
<div class="space-y-4"> <div class="space-y-4">
@foreach ($services as $service) @foreach ($services as $service)
<a href="{{ route('services.show', $service) }}" wire:navigate> <a href="{{ route('services.show', $service) }}" wire:navigate>
<div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral p-4 rounded-lg mb-4"> <div class="bg-gradient-to-br from-background-secondary/50 to-background-secondary/30 border border-neutral p-4 rounded-lg mb-4 hover-lift">
<div class="flex items-center justify-between mb-2"> <div class="flex items-center justify-between mb-2">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<div class="bg-secondary/10 p-2 rounded-full"> <div class="bg-secondary/10 p-2 rounded-full">

View File

@@ -12,15 +12,22 @@
<div class="bg-background-secondary/50 border border-neutral p-6 rounded-xl shadow-lg mb-6"> <div class="bg-background-secondary/50 border border-neutral p-6 rounded-xl shadow-lg mb-6">
<div class="flex flex-col gap-5 max-h-[60vh] overflow-y-auto pr-4 custom-scrollbar" wire:poll.5s> <div class="flex flex-col gap-5 max-h-[60vh] overflow-y-auto pr-4 custom-scrollbar" wire:poll.5s>
@foreach ($ticket->messages()->with('user')->get() as $index => $message) @foreach ($ticket->messages()->with('user')->get() as $index => $message)
@php
$isMine = $message->user_id === auth()->id();
$isStaff = ! $isMine && ($message->user->role_id ?? null);
@endphp
<div <div
class="bg-background/10 border border-neutral/20 p-4 rounded-lg class="p-4 rounded-lg w-full max-w-[80%] transition-all duration-200
w-full max-w-[80%] {{ $message->user_id === auth()->id() ? 'ml-auto bg-primary/10 border-primary/20' : 'mr-auto' }} {{ $isMine ? 'ml-auto bg-primary/15 border border-primary/25' : 'mr-auto glass-panel' }}
transition-all duration-200"> {{ $isStaff ? 'border-l-2 border-l-secondary' : '' }}">
<div class="flex items-center justify-between mb-2"> <div class="flex items-center justify-between mb-2">
<div> <div class="flex items-center gap-2">
<h3 class="font-semibold text-color-base text-lg">{{ $message->user->name }}</h3> <h3 class="font-semibold text-color-base text-lg">{{ $message->user->name }}</h3>
<p class="text-sm text-neutral-400">{{ $message->created_at->diffForHumans() }}</p> @if($isStaff)
<span class="text-[10px] uppercase tracking-wider font-bold px-1.5 py-0.5 rounded bg-secondary/15 text-secondary">{{ __('Staff') }}</span>
@endif
</div> </div>
<p class="text-sm text-neutral-400">{{ $message->created_at->diffForHumans() }}</p>
</div> </div>
<div class="mt-2 prose dark:prose-invert max-w-none text-color-base"> <div class="mt-2 prose dark:prose-invert max-w-none text-color-base">
{!! Str::markdown(nl2br(e($message->message)), ['allow_unsafe_links' => false]) !!} {!! Str::markdown(nl2br(e($message->message)), ['allow_unsafe_links' => false]) !!}