CluPilotCloud/resources/views/livewire/dashboard.blade.php

216 lines
15 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<div class="space-y-4" x-data="{ msgs: @js(['addon' => __('dashboard.modules.add_toast'), 'restore' => __('dashboard.backups.restore_toast')]) }">
{{-- Header --}}
<div class="flex flex-wrap items-center gap-x-4 gap-y-1 animate-rise">
<h1 class="text-2xl font-semibold tracking-tight text-ink">{{ __('dashboard.greeting', ['name' => auth()->user()->name]) }}</h1>
<span class="ml-auto inline-flex items-center gap-2 rounded-pill bg-success-bg px-3.5 py-1.5 text-xs font-semibold text-success">
<span class="size-2 rounded-pill bg-success-bright" aria-hidden="true"></span>{{ __('dashboard.system_ok') }}
</span>
<p class="w-full text-sm text-muted">{{ __('dashboard.as_of', ['time' => '08:42']) }}</p>
</div>
{{-- Live provisioning progress (only while a run is in flight) --}}
<livewire:customer-provisioning />
{{-- KPI row --}}
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4">
{{-- Storage ring --}}
<div class="flex items-center gap-4 rounded-xl border border-line bg-surface p-5 shadow-xs animate-rise [animation-delay:60ms]">
<div class="relative size-24 shrink-0" wire:ignore>
<x-ui.chart :config="$storageChart" class="size-24" :label="__('dashboard.kpi.storage')" />
<div class="pointer-events-none absolute inset-0 grid place-items-center text-center">
<div>
<span class="font-mono text-lg font-semibold text-ink">{{ $storagePercent }}%</span>
<span class="block text-[0.62rem] text-muted">{{ __('dashboard.occupied') }}</span>
</div>
</div>
</div>
<div>
<p class="text-xs font-semibold uppercase tracking-wide text-faint">{{ __('dashboard.kpi.storage') }}</p>
<p class="mt-1 font-mono text-xl font-semibold text-ink">{{ $storageUsed }}<span class="ml-1 text-sm font-normal text-muted">/ {{ $storageQuota }} GB</span></p>
<p class="mt-0.5 text-xs text-muted">{{ __('dashboard.storage_week') }}</p>
</div>
</div>
{{-- Users --}}
<div class="rounded-xl border border-line bg-surface p-5 shadow-xs animate-rise [animation-delay:120ms]">
<p class="text-xs font-semibold uppercase tracking-wide text-faint">{{ __('dashboard.kpi.users') }}</p>
<p class="mt-2 font-mono text-2xl font-semibold text-ink">{{ $usersActive }}<span class="ml-1 text-sm font-normal text-muted">/ {{ $usersQuota }}</span></p>
<p class="mt-1 text-xs text-muted">{{ __('dashboard.users_detail') }}</p>
</div>
{{-- Last backup --}}
<div class="rounded-xl border border-line bg-surface p-5 shadow-xs animate-rise [animation-delay:180ms]">
<p class="text-xs font-semibold uppercase tracking-wide text-faint">{{ __('dashboard.kpi.backup') }}</p>
<p class="mt-2 flex items-center gap-1.5 font-mono text-2xl font-semibold text-success">
{{ $lastBackup }}<x-ui.icon name="check" class="size-5" />
</p>
<p class="mt-1 text-xs text-muted">{{ __('dashboard.backup_verified') }}</p>
</div>
{{-- Availability sparkline --}}
<div class="rounded-xl border border-line bg-surface p-5 shadow-xs animate-rise [animation-delay:240ms]">
<p class="text-xs font-semibold uppercase tracking-wide text-faint">{{ __('dashboard.kpi.availability') }}</p>
<p class="mt-2 font-mono text-2xl font-semibold text-ink">{{ $availability }}<span class="ml-1 text-sm font-normal text-muted">%</span></p>
<div class="mt-2 h-8" wire:ignore>
<x-ui.chart :config="$availabilityChart" class="h-8" :label="__('dashboard.kpi.availability')" />
</div>
</div>
</div>
{{-- Upsell --}}
<div class="flex flex-wrap items-center gap-4 rounded-xl border border-accent-border bg-accent-subtle px-5 py-4 animate-rise [animation-delay:300ms]">
<x-ui.icon name="alert-triangle" class="size-6 shrink-0 text-accent-active" />
<div class="min-w-0">
<p class="text-sm font-semibold text-ink">{{ __('dashboard.upsell.title', ['percent' => $storagePercent]) }}</p>
<p class="text-sm text-muted">{{ __('dashboard.upsell.body') }}</p>
</div>
<x-ui.button variant="secondary" size="sm" class="ml-auto" @click="$dispatch('notify', { message: msgs.addon })">
{{ __('dashboard.upsell.cta') }}
</x-ui.button>
</div>
<div class="grid grid-cols-1 gap-4 lg:grid-cols-[1.35fr_1fr] lg:items-start">
{{-- Left column --}}
<div class="flex flex-col gap-4">
{{-- Cloud card --}}
<div class="rounded-xl border border-line bg-surface p-5 shadow-xs animate-rise [animation-delay:360ms]">
<div class="flex flex-wrap items-center gap-3">
<span class="grid size-11 place-items-center rounded-lg bg-ink font-semibold text-on-accent">B</span>
<div class="min-w-0">
<p class="font-semibold text-ink">{{ $cloud['name'] }}</p>
<a href="https://{{ $cloud['domain'] }}" target="_blank" rel="noopener noreferrer" class="font-mono text-sm text-accent-text hover:underline">{{ $cloud['domain'] }}</a>
</div>
<a href="https://{{ $cloud['domain'] }}" target="_blank" rel="noopener noreferrer"
class="ml-auto inline-flex items-center gap-2 rounded-pill bg-accent-active px-4 py-2 text-sm font-semibold text-on-accent transition hover:bg-accent-press">
<x-ui.icon name="external-link" class="size-4" />{{ __('dashboard.cloud.open') }}
</a>
</div>
<div class="mt-4 grid grid-cols-1 gap-3 border-t border-line pt-4 sm:grid-cols-2">
<div><p class="text-xs font-semibold uppercase tracking-wide text-faint">{{ __('dashboard.cloud.package') }}</p><p class="mt-1 text-sm text-body">{{ $cloud['package'] }}</p></div>
<div><p class="text-xs font-semibold uppercase tracking-wide text-faint">{{ __('dashboard.cloud.status') }}</p><p class="mt-1 text-sm text-success">{{ __('dashboard.cloud.active_since', ['date' => $cloud['since']]) }}</p></div>
<div><p class="text-xs font-semibold uppercase tracking-wide text-faint">{{ __('dashboard.cloud.location') }}</p><p class="mt-1 text-sm text-body">{{ $cloud['location'] }}</p></div>
<div><p class="text-xs font-semibold uppercase tracking-wide text-faint">{{ __('dashboard.cloud.version') }}</p><p class="mt-1 font-mono text-sm text-body">{{ $cloud['version'] }} · {{ __('dashboard.cloud.current') }}</p></div>
</div>
</div>
{{-- Onboarding checklist (Alpine) --}}
<div
class="rounded-xl border border-line bg-surface p-5 shadow-xs animate-rise [animation-delay:420ms]"
x-data="{
tasks: @js(collect($onboarding)->map(fn ($t) => ['label' => $t['label'], 'done' => $t['done'], 'date' => $t['date']])),
total: {{ count($onboarding) }},
get doneCount() { return this.tasks.filter(t => t.done).length; },
get percent() { return Math.round(this.doneCount / this.total * 100); },
complete(i) {
if (this.tasks[i].done) return;
this.tasks[i].done = true;
$dispatch('notify', { message: @js(__('dashboard.onboarding.done_toast')) });
},
nextIndex() { return this.tasks.findIndex(t => !t.done); },
}"
>
<div class="flex items-baseline gap-3">
<h2 class="font-semibold text-ink">{{ __('dashboard.onboarding.title') }}</h2>
<span class="ml-auto font-mono text-sm font-semibold text-muted" x-text="doneCount + ' / ' + total"></span>
</div>
<div class="mt-3 h-1.5 overflow-hidden rounded-pill bg-surface-2">
<div class="h-full rounded-pill bg-accent transition-all duration-700" :style="`width: ${percent}%`"></div>
</div>
<ul class="mt-2">
<template x-for="(task, i) in tasks" :key="i">
<li>
<button
type="button"
class="flex w-full items-center gap-3 rounded-lg px-2.5 py-3 text-left text-sm transition hover:bg-surface-hover"
:class="!task.done && i === nextIndex() ? 'bg-accent-subtle' : ''"
:disabled="task.done"
@click="complete(i)"
>
<span
class="grid size-6 shrink-0 place-items-center rounded-pill border"
:class="task.done ? 'border-success-bright bg-success-bright text-on-accent' : (i === nextIndex() ? 'border-accent' : 'border-line-strong')"
>
<svg x-show="task.done" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" class="size-3.5" aria-hidden="true"><polyline points="20 6 9 17 4 12"/></svg>
</span>
<span class="min-w-0 flex-1" :class="task.done ? 'text-muted line-through' : 'text-body'" x-text="task.label"></span>
<span x-show="task.date" class="text-xs text-faint" x-text="task.date"></span>
<span x-show="!task.done && i === nextIndex()" class="text-xs font-semibold text-accent-text whitespace-nowrap">{{ __('dashboard.onboarding.review') }} </span>
</button>
</li>
</template>
</ul>
</div>
</div>
{{-- Right column --}}
<div class="flex flex-col gap-4">
{{-- Backups --}}
<div class="rounded-xl border border-line bg-surface p-5 shadow-xs animate-rise [animation-delay:360ms]">
<div class="flex items-center">
<h2 class="font-semibold text-ink">{{ __('dashboard.backups.title') }}</h2>
<span class="ml-auto rounded-pill bg-success-bg px-2.5 py-1 text-xs font-semibold text-success">{{ __('dashboard.backups.schedule') }}</span>
</div>
<ul class="mt-2 divide-y divide-line">
@foreach ($backups as $b)
<li class="flex items-center gap-3 py-2.5 text-sm">
<x-ui.icon name="check" class="size-4 text-success" />
<span class="font-mono text-xs text-muted">{{ $b['when'] }}</span>
<span class="ml-auto font-mono text-xs text-muted">{{ $b['size'] }}</span>
<x-ui.button variant="secondary" size="sm" @click="$dispatch('notify', { message: msgs.restore })">{{ __('dashboard.backups.restore') }}</x-ui.button>
</li>
@endforeach
</ul>
<p class="mt-3 flex gap-2 text-xs leading-relaxed text-muted">
<x-ui.icon name="shield" class="mt-px size-4 shrink-0 text-accent-active" />
<span>{{ __('dashboard.backups.note') }}</span>
</p>
</div>
{{-- Modules --}}
<div class="rounded-xl border border-line bg-surface p-5 shadow-xs animate-rise [animation-delay:420ms]">
<div class="flex items-center">
<h2 class="font-semibold text-ink">{{ __('dashboard.modules.title') }}</h2>
<span class="ml-auto rounded-pill bg-surface-2 px-2.5 py-1 text-xs font-semibold text-muted">{{ __('dashboard.modules.addon') }}</span>
</div>
<ul class="mt-2 divide-y divide-line">
@foreach ($modules as $m)
<li class="flex items-center gap-3 py-2.5 text-sm">
<span class="grid size-9 shrink-0 place-items-center rounded-lg bg-surface-2 text-muted"><x-ui.icon :name="$m['icon']" class="size-5" /></span>
<div class="min-w-0">
<p class="font-medium text-ink">{{ $m['name'] }}</p>
<p class="text-xs text-muted">{{ $m['desc'] }}</p>
</div>
@if ($m['status'] === 'active')
<span class="ml-auto rounded-pill bg-success-bg px-2.5 py-1 text-xs font-semibold text-success">{{ __('dashboard.modules.active') }}</span>
@elseif ($m['status'] === 'available')
<x-ui.button variant="secondary" size="sm" class="ml-auto" @click="$dispatch('notify', { message: msgs.addon })">{{ __('dashboard.modules.add') }}</x-ui.button>
@else
<span class="ml-auto rounded-pill bg-surface-2 px-2.5 py-1 text-xs font-semibold text-muted">{{ __('dashboard.modules.soon') }}</span>
@endif
</li>
@endforeach
</ul>
</div>
{{-- Activity feed --}}
<div class="rounded-xl border border-line bg-surface p-5 shadow-xs animate-rise [animation-delay:480ms]">
<div class="flex items-center">
<h2 class="font-semibold text-ink">{{ __('dashboard.activity') }}</h2>
<span class="ml-auto rounded-pill bg-surface-2 px-2.5 py-1 text-xs font-semibold text-muted">{{ __('dashboard.activity_live') }}</span>
</div>
<ul class="mt-2 divide-y divide-line">
@foreach ($activity as $a)
<li class="flex items-start gap-3 py-2.5 text-sm">
<span class="grid size-7 shrink-0 place-items-center rounded-lg bg-surface-2 text-accent-active"><x-ui.icon :name="$a['icon']" class="size-4" /></span>
<div class="min-w-0 flex-1">
<p class="text-body">{{ $a['text'] }}</p>
<p class="font-mono text-xs text-faint">{{ $a['time'] }}</p>
</div>
</li>
@endforeach
</ul>
</div>
</div>
</div>
</div>