@@ -49,7 +49,7 @@ use Relaticle\Flowforge\Board;
4949use Relaticle\Flowforge\BoardPage;
5050use Relaticle\Flowforge\Column;
5151
52- class TaskBoard extends BoardPage
52+ class TaskBoardPage extends BoardPage
5353{
5454 protected static ?string $navigationIcon = 'heroicon-o-view-columns';
5555
@@ -62,8 +62,9 @@ class TaskBoard extends BoardPage
6262 {
6363 return $board
6464 ->query($this->getEloquentQuery())
65- ->cardTitle ('title')
65+ ->recordTitleAttribute ('title')
6666 ->columnIdentifier('status')
67+ ->reorderBy('order_column')
6768 ->columns([
6869 Column::make('todo')->label('To Do')->color('gray'),
6970 Column::make('in_progress')->label('In Progress')->color('blue'),
@@ -135,7 +136,7 @@ php artisan flowforge:make-board TaskBoard --model=Task
135136``` php
136137// app/Providers/Filament/AdminPanelProvider.php
137138->pages([
138- App\Filament\Pages\TaskBoard ::class,
139+ App\Filament\Pages\TaskBoardPage ::class,
139140])
140141```
141142
@@ -153,7 +154,7 @@ public function board(Board $board): Board
153154{
154155 return $board
155156 ->query($this->getEloquentQuery())
156- ->cardTitle ('title')
157+ ->recordTitleAttribute ('title')
157158 ->columnIdentifier('status')
158159 ->columns([
159160 Column::make('backlog')->label('Backlog'),
@@ -169,41 +170,79 @@ Add create and edit capabilities:
169170``` php
170171use Filament\Actions\CreateAction;
171172use Filament\Actions\EditAction;
173+ use Filament\Actions\DeleteAction;
172174use Filament\Forms\Components\TextInput;
175+ use Filament\Forms\Components\Select;
173176
174177public function board(Board $board): Board
175178{
176179 return $board
177180 ->query($this->getEloquentQuery())
178- ->cardTitle ('title')
181+ ->recordTitleAttribute ('title')
179182 ->columnIdentifier('status')
180- ->columns([...])
183+ ->reorderBy('order_column')
184+ ->columns([
185+ Column::make('todo')->label('To Do')->color('gray'),
186+ Column::make('in_progress')->label('In Progress')->color('blue'),
187+ Column::make('completed')->label('Completed')->color('green'),
188+ ])
181189 ->columnActions([
182- CreateAction::make()
190+ CreateAction::make('create' )
183191 ->label('Add Task')
184- ->iconButton( )
192+ ->icon('heroicon-o-plus' )
185193 ->model(Task::class)
186- ->schema ([
194+ ->form ([
187195 TextInput::make('title')->required(),
188- ]),
196+ Select::make('priority')
197+ ->options(['low' => 'Low', 'medium' => 'Medium', 'high' => 'High'])
198+ ->default('medium'),
199+ ])
200+ ->mutateFormDataUsing(function (array $data, string $columnId): array {
201+ $data['status'] = $columnId;
202+ return $data;
203+ }),
189204 ])
190205 ->cardActions([
191- EditAction::make()->model(Task::class),
192- ]);
206+ EditAction::make('edit')->model(Task::class),
207+ DeleteAction::make('delete')->model(Task::class),
208+ ])
209+ ->cardAction('edit'); // Make cards clickable
193210}
194211```
195212
196- ### Advanced Board with Properties
197- Display additional model attributes :
213+ ### Advanced Board with Schema
214+ Use Filament's Schema system for rich card content :
198215
199216``` php
200- use Relaticle\Flowforge\Property;
217+ use Filament\Infolists\Components\TextEntry;
218+ use Filament\Schemas\Schema;
201219
202- ->cardProperties([
203- Property::make('description')->label('Description'),
204- Property::make('due_date')->label('Due')->color('red'),
205- Property::make('assignee.name')->label('Assigned')->icon('heroicon-o-user'),
206- ])
220+ public function board(Board $board): Board
221+ {
222+ return $board
223+ ->query($this->getEloquentQuery())
224+ ->recordTitleAttribute('title')
225+ ->columnIdentifier('status')
226+ ->cardSchema(fn (Schema $schema) => $schema
227+ ->components([
228+ TextEntry::make('priority')
229+ ->badge()
230+ ->color(fn ($state) => match ($state) {
231+ 'high' => 'danger',
232+ 'medium' => 'warning',
233+ 'low' => 'success',
234+ default => 'gray',
235+ }),
236+ TextEntry::make('due_date')
237+ ->date()
238+ ->icon('heroicon-o-calendar'),
239+ TextEntry::make('assignee.name')
240+ ->icon('heroicon-o-user')
241+ ->placeholder('Unassigned'),
242+ ])
243+ )
244+ ->columns([...]);
245+ }
207246```
208247
209248---
@@ -214,14 +253,26 @@ use Relaticle\Flowforge\Property;
214253
215254| Method | Description | Required |
216255| --------| -------------| ----------|
217- | ` cardTitle(string) ` | Field used for card titles | ✅ |
256+ | ` query(Builder\|Closure) ` | Set the data source | ✅ |
257+ | ` recordTitleAttribute(string) ` | Field used for card titles | ✅ |
218258| ` columnIdentifier(string) ` | Field that determines column placement | ✅ |
219259| ` columns(array) ` | Define board columns | ✅ |
220- | ` query(Builder) ` | Set the data source | ✅ |
221- | ` defaultSort(string, ?string) ` | Field and optional direction for drag & drop ordering | |
222- | ` cardProperties(array) ` | Additional fields to display | |
223- | ` columnActions(array) ` | Actions for column headers | |
260+ | ` reorderBy(string, string) ` | Enable drag & drop with field and direction | |
261+ | ` cardSchema(Closure) ` | Configure card content with Filament Schema | |
224262| ` cardActions(array) ` | Actions for individual cards | |
263+ | ` columnActions(array) ` | Actions for column headers | |
264+ | ` cardAction(string) ` | Default action when cards are clicked | |
265+ | ` searchable(array) ` | Enable search across specified fields | |
266+
267+ ### Livewire Methods (Available in your BoardPage)
268+
269+ | Method | Description | Usage |
270+ | --------| -------------| -------|
271+ | ` updateRecordsOrderAndColumn(string, array) ` | Handle drag & drop updates | Automatic |
272+ | ` loadMoreItems(string, ?int) ` | Load more cards for pagination | Automatic |
273+ | ` getBoardRecord(int\|string) ` | Get single record by ID | Manual |
274+ | ` getBoardColumnRecords(string) ` | Get all records for a column | Manual |
275+ | ` getBoardColumnRecordCount(string) ` | Count records in a column | Manual |
225276
226277### Available Colors
227278
@@ -231,39 +282,201 @@ use Relaticle\Flowforge\Property;
231282
232283## Troubleshooting
233284
234- <details >
235- <summary ><strong >🔧 Cards not draggable</strong ></summary >
285+ ### 🔧 Cards not draggable
286+ ** Cause:** Missing order column or reorderBy configuration
287+ ** Solution:**
288+ 1 . Add integer column to your migration: ` $table->integer('order_column')->nullable(); `
289+ 2 . Add ` ->reorderBy('order_column') ` to your board configuration
290+ 3 . Ensure your model's ` $fillable ` includes the order column
291+
292+ ### 📭 Empty board showing
293+ ** Cause:** Query returns no results or status field mismatch
294+ ** Debug steps:**
295+ 1 . Check query: ` dd($this->getEloquentQuery()->get()); `
296+ 2 . Verify status values match column names exactly
297+ 3 . Check database field type (string vs enum)
298+
299+ ### ❌ Actions not working
300+ ** Cause:** Missing Filament traits or action configuration
301+ ** Solution:**
302+ 1 . Ensure your BoardPage implements ` HasActions ` , ` HasForms `
303+ 2 . Use these traits in your class:
304+ ``` php
305+ use InteractsWithActions;
306+ use InteractsWithForms;
307+ use InteractsWithBoard;
308+ ```
309+ 3 . Configure actions properly with ` ->model(YourModel::class) `
236310
311+ ### 🔄 Drag & drop updates not saving
312+ ** Cause:** Missing primary key handling or invalid field names
237313** Solution:**
238- 1 . Add an integer ` order_column ` to your model
239- 2 . Configure ` ->defaultSort('order_column') ` in your board
314+ 1 . Ensure your model uses standard primary key or override ` getKeyName() `
315+ 2 . Check status field accepts the column identifier values
316+ 3 . Verify order column exists and is fillable
240317
318+ ### 💥 "No default Filament panel" error
319+ ** Cause:** Missing panel configuration in tests/development
320+ ** Solution:** Add to your panel provider:
241321``` php
242- ->defaultSort('order_column ')
322+ Panel::make()->default()->id('admin ')
243323```
244- </details >
245-
246- <details >
247- <summary ><strong >📭 Empty board</strong ></summary >
248324
325+ ### 🎨 Styling not loading
326+ ** Cause:** Assets not built or registered
249327** Solution:**
250- 1 . Ensure your model has records
251- 2 . Check status field values match column keys
252- 3 . Debug with: ` dd($this->getEloquentQuery()->get()) `
253- </details >
328+ 1 . Run ` npm run build ` to compile assets
329+ 2 . Ensure Filament can load the assets with proper permissions
254330
255- <details >
256- <summary ><strong >❌ Create/Edit not working</strong ></summary >
331+ ## Real-World Examples
257332
258- ** Solution:**
259- 1 . Implement ` columnActions() ` for create functionality
260- 2 . Implement ` cardActions() ` for edit functionality
261- 3 . Ensure proper action configuration
333+ ### Complete Task Management Board
334+
335+ ``` php
336+ <?php
337+
338+ namespace App\Filament\Pages;
339+
340+ use App\Models\Task;
341+ use Filament\Actions\CreateAction;
342+ use Filament\Actions\EditAction;
343+ use Filament\Actions\DeleteAction;
344+ use Filament\Forms\Components\Select;
345+ use Filament\Forms\Components\TextInput;
346+ use Filament\Forms\Components\DatePicker;
347+ use Filament\Infolists\Components\TextEntry;
348+ use Filament\Schemas\Schema;
349+ use Illuminate\Database\Eloquent\Builder;
350+ use Relaticle\Flowforge\Board;
351+ use Relaticle\Flowforge\BoardPage;
352+ use Relaticle\Flowforge\Column;
353+
354+ class TaskBoardPage extends BoardPage
355+ {
356+ protected static ?string $navigationIcon = 'heroicon-o-view-columns';
357+ protected static ?string $navigationLabel = 'Task Board';
358+
359+ public function getEloquentQuery(): Builder
360+ {
361+ return Task::query()->with('assignee');
362+ }
363+
364+ public function board(Board $board): Board
365+ {
366+ return $board
367+ ->query($this->getEloquentQuery())
368+ ->recordTitleAttribute('title')
369+ ->columnIdentifier('status')
370+ ->reorderBy('order_column', 'desc')
371+ ->searchable(['title', 'description', 'assignee.name'])
372+ ->columns([
373+ Column::make('todo')->label('📋 To Do')->color('gray'),
374+ Column::make('in_progress')->label('🔄 In Progress')->color('blue'),
375+ Column::make('review')->label('👁️ Review')->color('purple'),
376+ Column::make('completed')->label('✅ Completed')->color('green'),
377+ ])
378+ ->cardSchema(fn (Schema $schema) => $schema
379+ ->components([
380+ TextEntry::make('priority')
381+ ->badge()
382+ ->color(fn ($state) => match ($state) {
383+ 'high' => 'danger',
384+ 'medium' => 'warning',
385+ 'low' => 'success',
386+ default => 'gray',
387+ }),
388+ TextEntry::make('due_date')
389+ ->date()
390+ ->icon('heroicon-o-calendar')
391+ ->color('orange'),
392+ TextEntry::make('assignee.name')
393+ ->icon('heroicon-o-user')
394+ ->placeholder('Unassigned'),
395+ ])
396+ )
397+ ->columnActions([
398+ CreateAction::make('create')
399+ ->label('Add Task')
400+ ->icon('heroicon-o-plus')
401+ ->model(Task::class)
402+ ->form([
403+ TextInput::make('title')->required(),
404+ Select::make('priority')
405+ ->options(['low' => 'Low', 'medium' => 'Medium', 'high' => 'High'])
406+ ->default('medium'),
407+ DatePicker::make('due_date'),
408+ ])
409+ ->mutateFormDataUsing(function (array $data, string $columnId): array {
410+ $data['status'] = $columnId;
411+ return $data;
412+ }),
413+ ])
414+ ->cardActions([
415+ EditAction::make('edit')
416+ ->model(Task::class)
417+ ->form([
418+ TextInput::make('title')->required(),
419+ Select::make('status')
420+ ->options([
421+ 'todo' => 'To Do',
422+ 'in_progress' => 'In Progress',
423+ 'review' => 'Review',
424+ 'completed' => 'Completed',
425+ ]),
426+ Select::make('priority')
427+ ->options(['low' => 'Low', 'medium' => 'Medium', 'high' => 'High']),
428+ DatePicker::make('due_date'),
429+ ]),
430+ DeleteAction::make('delete')
431+ ->model(Task::class)
432+ ->requiresConfirmation(),
433+ ])
434+ ->cardAction('edit'); // Make cards clickable to edit
435+ }
436+ }
437+ ```
438+
439+ ### Required Database Schema
440+
441+ ``` sql
442+ CREATE TABLE tasks (
443+ id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY ,
444+ title VARCHAR (255 ) NOT NULL ,
445+ status VARCHAR (255 ) DEFAULT ' todo' ,
446+ order_column INT NULL , -- Required for drag & drop
447+ priority VARCHAR (255 ) DEFAULT ' medium' ,
448+ due_date DATE NULL ,
449+ assignee_id BIGINT UNSIGNED NULL ,
450+ created_at TIMESTAMP NULL ,
451+ updated_at TIMESTAMP NULL
452+ );
453+ ```
454+
455+ ### Testing Your Board
262456
263457``` php
264- ->columnActions([CreateAction::make()->model(Task::class)])
458+ // tests/Feature/TaskBoardTest.php
459+ use Livewire\Livewire;
460+
461+ test('task board renders successfully', function () {
462+ Task::create(['title' => 'Test Task', 'status' => 'todo']);
463+
464+ Livewire::test(TaskBoardPage::class)
465+ ->assertSuccessful()
466+ ->assertSee('Test Task')
467+ ->assertSee('To Do');
468+ });
469+
470+ test('can move tasks between columns', function () {
471+ $task = Task::create(['title' => 'Test Task', 'status' => 'todo']);
472+
473+ Livewire::test(TaskBoardPage::class)
474+ ->call('updateRecordsOrderAndColumn', 'completed', [$task->getKey()])
475+ ->assertSuccessful();
476+
477+ expect($task->fresh()->status)->toBe('completed');
478+ });
265479```
266- </details >
267480
268481---
269482
0 commit comments