@php $formatNumber = function ($value) { return number_format((float) $value); }; $formatPercent = function ($value) { return number_format((float) $value, 1) . '%'; }; $formatDate = function ($value) { return $value ? \Carbon\Carbon::parse($value)->format('M j, Y H:i') : 'Never'; }; $formatShortDate = function ($value) { return $value ? \Carbon\Carbon::parse($value)->format('M j, Y') : 'Never'; }; $proLabel = function ($mode) { if ((int) $mode === 2) { return 'AI Pro'; } if ((int) $mode === 1) { return 'Pro'; } return 'Free'; }; $proClass = function ($mode) { if ((int) $mode === 2) { return 'bg-violet-100 text-violet-700 ring-violet-200'; } if ((int) $mode === 1) { return 'bg-emerald-100 text-emerald-700 ring-emerald-200'; } return 'bg-slate-100 text-slate-600 ring-slate-200'; }; $urlFor = function ($overrides = []) { return route('admin.stats', array_merge(request()->except('page'), $overrides)); }; $presetLinks = [ 'today' => 'Today', 'week' => 'This week', 'month' => 'This month', 'custom' => 'Custom', ]; $maxTopUserAttempts = max(1, (int) $topActiveUsers->max('total_attempts')); $maxTopVerbRecords = max(1, (int) $topVerbs->max('records')); @endphp
Admin dashboard

Conjugate usage overview

{{ $dateRange['label'] }}

Reset

Users

{{ $formatNumber($summary['total_users']) }}

{{ $formatNumber($periodCounts['users']['range']) }} new in range

Pro members

{{ $formatNumber($summary['pro_users']) }}

{{ $formatPercent($summary['pro_rate']) }} conversion, {{ $formatNumber($summary['ai_pro_users']) }} AI Pro

Attempts

{{ $formatNumber($summary['total_attempts']) }}

{{ $formatPercent($summary['accuracy_rate']) }} success, {{ $formatNumber($summary['practice_users']) }} users

Lists created

{{ $formatNumber($summary['total_lists']) }}

{{ $formatNumber($summary['lists_in_range']) }} new in range

New users

Today, week, month, and selected range

Today

{{ $formatNumber($periodCounts['users']['today']) }}

Week

{{ $formatNumber($periodCounts['users']['week']) }}

Month

{{ $formatNumber($periodCounts['users']['month']) }}

Range

{{ $formatNumber($periodCounts['users']['range']) }}

Conjugation activity

Records updated by practice checks

Today

{{ $formatNumber($periodCounts['conjugations']['today']['records']) }}

{{ $formatNumber($periodCounts['conjugations']['today']['users']) }} users

Week

{{ $formatNumber($periodCounts['conjugations']['week']['records']) }}

{{ $formatNumber($periodCounts['conjugations']['week']['users']) }} users

Month

{{ $formatNumber($periodCounts['conjugations']['month']['records']) }}

{{ $formatNumber($periodCounts['conjugations']['month']['users']) }} users

Range

{{ $formatNumber($periodCounts['conjugations']['range']['records']) }}

{{ $formatNumber($periodCounts['conjugations']['range']['users']) }} users

Growth and activity

Selected date range

Membership mix

Account links

Languages

Platforms

Top active users

@forelse ($topActiveUsers as $user)
#{{ $user->id }} {{ $user->email ?: $user->google_email ?: $user->apple_email ?: $user->name ?: 'No email' }} {{ $formatNumber($user->total_attempts) }}
@empty

No activity yet.

@endforelse

Top verbs in range

@forelse ($topVerbs as $verb)
{{ $verb->label }} {{ $formatNumber($verb->records) }}

{{ $formatNumber($verb->users) }} users, {{ $formatNumber($verb->attempts) }} total attempts on active records

@empty

No conjugation records in this range.

@endforelse

Streak and AI usage

Active streaks

{{ $formatNumber($summary['active_streak_users']) }}

Longest

{{ $formatNumber($summary['longest_streak']) }}

Avg current

{{ $summary['avg_current_streak'] }}

AI phrases

{{ $formatNumber($summary['phrase_generations']) }}

Users

Last app use is the latest profile update, practice update, list activity, purchase update, or API token usage.

{{ $formatNumber($userRows->total()) }} matching users
@forelse ($userRows as $user) @empty @endforelse
User Language Platform Member Subscribed Attempts Lists Phrases Created Last app use
#{{ $user->id }} {{ $user->name ?: 'Unnamed user' }}
{{ $user->email ?: $user->google_email ?: $user->apple_email ?: 'No linked email' }}
{{ $user->language ?: 'Unknown' }} {{ $user->platform ?: 'Unknown' }} {{ $proLabel($user->pro_mode) }} {{ $formatShortDate($user->first_subscription_at) }}
{{ $formatNumber($user->total_attempts) }}
{{ $formatNumber($user->attempt_success) }} ok, {{ $formatNumber($user->attempt_fail) }} miss
{{ $formatNumber($user->lists_count) }}
{{ $formatNumber($user->list_verbs_count) }} verbs
{{ $formatNumber($user->nb_phrases) }} {{ $formatShortDate($user->created_at) }} {{ $formatDate($user->last_used_at) }}
No users match the current filters.
{{ $userRows->links() }}

Recent active users

@forelse ($recentActiveUsers as $user)

#{{ $user->id }} {{ $user->email ?: $user->google_email ?: $user->apple_email ?: $user->name ?: 'No email' }}

{{ $user->language ?: 'Unknown' }} ยท {{ $proLabel($user->pro_mode) }}

{{ $formatDate($user->last_used_at) }}
@empty

No recent activity yet.

@endforelse

Tenses practiced in range

@forelse ($topTenses as $tense) @php $tenseAttempts = (int) $tense->success_attempts + (int) $tense->failed_attempts; $tenseAccuracy = $tenseAttempts > 0 ? ((int) $tense->success_attempts / $tenseAttempts) * 100 : 0; @endphp @empty @endforelse
Tense Records Users Accuracy
{{ $tense->label }} {{ $formatNumber($tense->records) }} {{ $formatNumber($tense->users) }} {{ $formatPercent($tenseAccuracy) }}
No tense activity in this range.

All-time attempts are exact sums from user_conjugaison.attempt_success and attempt_fail. Range-based conjugation counts use user_conjugaison.updated_at, because the current schema stores aggregate counters instead of one event row per attempt.