occtherapist / advanced-roster-for-filament
A flexible weekly roster page for Filament — drag-and-drop entries, recurrence, day notes, and optional PDF export.
Package info
github.com/OccTherapist/advanced-roster-for-filament
pkg:composer/occtherapist/advanced-roster-for-filament
Requires
- php: ^8.2
- filament/filament: ^4.0|^5.0
- illuminate/contracts: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
Requires (Dev)
- laravel/pint: ^1.22
- mockery/mockery: ^1.6
- orchestra/testbench: ^10.0|^11.0
- phpunit/phpunit: ^11.0|^12.0
Suggests
- spatie/laravel-pdf: Required for PDF roster export.
README
A flexible weekly roster page for Filament — drag-and-drop entries, filters & views, row reordering, recurring shifts, day notes, and optional PDF export.
Built for Filament v4 and v5 on Laravel 11, 12, and 13. Assign any Eloquent model as roster rows (default: User), scope data to your tenant or team, and extend validation rules when you need them.
Why this package?
| Need | This package |
|---|---|
| Weekly roster in a Filament panel | Ready-made RosterPage with calendar navigation |
| Any model as rows | Configurable assignee model (default: User) |
| Drag & drop entries and row order | Move, copy, and reorder — persisted per user and scope |
| Filters & views | Filter visible rows by assignee; register custom filters for role, location, and more |
| Recurring entries | Series with edit/delete for single or future occurrences |
| Day notes | Optional colored notes per day (feature-flagged) |
| No hard-coded HR rules | Overlap check built-in; custom rules via validator registry |
| Multi-tenant ready | RosterScopeResolver — Filament tenancy as default |
| PDF or browser print | Spatie PDF when available, print-optimized Blade as fallback |
| German & English | Translations included |
Features
- Filament page plugin — register once on your panel
- Configurable assignee model — defaults to
User, override via config or model methods - Entry types — work, sick, vacation, unavailable (or your own enum)
- Drag & drop — move and copy entries between days and rows
- Filters & views — built-in assignee filter; extensible
RosterFiltercontract - Row reordering — custom sort order per user and scope (not stored on the user model)
- Recurrence — daily, weekly, monthly, and custom weekday patterns
- Day notes — optional per-day notes with recurrence (
roster.features.notes) - Overlap validation — enabled by default, disable via config
- Validator registry — register custom
RosterEntryValidatorclasses - Section registry — one section in v1, add more via
RosterSectioncontract - Scope resolver — abstract tenancy/team/location scoping
- Print / PDF — optional PDF export with print-friendly fallback view
- Configurable week — week start and visible day count (default: Mon–Fri)
- i18n — English and German
Requirements
- PHP 8.2+
- Filament 4 or 5
- Laravel 11, 12, or 13
Optional (PDF export):
| Package | Purpose |
|---|---|
spatie/laravel-pdf |
PDF generation for printAction() |
Without a PDF library, the package falls back to a print-optimized Blade view (browser print).
Installation
composer require occtherapist/advanced-roster-for-filament
Register the plugin in your panel provider:
use Filament\Panel; use OccTherapist\AdvancedRosterForFilament\AdvancedRosterForFilamentPlugin; public function panel(Panel $panel): Panel { return $panel ->plugins([ AdvancedRosterForFilamentPlugin::make(), ]); }
Publish and run migrations:
php artisan vendor:publish --tag=advanced-roster-for-filament-migrations php artisan migrate
Publish config (optional):
php artisan vendor:publish --tag=advanced-roster-for-filament-config
Configuration
// config/advanced-roster-for-filament.php return [ 'assignee_model' => \App\Models\User::class, 'entry_type_enum' => \OccTherapist\AdvancedRosterForFilament\Enums\RosterEntryType::class, 'validate_overlap' => true, 'week_starts_at' => 'monday', 'week_days' => 5, 'filters' => [ \OccTherapist\AdvancedRosterForFilament\Support\Filters\AssigneeRosterFilter::class, // \App\Roster\Filters\RoleRosterFilter::class, // \App\Roster\Filters\LocationRosterFilter::class, ], 'features' => [ 'notes' => true, 'print' => true, 'filters' => true, ], ];
Assignee model overrides
The assignee model can override config defaults with optional methods:
| Method | Purpose |
|---|---|
getRosterNameColumn() |
Column used for the row label |
getRosterLabel() |
Custom label when a column is not enough |
scopeForRoster($query, $scope) |
Filter which records appear as rows |
getRosterUrl() |
Optional link from the row header |
isVisibleOnRoster($date) |
Hide rows on specific dates |
Custom entry type enum
Provide your own backed enum in config. It must implement Filament's HasLabel and HasColor contracts, plus RosterEntryTypeContract:
interface RosterEntryTypeContract { public function isFullDay(): bool; public function deletesConflictingEntries(): bool; }
Filters & views
The roster toolbar includes a filter button (funnel icon). Active filters are combined with AND logic. An empty selection means “no restriction” — all assignees stay visible.
The built-in AssigneeRosterFilter lets users pick which people appear in the grid. Filter values are stored per user and scope in roster_user_preferences.
Register additional filters by implementing RosterFilter:
use Filament\Forms\Components\Select; use Illuminate\Support\Collection; use OccTherapist\AdvancedRosterForFilament\Contracts\RosterFilter; use OccTherapist\AdvancedRosterForFilament\Contracts\RosterScope; class RoleRosterFilter implements RosterFilter { public function getKey(): string { return 'role'; } public function getLabel(): string { return __('Role'); } public function getFormComponent(): Select { return Select::make($this->getKey()) ->label($this->getLabel()) ->options([ 'nurse' => 'Nurse', 'doctor' => 'Doctor', 'admin' => 'Admin', ]) ->multiple(); } public function apply(Collection $assignees, mixed $value, ?RosterScope $scope): Collection { if (! is_array($value) || $value === []) { return $assignees; } return $assignees->filter( fn ($assignee) => in_array($assignee->role, $value, true), ); } }
Add your filter class to the filters array in config. Disable the entire feature with 'features.filters' => false.
Extension points
| Contract | Purpose |
|---|---|
RosterScopeResolver |
Resolve scope_id / scope_type (Filament tenant by default) |
RosterSection |
Register additional roster sections (v1 ships one default section) |
RosterEntryValidator |
Add create/update/move/copy validation rules |
RosterFilter |
Add roster view filters (role, location, team, etc.) |
Authorization
The package does not ship permissions or policies. Restrict access in your host app via Filament canAccess(), policies, or middleware.
Roadmap
| Version | Focus |
|---|---|
| v0.1.0 | Core roster page, entries, drag & drop, row order, recurrence |
| v0.2.0 | Day notes, PDF / print fallback |
| v0.3.0 | Filters & views with extensible filter registry |
| v1.0 | Stable API, test coverage, Filament plugin listing |
Contributing
Contributions are welcome! Open an issue for bugs or feature requests.
License
MIT © Igor Clauss
Author
Igor Clauss — Laravel & Filament developer
- Website: igorclauss.de
- GitHub: @OccTherapist