@use('App\Models\License') @php /* | Everything the row needs is derived once, from relations the controller | already eager-loaded. Nothing here queries. */ $customer = $license->customer; $domain = $license->domain?->domain; $expires = $license->expires_at; $status = $license->effectiveStatus(); // Signed whole days: negative once the date has passed. $days = $expires ? (int) round(now()->startOfDay()->diffInDays($expires->copy()->startOfDay(), false)) : null; // "Expiring soon" is not a stored status — it is an active licence close to // its date. Folding it in here is what lets the badge warn before the // renewal is already late. $isExpiring = $status === License::STATUS_ACTIVE && $days !== null && $days >= 0 && $days <= $expiringDays; $badge = $isExpiring ? 'expiring' : $status; $expiryTone = match (true) { $days === null => 'text-slate-400 dark:text-slate-500', $days < 0 => 'text-red-600 dark:text-red-400', $days <= $expiringDays => 'text-amber-600 dark:text-amber-400', default => 'text-emerald-600 dark:text-emerald-400', }; $canManage = auth()->user()?->isOwner() ?? false; // Blade compiles {{ }} inside a component tag's attributes but not @js(), // so anything handed to Alpine through has to be encoded // here instead. Js::from() escapes quotes as \uXXXX, which survives both // the attribute and htmlspecialchars untouched. $keyJs = \Illuminate\Support\Js::from($license->license_key); $domainJs = \Illuminate\Support\Js::from($domain); @endphp {{-- The data-* attributes are the client-side model: refinement, sorting and the CSV export all read them, so nothing has to be re-derived in JS. --}} {{-- License key: the identifier people actually paste into a support ticket, so copying it must not cost a page load. --}}
{{ $license->license_key }} #{{ $license->id }} · {{ $license->duration_days ? $license->duration_days.' days' : 'No fixed term' }}
{{-- Customer: one cell carrying the whole identity, so the eye does not have to join two columns to know whose licence this is. --}} @if ($customer)
{{-- Customer pages sit behind the owner middleware, so a viewer gets the name as text rather than a 403. --}} @if ($canManage) {{ $customer->company_name }} @else {{ $customer->company_name }} @endif {{ $customer->email }}
@else — @endif @if ($domain)
{{ $domain }}
@else Not activated @endif
{{ $license->package?->name ?? '—' }} {{ $license->package?->duration_days ? $license->package->duration_days.'-day term' : 'Custom term' }}
{{ $license->created_at?->format('d M Y') ?? '—' }}
@if ($expires)
{{ $expires->format('d M Y') }} @if ($days < 0) Expired {{ abs($days) }} {{ Str::plural('day', abs($days)) }} ago @elseif ($days === 0) Expires today @else {{ $days }} {{ Str::plural('day', $days) }} left @endif
@else Never expires @endif View details @if ($canManage) {{-- There is no separate licence edit screen; every change is made from the detail page, so "Edit" goes there rather than to a route that does not exist. --}} Edit & manage @endif
Copy license key @if ($domain) Copy domain @else Copy domain @endif @if ($canManage)
{{-- Each of these is a POST that changes state on a live installation, so none of them can be a link. --}}
@csrf Renew
@csrf Extend 30 days
@if ($license->status === License::STATUS_ACTIVE)
@csrf Suspend
@elseif (in_array($license->status, [License::STATUS_SUSPENDED, License::STATUS_PENDING], true))
@csrf Activate
@else Activate @endif @if ($domain)
@csrf Push status now
@else Push status now @endif
{{-- Revoke, not delete: licences are never removed from this system — the audit trail depends on the row surviving. --}}
@csrf Revoke license
@endif