@extends('layouts.portal')
@section('title', 'Digital badges')
@section('subtitle', 'The badge catalogue, who holds each badge, and manual awards. Every issued badge has a public verification page.')
@section('actions')
New badge
@endsection
@section('content')
@php
$levels = config('infotics.badges.levels');
$byLevel = $badges->groupBy('level');
$known = array_keys($levels);
$others = $badges->reject(fn ($b) => in_array($b->level, $known, true));
$activeCount = $badges->where('status', 'active')->count();
$validCount = (int) ($stats['issued'] ?? 0);
$revokedCount = (int) ($stats['revoked'] ?? 0);
$top = $badges->sortByDesc('issuances_count')->first();
$criteriaLabel = ['manual' => 'Manual award', 'course' => 'Course completion', 'exam' => 'Exam pass'];
$errorsOnIssue = $errors->any() && old('_form') === 'issue';
@endphp
Badges in catalogue{{ $badges->count() }}{{ $activeCount }} active{{ $badges->count() > $activeCount ? ' · '.($badges->count() - $activeCount).' draft or archived' : '' }}
Valid badges held{{ number_format($validCount) }}Publicly verifiable right now
Revoked{{ number_format($revokedCount) }}Shown as revoked on the verify page
Most awarded{{ $top && $top->issuances_count ? \Illuminate\Support\Str::limit($top->name, 26) : 'None yet' }}{{ $top && $top->issuances_count ? number_format($top->issuances_count).' issued' : 'Issue the first badge' }}
Catalogue
Grouped by level. Artwork is generated from the level colour unless you upload your own.
@if($badges->isEmpty())
@endif
@foreach(array_merge($known, $others->isNotEmpty() ? ['Other'] : []) as $level)
@php $group = $level === 'Other' ? $others : ($byLevel[$level] ?? collect()); @endphp
@continue($group->isEmpty())
@php $cfg = $levels[$level] ?? ['color' => '#0A66FF', 'stars' => 1]; @endphp
@for($s = 0; $s < $cfg['stars']; $s++)@endfor
{{ $level }}
{{ $group->count() }}
@foreach($group as $b)
@if($b->status !== 'active')@endif
{{ $b->name }}
{{ $criteriaLabel[$b->criteria_type] ?? 'Manual award' }} · {{ number_format($b->issuances_count) }} issued
Edit
@endforeach
@endforeach
Issued badges
{{ number_format($issuances->total()) }} {{ \Illuminate\Support\Str::plural('record', $issuances->total()) }}{{ $filters ? ' match your search' : '' }}
@if($issuances->isEmpty())
@else
| Holder | Badge | Issued | Status | Verify | Actions |
@foreach($issuances as $i)
@php $st = $i->effectiveStatus(); @endphp
@if($i->user?->avatarUrl()) @else{{ $i->user?->initials() ?? '?' }}@endif{{ $i->user?->name ?? 'Deleted user' }}{{ $i->user?->email }} |
{{ $i->badge?->name ?? 'Deleted badge' }}{{ $i->badge_id_code }} |
{{ $i->issued_at?->format('d M Y') ?? '—' }}{{ $i->expires_at ? 'Expires '.$i->expires_at->format('d M Y') : 'No expiry' }} |
@if($st === 'revoked' && $i->revoke_reason){{ \Illuminate\Support\Str::limit($i->revoke_reason, 26) }}@endif |
|
@if($st !== 'revoked')
@else
{{ $i->revoked_at?->format('d M Y') }}
@endif
|
@endforeach
{{ $issuances->links() }}
@endif
{{-- Issue badge --}}
Issue a badge
The learner is emailed a link to their badge with share buttons.
@php $firstActive = $badges->firstWhere('status', 'active'); @endphp
@if(! $firstActive)
There is no active badge to issue. Activate or create a badge first.
@else
@endif
{{-- Revoke badge --}}
The public verification page will immediately show this badge as revoked, and any linked certificate is revoked with it. This cannot be undone.
@push('scripts')@endpush
@endsection