New features with Mail and SMS

This commit is contained in:
Anna-Sara Sélea 2026-05-21 18:40:00 +02:00
parent ee718a3a7d
commit 0b1a3ae96f
28 changed files with 656 additions and 85 deletions

View file

@ -1,10 +1,11 @@
FROM php:8.2-apache-buster
FROM php:8.2-apache-bookworm
RUN usermod -u 1000 www-data
RUN a2enmod rewrite
RUN apt-get update \
&& apt-get install -y gnupg2 zlib1g-dev libzip-dev zlib1g-dev libpng-dev libfreetype6-dev libjpeg62-turbo-dev libmcrypt-dev libxml2-dev
&& apt-get install -y gnupg2 zlib1g-dev libzip-dev libpng-dev libfreetype6-dev libjpeg62-turbo-dev libxml2-dev \
&& rm -rf /var/lib/apt/lists/*
RUN docker-php-ext-install zip mysqli pdo pdo_mysql && docker-php-ext-enable mysqli pdo pdo_mysql sodium
RUN docker-php-ext-configure gd
RUN docker-php-ext-configure gd --with-freetype --with-jpeg
RUN docker-php-ext-install gd
RUN docker-php-ext-configure intl
RUN docker-php-ext-install intl

View file

@ -0,0 +1,77 @@
<?php
namespace App\Filament\Resources\EmailLogs;
use App\Filament\Resources\EmailLogs\Pages\ListEmailLogs;
use App\Models\EmailLog;
use BackedEnum;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class EmailLogResource extends Resource
{
protected static ?string $model = EmailLog::class;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedClipboardDocumentList;
protected static ?string $navigationLabel = 'Email log';
protected static \UnitEnum|string|null $navigationGroup = 'Logs';
protected static ?int $navigationSort = 100;
public static function table(Table $table): Table
{
return $table
->columns([
TextColumn::make('created_at')
->label('Sent at')
->dateTime()
->sortable(),
TextColumn::make('lan_id')
->label('LAN ID')
->sortable()
->searchable(),
TextColumn::make('guardian_email')
->label('Email')
->searchable(),
TextColumn::make('mailtemplate.title')
->label('Mail template')
->searchable(),
TextColumn::make('smstemplate.title')
->label('SMS template')
->default('—'),
TextColumn::make('participant.first_name')
->label('First name')
->searchable(),
TextColumn::make('participant.surname')
->label('Surname')
->searchable(),
TextColumn::make('error')
->label('Error')
->wrap()
->color('danger')
->default('—')
->toggleable(isToggledHiddenByDefault: true),
])
->defaultSort('created_at', 'desc')
->filters([])
->recordActions([])
->toolbarActions([]);
}
public static function getPages(): array
{
return [
'index' => ListEmailLogs::route('/'),
];
}
public static function canCreate(): bool
{
return false;
}
}

View file

@ -0,0 +1,16 @@
<?php
namespace App\Filament\Resources\EmailLogs\Pages;
use App\Filament\Resources\EmailLogs\EmailLogResource;
use Filament\Resources\Pages\ListRecords;
class ListEmailLogs extends ListRecords
{
protected static string $resource = EmailLogResource::class;
protected function getHeaderActions(): array
{
return [];
}
}

View file

@ -18,7 +18,13 @@ class MailtemplateResource extends Resource
{
protected static ?string $model = Mailtemplate::class;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedRectangleStack;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedEnvelope;
protected static ?string $navigationLabel = 'Mail templates';
protected static \UnitEnum|string|null $navigationGroup = 'Templates';
protected static ?int $navigationSort = 99;
public static function form(Schema $schema): Schema
{

View file

@ -2,9 +2,9 @@
namespace App\Filament\Resources\Mailtemplates\Schemas;
use App\Models\Smstemplate;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\RichEditor;
use Filament\Forms\Components\Toggle;
use Filament\Schemas\Schema;
use Filament\Forms\Components\MarkdownEditor;
@ -25,6 +25,11 @@ class MailtemplateForm
MarkdownEditor::make('content')
->required()
->columnSpanFull(),
Select::make('smstemplate_id')
->label('SMS template')
->options(Smstemplate::where('draft', false)->pluck('title', 'id'))
->nullable()
->columnSpanFull(),
Toggle::make('draft')
->default(false)
->onColor('warning')

View file

@ -7,6 +7,7 @@ use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use Filament\Tables\Columns\ToggleColumn;
class MailtemplatesTable
{
@ -16,6 +17,8 @@ class MailtemplatesTable
->columns([
TextColumn::make('title')
->searchable(),
ToggleColumn::make('draft')
->sortable(),
TextColumn::make('created_at')
->dateTime()
->sortable()

View file

@ -2,6 +2,7 @@
namespace App\Filament\Resources\Participants\Schemas;
use Filament\Forms\Components\Placeholder;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Schemas\Schema;
@ -10,6 +11,7 @@ use Filament\Forms\Components\Select;
use Filament\Forms\Components\Textarea;
use Filament\Schemas\Components\Grid;
use DiscoveryDesign\FilamentGaze\Forms\Components\GazeBanner;
use Illuminate\Support\HtmlString;
class ParticipantForm
{
@ -101,6 +103,29 @@ class ParticipantForm
->columnSpan('full'),
Textarea::make('comment')
->columnSpan('full'),
Placeholder::make('email_log')
->label('Emails sent')
->columnSpan('full')
->hidden(fn ($record) => $record === null)
->content(function ($record) {
if (!$record) {
return '—';
}
$logs = $record->emailLogs()->with(['mailtemplate', 'smstemplate'])->latest()->get();
if ($logs->isEmpty()) {
return new HtmlString('<span class="text-gray-400 text-sm">No emails sent</span>');
}
$rows = $logs->map(function ($log) {
$date = $log->created_at->format('Y-m-d H:i');
$mail = e($log->mailtemplate?->title ?? '—');
$sms = $log->smstemplate ? ' + SMS: ' . e($log->smstemplate->title) : '';
$error = $log->error
? ' <span class="text-red-500 font-medium">&#9888; ' . e($log->error) . '</span>'
: '';
return "<li class=\"text-sm py-1 border-b border-gray-100 last:border-0\"><span class=\"text-gray-400\">{$date}</span> &mdash; {$mail}{$sms}{$error}</li>";
})->join('');
return new HtmlString("<ul class=\"divide-y divide-gray-100\">{$rows}</ul>");
}),
]);
}
}

View file

@ -2,12 +2,15 @@
namespace App\Filament\Resources\Participants\Tables;
use Filament\Actions\BulkAction;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Illuminate\Database\Eloquent\Collection;
use Filament\Actions\EditAction;
use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
use App\Models\EmailLog;
use App\Models\Participant;
use App\Models\Mailtemplate;
use Filament\Tables\Columns\SelectColumn;
@ -32,9 +35,11 @@ class ParticipantsTable
{
return $table
->paginated([10, 25, 50, 100, 'all'])
->defaultPaginationPageOption(100)
->groups([
Group::make('status')
->label('Status')
->getTitleFromRecordUsing(fn ($record) => ucfirst($record->status))
->collapsible(),
])
->defaultGroup('status')
@ -59,7 +64,6 @@ class ParticipantsTable
TextColumn::make('status')
->label('Status')
->badge()
->summarize(Count::make()->label(''))
->color(fn (string $state): string => match ($state) {
'lan' => 'success',
'reserv' => 'warning',
@ -73,7 +77,8 @@ class ParticipantsTable
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('first_name')
->searchable()
->sortable(),
->sortable()
->summarize(Count::make()->label('')),
TextColumn::make('surname')
->searchable()
->sortable(),
@ -114,7 +119,16 @@ class ParticipantsTable
->sortable(),
IconColumn::make('emailed')
->boolean()
->sortable(),
->sortable()
->tooltip(function ($record) {
$logs = $record->emailLogs()->with(['mailtemplate', 'smstemplate'])->latest()->get();
if ($logs->isEmpty()) {
return 'No emails sent';
}
return $logs->map(fn($log) =>
' Mail: ' . ($log->mailtemplate?->title ?? '—')
)->join("\n");
}),
TextColumn::make('comment')
->sortable()
->toggleable(isToggledHiddenByDefault: true),
@ -161,34 +175,72 @@ class ParticipantsTable
->options(Mailtemplate::where('draft', false)->pluck('title', 'id'))
])
->action(function (array $data, Participant $record) {
$mailContent = Mailtemplate::where('id', $data['mailtemplate'])->get();
$mailtemplate = Mailtemplate::with('smstemplate')->find($data['mailtemplate']);
if ($record->guardian_email) {
$error = null;
try {
Mail::to($record->guardian_email)
->send(new LanMail($mailContent, $record));
->send(new LanMail(collect([$mailtemplate]), $record));
Participant::where('id', $record->id)->update(['emailed' => true]);
if ($mailtemplate->smstemplate && config('app.smsUrl')) {
Mail::to(config('app.smsUrl'))
->send(new SmsMail($record));
})
->hidden(fn($record) => $record->emailed),
Action::make('sendRemindEmail')
->label('Send remind email')
->send(new SmsMail($record, $mailtemplate->smstemplate->content));
}
} catch (\Throwable $e) {
$error = $e->getMessage();
}
EmailLog::create([
'participant_id' => $record->id,
'lan_id' => $record->lan_id,
'guardian_email' => $record->guardian_email,
'mailtemplate_id' => $mailtemplate->id,
'smstemplate_id' => $mailtemplate->smstemplate?->id,
'error' => $error,
]);
}
}),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
BulkAction::make('bulkSendEmail')
->label('Send email')
->icon(Heroicon::Envelope)
->schema([
Select::make('mailtemplate')
->label('Mailtemplate')
->options(Mailtemplate::where('draft', false)->pluck('title', 'id'))
->required(),
])
->action(function (array $data, Participant $record) {
$mailContent = Mailtemplate::where('id', $data['mailtemplate'])->get();
->action(function (Collection $records, array $data) {
$mailtemplate = Mailtemplate::with('smstemplate')->find($data['mailtemplate']);
foreach ($records as $record) {
if (!$record->guardian_email) {
continue;
}
$error = null;
try {
Mail::to($record->guardian_email)
->queue(new LanMail($mailContent, $record));
Participant::where('id', $record->id)->update(['emailed' => true]);
->queue(new LanMail(collect([$mailtemplate]), $record));
if ($mailtemplate->smstemplate && config('app.smsUrl')) {
Mail::to(config('app.smsUrl'))
->queue(new SmsMail($record, $mailtemplate->smstemplate->content));
}
} catch (\Throwable $e) {
$error = $e->getMessage();
}
EmailLog::create([
'participant_id' => $record->id,
'lan_id' => $record->lan_id,
'guardian_email' => $record->guardian_email,
'mailtemplate_id' => $mailtemplate->id,
'smstemplate_id' => $mailtemplate->smstemplate?->id,
'error' => $error,
]);
}
$records->toQuery()->whereDoesntHave('emailLogs', fn ($q) => $q->whereNotNull('error'))->update(['emailed' => true]);
})
->hidden(fn($record) => !$record->emailed),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
->deselectRecordsAfterCompletion(),
]),
]);
}

View file

@ -0,0 +1,11 @@
<?php
namespace App\Filament\Resources\Smstemplates\Pages;
use App\Filament\Resources\Smstemplates\SmstemplatesResource;
use Filament\Resources\Pages\CreateRecord;
class CreateSmstemplates extends CreateRecord
{
protected static string $resource = SmstemplatesResource::class;
}

View file

@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\Smstemplates\Pages;
use App\Filament\Resources\Smstemplates\SmstemplatesResource;
use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord;
class EditSmstemplates extends EditRecord
{
protected static string $resource = SmstemplatesResource::class;
protected function getHeaderActions(): array
{
return [
DeleteAction::make(),
];
}
}

View file

@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\Smstemplates\Pages;
use App\Filament\Resources\Smstemplates\SmstemplatesResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ListRecords;
class ListSmstemplates extends ListRecords
{
protected static string $resource = SmstemplatesResource::class;
protected function getHeaderActions(): array
{
return [
CreateAction::make(),
];
}
}

View file

@ -0,0 +1,29 @@
<?php
namespace App\Filament\Resources\Smstemplates\Schemas;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Schemas\Schema;
use Filament\Forms\Components\MarkdownEditor;
use DiscoveryDesign\FilamentGaze\Forms\Components\GazeBanner;
class SmstemplatesForm
{
public static function configure(Schema $schema): Schema
{
return $schema
->components([
GazeBanner::make()
->pollTimer(10)
->hideOnCreate(),
TextInput::make('title')
->required()
->columnSpanFull(),
Toggle::make('draft')
->default(false)
->onColor('warning')
->columnSpanFull(),
]);
}
}

View file

@ -0,0 +1,54 @@
<?php
namespace App\Filament\Resources\Smstemplates;
use App\Filament\Resources\Smstemplates\Pages\CreateSmstemplates;
use App\Filament\Resources\Smstemplates\Pages\EditSmstemplates;
use App\Filament\Resources\Smstemplates\Pages\ListSmstemplates;
use App\Filament\Resources\Smstemplates\Schemas\SmstemplatesForm;
use App\Filament\Resources\Smstemplates\Tables\SmstemplatesTable;
use App\Models\Smstemplate;
use BackedEnum;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Table;
class SmstemplatesResource extends Resource
{
protected static ?string $model = Smstemplate::class;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedChatBubbleLeftRight;
protected static ?string $navigationLabel = 'SMS templates';
protected static \UnitEnum|string|null $navigationGroup = 'Templates';
protected static ?int $navigationSort = 99;
public static function form(Schema $schema): Schema
{
return SmstemplatesForm::configure($schema);
}
public static function table(Table $table): Table
{
return SmstemplatesTable::configure($table);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => ListSmstemplates::route('/'),
'create' => CreateSmstemplates::route('/create'),
'edit' => EditSmstemplates::route('/{record}/edit'),
];
}
}

View file

@ -0,0 +1,46 @@
<?php
namespace App\Filament\Resources\Smstemplates\Tables;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Columns\ToggleColumn;
use Filament\Tables\Filters\TernaryFilter;
use Filament\Tables\Table;
class SmstemplatesTable
{
public static function configure(Table $table): Table
{
return $table
->columns([
TextColumn::make('title')
->searchable()
->sortable(),
ToggleColumn::make('draft')
->sortable(),
TextColumn::make('created_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('updated_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
TernaryFilter::make('draft')
->label('Draft'),
])
->recordActions([
EditAction::make(),
])
->toolbarActions([
BulkActionGroup::make([
DeleteBulkAction::make(),
]),
]);
}
}

View file

@ -33,10 +33,8 @@ class VolunteersTable
TextInputColumn::make('lan_id')
->label('ID')
->searchable()
->sortable(),
IconColumn::make('emailed')
->boolean()
->sortable(),
->sortable()
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('first_name')
->searchable()
->sortable(),
@ -76,36 +74,22 @@ class VolunteersTable
EditAction::make()
->modalWidth()
->slideOver(),
Action::make('sendEmail')
->label('Send email')
->icon(Heroicon::Envelope)
->schema([
Select::make('mailtemplate')
->label('Mailtemplate')
->options(Mailtemplate::all()->pluck('title', 'id'))
])
->action(function (array $data, Volunteer $record) {
$mailContent = Mailtemplate::where('id', $data['mailtemplate'])->get();
Mail::to($record->email)
->queue(new LanMail($mailContent, $record));
Volunteer::where('id', $record->id)->update(['emailed' => true]);
})
->hidden(fn($record) => $record->emailed),
Action::make('sendRemindEmail')
->label('Send remind email')
->icon(Heroicon::Envelope)
->schema([
Select::make('mailtemplate')
->label('Mailtemplate')
->options(Mailtemplate::all()->pluck('title', 'id'))
])
->action(function (array $data, Volunteer $record) {
$mailContent = Mailtemplate::where('id', $data['mailtemplate'])->get();
Mail::to($record->email)
->send(new LanMail($mailContent, $record));
Volunteer::where('id', $record->id)->update(['emailed' => true]);
})
->hidden(fn($record) => !$record->emailed)
//Action::make('sendEmail')
//->label('Send email')
//->icon(Heroicon::Envelope)
//->schema([
// Select::make('mailtemplate')
// ->label('Mailtemplate')
// ->options(Mailtemplate::all()->pluck('title', 'id'))
//])
//->action(function (array $data, Volunteer $record) {
// if ($record->email) {
// $mailContent = Mailtemplate::where('id', $data['mailtemplate'])->get();
// Mail::to($record->email)
// ->queue(new LanMail($mailContent, $record));
// Volunteer::where('id', $record->id)->update(['emailed' => true]);
// }
//}),
])
->toolbarActions([
BulkActionGroup::make([

View file

@ -0,0 +1,44 @@
<?php
namespace App\Http\Controllers;
use App\Models\Smstemplate;
use Illuminate\Http\Request;
class SmstemplateController extends Controller
{
public function index()
{
//
}
public function create()
{
//
}
public function store(Request $request)
{
//
}
public function show(Smstemplate $smstemplate)
{
//
}
public function edit(Smstemplate $smstemplate)
{
//
}
public function update(Request $request, Smstemplate $smstemplate)
{
//
}
public function destroy(Smstemplate $smstemplate)
{
//
}
}

View file

@ -15,13 +15,9 @@ class SmsMail extends Mailable
public $phone;
public $name;
public $smsContent;
/**
* Create a new message instance.
*/
public function __construct($participant)
public function __construct($participant, ?string $smsContent = null)
{
function formatToSwedenPrefix($phoneNumber) {
@ -50,6 +46,7 @@ class SmsMail extends Mailable
$this->name = $participant->first_name;
$this->phone = formatToSwedenPrefix($participant->guardian_phone);
$this->smsContent = $smsContent;
}
/**
@ -69,6 +66,7 @@ class SmsMail extends Mailable
{
return new Content(
view: 'mail.sms',
with: ['smsContent' => $this->smsContent],
);
}

32
app/Models/EmailLog.php Normal file
View file

@ -0,0 +1,32 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class EmailLog extends Model
{
protected $fillable = [
'participant_id',
'lan_id',
'guardian_email',
'mailtemplate_id',
'smstemplate_id',
'error',
];
public function participant()
{
return $this->belongsTo(Participant::class);
}
public function mailtemplate()
{
return $this->belongsTo(Mailtemplate::class);
}
public function smstemplate()
{
return $this->belongsTo(Smstemplate::class);
}
}

View file

@ -10,6 +10,12 @@ class Mailtemplate extends Model
'title',
'content',
'type',
'draft'
'draft',
'smstemplate_id',
];
public function smstemplate()
{
return $this->belongsTo(Smstemplate::class);
}
}

View file

@ -29,6 +29,11 @@ class Participant extends Model
'ssn'
];
public function emailLogs()
{
return $this->hasMany(EmailLog::class);
}
protected static function booted()
{
static::created(function ($post) {

View file

@ -0,0 +1,15 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Smstemplate extends Model
{
protected $fillable = [
'title',
'content',
'type',
'draft',
];
}

View file

@ -40,6 +40,18 @@ class AdminPanelProvider extends PanelProvider
->colors([
'primary' => Color::Amber,
])
->renderHook(
'panels::head.end',
fn () => '<style>.tippy-content { white-space: pre-line; } .fi-ta-cell.fi-ta-summary-header-cell.fi-align-start { color: transparent; } .fi-topbar-start { padding-top: 0.5rem !important; padding-bottom: 1rem !important; }</style>',
)
->navigationGroups([
\Filament\Navigation\NavigationGroup::make('Templates'),
\Filament\Navigation\NavigationGroup::make('Logs'),
])
->brandName('vBytes LAN')
->favicon(asset('images/vbytes-logo.png'))
->brandLogo(asset('images/vbytes-logo.png'))
->brandLogoHeight('5rem')
->discoverResources(in: app_path('Filament/Resources'), for: 'App\Filament\Resources')
->discoverPages(in: app_path('Filament/Pages'), for: 'App\Filament\Pages')
->pages([

View file

@ -0,0 +1,25 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('smstemplates', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('type');
$table->longText('content');
$table->boolean('draft')->default(false);
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('smstemplates');
}
};

View file

@ -0,0 +1,22 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('smstemplates', function (Blueprint $table) {
$table->string('type')->nullable()->change();
});
}
public function down(): void
{
Schema::table('smstemplates', function (Blueprint $table) {
$table->string('type')->nullable(false)->change();
});
}
};

View file

@ -0,0 +1,22 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('mailtemplates', function (Blueprint $table) {
$table->foreignId('smstemplate_id')->nullable()->constrained('smstemplates')->nullOnDelete()->after('draft');
});
}
public function down(): void
{
Schema::table('mailtemplates', function (Blueprint $table) {
$table->dropForeignIdFor(\App\Models\Smstemplate::class);
});
}
};

View file

@ -0,0 +1,26 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('email_logs', function (Blueprint $table) {
$table->id();
$table->foreignId('participant_id')->constrained()->cascadeOnDelete();
$table->integer('lan_id')->nullable();
$table->string('guardian_email')->nullable();
$table->foreignId('mailtemplate_id')->nullable()->constrained('mailtemplates')->nullOnDelete();
$table->foreignId('smstemplate_id')->nullable()->constrained('smstemplates')->nullOnDelete();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('email_logs');
}
};

View file

@ -0,0 +1,22 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('email_logs', function (Blueprint $table) {
$table->text('error')->nullable()->after('smstemplate_id');
});
}
public function down(): void
{
Schema::table('email_logs', function (Blueprint $table) {
$table->dropColumn('error');
});
}
};

View file

@ -1,7 +1,2 @@
Mejlutskick gällande LAN
{{ $smsContent }}
Vi vill med detta sms göra dig uppmärksam att vi har skickat ett mejl till den e-postadress som angavs när ditt barn anmäldes till LAN.
Vi ber dig att läsa mejlet noggrant. I det framgår bland annat om ditt barn har fått en plats, vilken plats det är och hur du gör för att anmäla dig till att hjälpa till med evenemanget.
Om du har några funderingar, kontakta oss vbyteslan@gmail.com.
Med vänliga hälsningar, LAN-gruppen Vbytes