mirror of
https://github.com/anna-sara/vbytes_lan.git
synced 2026-09-03 09:35:21 +02:00
New features with Mail and SMS
This commit is contained in:
parent
ee718a3a7d
commit
0b1a3ae96f
28 changed files with 656 additions and 85 deletions
|
|
@ -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
|
||||
|
|
@ -14,4 +15,4 @@ RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local
|
|||
|
||||
ENV APACHE_DOCUMENT_ROOT /var/www/html/public
|
||||
RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf
|
||||
RUN sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf
|
||||
RUN sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf
|
||||
|
|
|
|||
77
app/Filament/Resources/EmailLogs/EmailLogResource.php
Normal file
77
app/Filament/Resources/EmailLogs/EmailLogResource.php
Normal 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;
|
||||
}
|
||||
}
|
||||
16
app/Filament/Resources/EmailLogs/Pages/ListEmailLogs.php
Normal file
16
app/Filament/Resources/EmailLogs/Pages/ListEmailLogs.php
Normal 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 [];
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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">⚠ ' . 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> — {$mail}{$sms}{$error}</li>";
|
||||
})->join('');
|
||||
return new HtmlString("<ul class=\"divide-y divide-gray-100\">{$rows}</ul>");
|
||||
}),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,10 +35,12 @@ class ParticipantsTable
|
|||
{
|
||||
return $table
|
||||
->paginated([10, 25, 50, 100, 'all'])
|
||||
->defaultPaginationPageOption(100)
|
||||
->groups([
|
||||
Group::make('status')
|
||||
->label('Status')
|
||||
->collapsible(),
|
||||
Group::make('status')
|
||||
->label('Status')
|
||||
->getTitleFromRecordUsing(fn ($record) => ucfirst($record->status))
|
||||
->collapsible(),
|
||||
])
|
||||
->defaultGroup('status')
|
||||
//->groupsOnly()
|
||||
|
|
@ -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();
|
||||
Mail::to($record->guardian_email)
|
||||
->send(new LanMail($mailContent, $record));
|
||||
Participant::where('id', $record->id)->update(['emailed' => true]);
|
||||
Mail::to(config('app.smsUrl'))
|
||||
->send(new SmsMail($record));
|
||||
|
||||
})
|
||||
->hidden(fn($record) => $record->emailed),
|
||||
Action::make('sendRemindEmail')
|
||||
->label('Send remind email')
|
||||
->icon(Heroicon::Envelope)
|
||||
->schema([
|
||||
Select::make('mailtemplate')
|
||||
->label('Mailtemplate')
|
||||
->options(Mailtemplate::where('draft', false)->pluck('title', 'id'))
|
||||
])
|
||||
->action(function (array $data, Participant $record) {
|
||||
$mailContent = Mailtemplate::where('id', $data['mailtemplate'])->get();
|
||||
Mail::to($record->guardian_email)
|
||||
->queue(new LanMail($mailContent, $record));
|
||||
Participant::where('id', $record->id)->update(['emailed' => true]);
|
||||
})
|
||||
->hidden(fn($record) => !$record->emailed),
|
||||
$mailtemplate = Mailtemplate::with('smstemplate')->find($data['mailtemplate']);
|
||||
if ($record->guardian_email) {
|
||||
$error = null;
|
||||
try {
|
||||
Mail::to($record->guardian_email)
|
||||
->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, $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 (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(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]);
|
||||
})
|
||||
->deselectRecordsAfterCompletion(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -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(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -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(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
54
app/Filament/Resources/Smstemplates/SmstemplatesResource.php
Normal file
54
app/Filament/Resources/Smstemplates/SmstemplatesResource.php
Normal 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'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -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(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -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([
|
||||
|
|
|
|||
44
app/Http/Controllers/SmstemplateController.php
Normal file
44
app/Http/Controllers/SmstemplateController.php
Normal 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)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -68,7 +65,8 @@ class SmsMail extends Mailable
|
|||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
view: 'mail.sms',
|
||||
view: 'mail.sms',
|
||||
with: ['smsContent' => $this->smsContent],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
32
app/Models/EmailLog.php
Normal file
32
app/Models/EmailLog.php
Normal 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,12 @@ class Mailtemplate extends Model
|
|||
'title',
|
||||
'content',
|
||||
'type',
|
||||
'draft'
|
||||
'draft',
|
||||
'smstemplate_id',
|
||||
];
|
||||
|
||||
public function smstemplate()
|
||||
{
|
||||
return $this->belongsTo(Smstemplate::class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
15
app/Models/Smstemplate.php
Normal file
15
app/Models/Smstemplate.php
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Smstemplate extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'content',
|
||||
'type',
|
||||
'draft',
|
||||
];
|
||||
}
|
||||
|
|
@ -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([
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
};
|
||||
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -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');
|
||||
}
|
||||
};
|
||||
|
|
@ -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');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -1,7 +1,2 @@
|
|||
Mejlutskick gällande LAN
|
||||
{{ $smsContent }}
|
||||
|
||||
Vi vill med detta sms göra dig uppmärksam på 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 på vbyteslan@gmail.com.
|
||||
|
||||
Med vänliga hälsningar, LAN-gruppen Vbytes
|
||||
Loading…
Reference in a new issue