Todo-Actions, Observer und Mails

This commit is contained in:
Alexander Gabriel 2026-06-03 23:34:48 +00:00
parent 9501d8f1c2
commit be675ec81e
10 changed files with 247 additions and 11 deletions

View File

@ -0,0 +1,28 @@
<?php
namespace App\Filament\Actions;
use Filament\Actions\Action;
use Filament\Support\Icons\Heroicon;
use Illuminate\Database\Eloquent\Model;
class DoneTodoAction extends Action
{
protected function setUp(): void
{
parent::setUp();
$this->iconButton()
->label('erledigt')
->tooltip('Todo als erledigt markieren')
->icon(Heroicon::Check)
->visible(function (Model $record) {
return $record->is_owner_or_assigned() && $record->done_date == null;
})
->action(function (Model $record) {
$record->done_date = now()->today();
$record->save();
});
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace App\Filament\Actions;
use App\Mail\TodoMail;
use Filament\Actions\Action;
use Filament\Forms\Components\TextInput;
use Filament\Support\Icons\Heroicon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Mail;
class ReopenTodoAction extends Action
{
protected function setUp(): void
{
parent::setUp();
$this->iconButton()
->label('Wiedereröffnen')
->tooltip('Todo wieder eröffnen')
->icon(Heroicon::ArrowUturnDown)
->visible(function (Model $record) {
return $record->is_owner_or_assigned() && $record->done_date != null;
})
->schema([
TextInput::make('notiz')->label('Notiz'),
])
->action(function (Model $record, array $data) {
$record->done_date = null;
$record->save();
$users = $record->getassignetusers();
foreach ($users as $user) {
Mail::to($user)->send(new TodoMail($record, 'Todo wiedereröffnet', 'Eine bereits erledigte Todo wurde wiedereröffnet:', notiz: $data['notiz']));
}
});
}
}

View File

@ -2,9 +2,12 @@
namespace App\Filament\Resources\Todos\Pages; namespace App\Filament\Resources\Todos\Pages;
use App\Filament\Actions\DoneTodoAction;
use App\Filament\Actions\ReopenTodoAction;
use App\Filament\Resources\Todos\TodoResource; use App\Filament\Resources\Todos\TodoResource;
use Filament\Actions\DeleteAction; use Filament\Actions\DeleteAction;
use Filament\Resources\Pages\EditRecord; use Filament\Resources\Pages\EditRecord;
use Filament\Support\Icons\Heroicon;
class EditTodo extends EditRecord class EditTodo extends EditRecord
{ {
@ -13,7 +16,9 @@ class EditTodo extends EditRecord
protected function getHeaderActions(): array protected function getHeaderActions(): array
{ {
return [ return [
DeleteAction::make(), DoneTodoAction::make('done'),
ReopenTodoAction::make('reopen'),
DeleteAction::make()->iconButton()->icon(Heroicon::Trash),
]; ];
} }
} }

View File

@ -2,10 +2,13 @@
namespace App\Filament\Resources\Todos\Tables; namespace App\Filament\Resources\Todos\Tables;
use App\Filament\Actions\DoneTodoAction;
use App\Filament\Actions\ReopenTodoAction;
use App\Models\Group; use App\Models\Group;
use App\Models\User; use App\Models\User;
use Filament\Actions\Action; use Filament\Actions\Action;
use Filament\Actions\BulkActionGroup; use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteAction;
use Filament\Actions\DeleteBulkAction; use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction; use Filament\Actions\EditAction;
use Filament\Support\Icons\Heroicon; use Filament\Support\Icons\Heroicon;
@ -80,14 +83,13 @@ class TodosTable
]) ])
->recordActions([ ->recordActions([
Action::make('done') DoneTodoAction::make('done'),
->iconButton() ReopenTodoAction::make('reopen'),
->icon(Heroicon::Check)
->action(function (Model $record) {
$record->done_date = now()->today();
$record->save();
}),
EditAction::make()->iconButton(), EditAction::make()->iconButton(),
DeleteAction::make()->iconButton()
->visible(function (Model $record) {
return $record->is_owner_or_assigned();
}),
]) ])
->toolbarActions([ ->toolbarActions([
BulkActionGroup::make([ BulkActionGroup::make([

View File

@ -18,7 +18,7 @@ class TodoResource extends Resource
{ {
protected static ?string $model = Todo::class; protected static ?string $model = Todo::class;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedRectangleStack; protected static string|BackedEnum|null $navigationIcon = Heroicon::CheckBadge;
protected static ?string $recordTitleAttribute = 'name'; protected static ?string $recordTitleAttribute = 'name';

28
app/FollowUpEnum.php Normal file
View File

@ -0,0 +1,28 @@
<?php
namespace App;
enum FollowUp: string
{
case TOMORROW = 'tomorrow';
case MONDAY = 'monday';
case TUESDAY = 'tuesday';
case WEDNESDAY = 'wednesday';
case THURSTDAY = 'hursday';
case FRIDAY = 'friday';
case TWO_WEEKS = 'two_weeks';
public function label(): string
{
return match($this) {
self::TOMORROW => 'Morgen',
self::MONDAY => 'Montag',
self::TUESDAY => 'Dienstag',
self::WEDNESDAY => 'Mittwoch',
self::THURSTDAY => 'Donnerstag',
self::FRIDAY => 'Freitag',
self::TWO_WEEKS => 'In 2 Wochen',
};
}
}

68
app/Mail/TodoMail.php Normal file
View File

@ -0,0 +1,68 @@
<?php
namespace App\Mail;
use App\Filament\Resources\Todos\TodoResource;
use App\Models\Todo;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class TodoMail extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*/
public function __construct(
public Todo $todo,
public string $mail_subject,
public string $message,
public string $notiz = "",
)
{
//
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: $this->mail_subject,
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
markdown: 'mail.todo-mail',
with: [
'message' => $this->message,
'subject' => $this->mail_subject,
'notiz' => $this->notiz,
'todo_url' => TodoResource::getUrl('edit', ['record' => $this->todo]),
'todo' => $this->todo,
],
);
}
/**
* Get the attachments for the message.
*
* @return array<int, Attachment>
*/
public function attachments(): array
{
return [];
}
}

View File

@ -4,6 +4,7 @@ namespace App\Models;
use App\Observers\TodoObserver; use App\Observers\TodoObserver;
use Illuminate\Database\Eloquent\Attributes\Fillable; use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Eloquent\Relations\MorphTo;
@ -21,4 +22,16 @@ class Todo extends Model
{ {
return $this->belongsTo(User::class); return $this->belongsTo(User::class);
} }
public function is_owner_or_assigned() {
if(filament()->auth()->user()->id == $this->user_id) return true;
if($this->todoable_type == Group::class && in_array(filament()->auth()->user()->id, $this->todoable()->first()->get()->users()->get()->pluck('id')->toArray())) return true;
if($this->todoable_type == User::class && $this->todoable()->first()->get()->id == filament()->auth()->user()->id) return true;
else return false;
}
public function getassignetusers() {
if($this->todoable_type == Group::class) return $this->todoable()->first()->users()->get();
if($this->todoable_type == User::class) return [$this->todoable()->first()->get()];
}
} }

View File

@ -2,7 +2,16 @@
namespace App\Observers; namespace App\Observers;
use App\Mail\TodoAssigned;
use App\Mail\TodoCreated;
use App\Mail\TodoMail;
use App\Mail\TodoNeedsReview;
use App\Mail\TodoReopened;
use App\Models\Group;
use App\Models\Todo; use App\Models\Todo;
use App\Models\User;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
class TodoObserver class TodoObserver
{ {
@ -11,7 +20,17 @@ class TodoObserver
*/ */
public function created(Todo $todo): void public function created(Todo $todo): void
{ {
// if($todo->todoable_type == null) return;
// if($todo->todoable_type == User::class && $todo->user_id == $todo->todoable_id) return;
if($todo->todoable_type == User::class) {
Mail::to(User::find($todo->todoable()->first()))->send(new TodoMail($todo, 'Neue Todo', 'Es gibt eine neue Todo für dich:'));
}
else {
$users = $todo->todoable()->first()->users()->get();
foreach ($users as $user) {
Mail::to($user)->send(new TodoMail($todo, 'Neue Todo', 'Es gibt eine neue Todo für dich:'));
}
}
} }
/** /**
@ -19,7 +38,18 @@ class TodoObserver
*/ */
public function updated(Todo $todo): void public function updated(Todo $todo): void
{ {
// $hasChanged = $todo->getChanges();
if ($hasChanged && isset($hasChanged['done_date']) && $todo->done_date != null) {
$user = User::find($todo->user_id);
Mail::to($user)->send(new TodoMail($todo, 'Todo erledigt', 'Todo erledigt, du wolltest, dass du Bescheid bekommst:'));
}
if ($hasChanged && (isset($hasChanged['todoable_type']) || isset($hasChanged['todoable_id']))) {
$users = $todo->getassignetusers();
foreach ($users as $user) {
Mail::to($user)->send(new TodoMail($todo, 'Todo zugewiesen', 'Dir wurde eine bestehende Todo zugewiesen:'));
}
}
} }
/** /**

View File

@ -0,0 +1,24 @@
<x-mail::message>
# {{ $subject }}
Hi,
{{ $message }}
{{ $notiz }}
## <a href="{{ $todo_url }}">{{ $todo->name }}:</a>
Erstellt von: {{ $todo->user->name }}
Zugewiesen: {{ $todo->todoable->name }}
Inhalt:
{!! $todo->content !!}
<x-mail::button :url="$todo_url" color="primary">
Todo im Cockpit ansehen
</x-mail::button>
Danke,<br>
{{ config('app.name') }}
</x-mail::message>