finity-labs/fin-mail
FinMail adds an email template manager and composer to Filament. Create dynamic, translatable templates with token/merge-tag replacement, CTA blocks, and versioning. Send from any resource via a reusable action, with full email logging and status tracking.
Installation:
composer require finity-labs/fin-mail
php artisan fin-mail:install
Follow prompts for locales, migrations, and plugin registration.
Register Plugin:
// app/Providers/Filament/AdminPanelProvider.php
public function panel(Panel $panel): Panel
{
return $panel->plugins([
FinMailPlugin::make(),
]);
}
First Use Case: Send an email from a Filament resource:
use FinityLabs\FinMail\Actions\SendEmailAction;
// In your resource's table actions
SendEmailAction::make()
->template('welcome-email')
->recipient(fn ($record) => $record->user->email)
->models(fn ($record) => ['user' => $record->user]);
Email Templates to create/edit templates.Template Creation & Management:
{{ model.attribute }} or merge tags in the editor toolbar.Sending Emails:
SendEmailAction to table/header actions with dynamic recipients/models:
SendEmailAction::make()
->template('invoice')
->recipient(fn ($invoice) => $invoice->customer_email)
->models(fn ($invoice) => ['invoice' => $invoice]);
TemplateMail with Mail::to()->send():
Mail::to($user->email)->send(
TemplateMail::make('welcome')
->models(['user' => $user])
->attachFile($filePath)
);
Token & Data Handling:
{{ user.name }} or {{ config.app.name }}.->with('tracking_url', $tracking->url)
// or
->extraData(['items' => $order->items]);
Logging & Tracking:
SentEmail model).SentEmailsRelationManager to resources to view sent emails:
public static function getRelations(): array {
return [SentEmailsRelationManager::class];
}
Customization:
DividerBlock):
FinMailPlugin::make()->customBlocks([\App\Mail\Blocks\MyBlock::class]);
TemplateMail::make('template')->overrideView('emails.custom');
FinMailPlugin::make()->policyNamespace('App\Policies');
spatie/laravel-translatable.config/fin-mail.php for async sending:
'queue' => [
'connection' => 'mail',
'queue' => 'emails',
],
php artisan fin-mail:test to send test emails.Token Parsing Errors:
{{ user.address.city }} fail if address is null.{{ user.address.city | 'N/A' }}
Tokens tab in the template editor for parsed errors.Editor Blocks Not Rendering:
toHtml() returns valid HTML and implements getId().toPreviewHtml() first.Locale Mismatches:
[en] placeholders.php artisan fin-mail:install with correct locales or manually publish translations:
php artisan vendor:publish --tag=fin-mail-translations
Attachment Limits:
config/fin-mail.php:
'attachments' => [
'max_size_mb' => 10,
],
Queue Stuck Jobs:
EmailFailed events to retry or log:
Event::listen(EmailFailed::class, function ($event) {
// Retry logic or notify admins
});
config/fin-mail.php:
'debug' => env('FIN_MAIL_DEBUG', false),
Event::listen('*', function ($event) {
logger()->debug($event::class, [$event]);
});
Custom Token Parsers:
Extend FinityLabs\FinMail\Tokens\TokenParser to add custom syntax (e.g., {{ math:add(1,2) }}):
public function parse($token, $context): string {
if (str_starts_with($token, 'math:')) {
return eval("return {$token};");
}
return parent::parse($token, $context);
}
Email Validation:
Override FinityLabs\FinMail\Mail\TemplateMail to add validation:
public static function validate($template, $models, $attachments) {
if (empty($models['user']->email)) {
throw new \Exception('User email is required.');
}
}
Dynamic Recipients:
Use closures in SendEmailAction to fetch recipients dynamically:
->recipient(fn ($record) => $record->getEmailsForTemplate('newsletter'))
Theme Overrides: Extend themes by publishing assets:
php artisan vendor:publish --tag=fin-mail-themes
Then modify resources/views/vendor/fin-mail/themes/custom.blade.php.
config/fin-mail.php:
'sender' => [
'address' => 'noreply@example.com',
'name' => 'Your App',
],
'attachments' => [
'allowed_mimes' => ['pdf', 'jpg', 'png'],
],
FinMailPlugin::make()->disableSentEmails();
$template->compileTokens(); // Run manually or via observer
TemplateMail::make()->disableLogging();
Mail::later()->send($mailable);
How can I help you explore Laravel packages today?