@extends('layouts.portal') @section('title', 'Letters') @section('subtitle', 'Issue verifiable letters such as training completion or enrollment confirmation, from reusable templates.') @section('actions') Issue a letter @endsection @section('content') @php $supported = [ 'name' => 'Learner’s full name', 'email' => 'Learner’s email address', 'course' => 'Course or programme, typed when you issue the letter', 'details' => 'Extra details, typed when you issue the letter', 'date' => 'Issue date, e.g. '.now()->format('d F Y'), 'organisation' => config('infotics.brand.legal_name'), ]; $errorsOnTpl = $errors->any() && old('_form') === 'tpl'; $errorsOnIssue = $errors->any() && old('_form') === 'letter'; $tplPlaceholders = function ($body) use ($supported) { preg_match_all('/\{\{([^{}]*)\}\}/', (string) $body, $m); return collect($m[1])->unique()->map(fn ($k) => ['key' => $k, 'ok' => array_key_exists($k, $supported)])->values(); }; $latest = $letters->first(); $ph = fn ($k) => '{'.'{'.$k.'}'.'}'; @endphp
Templates{{ $templates->count() }}{{ $templates->where('active', true)->count() }} active
Letters issued{{ number_format($letters->total()) }}Each one is emailed and verifiable
Latest letter{{ $latest?->issued_at?->format('d M Y') ?? 'None yet' }}{{ $latest ? ($latest->user?->name ?? '') : 'Issue the first letter below' }}

Issue a letter

The learner receives an email with a link to the letter and its verification page.
@if($templates->isEmpty()) @else
@csrf
@error('template_id')
{{ $message }}
@enderror
@error('user_id')
{{ $message }}
@enderror
Fills @{{course}} in the template.
@error('course')
{{ $message }}
@enderror
Fills @{{details}}. Maximum 500 characters.
@error('extra')
{{ $message }}
@enderror

Preview

Updates as you type
{{ config('infotics.brand.legal_name') }}{{ setting('address') }}
@endif

Templates

Use the exact placeholders below, without spaces inside the braces.
@if($templates->isEmpty())
@else
@foreach($templates as $t) @php $found = $tplPlaceholders($t->body_template); @endphp

{{ $t->name }}

{{ $t->type }} · version {{ $t->version }} · {{ $t->letters()->count() }} issued

{{ \Illuminate\Support\Str::limit(preg_replace('/\s+/', ' ', $t->body_template), 170) }}

@forelse($found as $p){{ $ph($p['key']) }}@emptyNo placeholders@endforelse
@endforeach
@endif

Issued letters

{{ number_format($letters->total()) }} in total
@if($letters->isEmpty())
@else
@foreach($letters as $l) @php $vurl = route('letters.verify', $l->letter_id); @endphp @endforeach
LetterRecipientIssuedStatusOpen
{{ $l->type }}{{ $l->letter_id }}
@if($l->user?->avatarUrl())@else{{ $l->user?->initials() ?? '?' }}@endif
{{ $l->user?->name ?? 'Deleted user' }}
{{ $l->user?->email }}
{{ $l->issued_at?->format('d M Y') ?? '—' }}
{{ $letters->links() }} @endif
{{-- Create / edit template --}} @push('scripts')@endpush @endsection