Tricks

The Power of Filament Actions & Widgets

Oct 29, 2022
Andrew Wallo
Admin panel, Form builder, Integration, Table builder
  • Have you ever wished you could have multiple Filament Resources with each one having a corresponding Model all within one Page?
  • What about having Multiple Tables and Forms with their own corresponding Model all within one Page?
  • What about having a separate Dashboard Page along with your existing one?
  • What about multiple Dashboard Pages?

I'm going to show you how to do this all while using Filament Widgets and Actions... using the Admin Panel 10 11 12 13

I will be using the Table Widgets here as an example but any other widgets should work in a similar fashion.

  1. Create a Filament Page:
php artisan make:filament-page ChartOfAccounts
  1. Edit your Page in a similar fashion:
<?php
 
namespace App\Filament\Pages;
 
use Filament\Pages\Page;
 
class ChartOfAccounts extends Page
{
protected static ?string $navigationIcon = 'heroicon-o-home';
 
protected static ?int $navigationSort = -1;
 
protected static string $view = 'filament.pages.chart-of-accounts';
}
  1. Create a New Folder called "Widgets" at App\Filament\Pages\{Widgets Folder}:

  2. Inside the "Widgets" Folder create a new File as such... (Mine is called Assets.php):

  • Note: I disabled Pagination for my tables for my use case but you can edit yours however you like... Obviously you need your Tables to match your Eloquent Models and Database Tables in your Migrations Folder... You can use mostly any Filament Actions including BulkActions such as DeleteBulkActions but I have used the ViewAction, EditAction, and the CreateAction. With the CreateAction you will want to use the getTableHeaderActions function to be displayed above each of your tables...
<?php
 
namespace App\Filament\Pages\Widgets;
 
use App\Models\Asset;
use Filament\Tables;
use Filament\Forms;
use App\Models\Company;
use App\Models\Department;
use Filament\Widgets\TableWidget as PageWidget;
use Illuminate\Database\Eloquent\Builder;
 
class Assets extends PageWidget
{
 
protected int | string | array $columnSpan = [
'md' => 2,
'xl' => 3,
];
 
protected function getTableQuery(): Builder
{
return Asset::query();
}
 
protected function getTableColumns(): array
{
return [
Tables\Columns\TextColumn::make('company.name', 'name')->hidden(),
Tables\Columns\TextColumn::make('department.name', 'name')->hidden(),
Tables\Columns\TextColumn::make('code'),
Tables\Columns\TextColumn::make('name'),
Tables\Columns\TextColumn::make('type'),
Tables\Columns\TextColumn::make('description')->hidden(),
Tables\Columns\TextColumn::make('expense_transactions_sum_amount')->sum('expense_transactions', 'amount')->money('USD', 2)->label('Amount'),
];
}
 
protected function getTableActions(): array
{
return [
Tables\Actions\ViewAction::make()
->form([
Forms\Components\Select::make('company_id')
->label('Company')
->options(Company::all()->pluck('name', 'id')->toArray())
->reactive()
->afterStateUpdated(fn (callable $set) => $set('department_id', null)),
 
Forms\Components\Select::make('department_id')
->label('Department')
->options(function (callable $get) {
$company = Company::find($get('company_id'));
 
if (! $company) {
return Department::all()->pluck('name', 'id');
}
 
return $company->departments->pluck('name', 'id');
}),
 
Forms\Components\TextInput::make('code')
->required(),
Forms\Components\TextInput::make('name')
->required()
->maxLength(255),
Forms\Components\Select::make('type')
->required()
->options([
'Current Asset' => 'Current Asset',
'Fixed Asset' => 'Fixed Asset',
'Tangible Asset' => 'Tangible Asset',
'Intangible Asset' => 'Intangible Asset',
'Operating Asset' => 'Operating Asset',
'Non-Operating Asset' => 'Non-Operating Asset',
]),
Forms\Components\TextInput::make('description')
->maxLength(255),
]),
 
Tables\Actions\EditAction::make()
->form([
Forms\Components\Select::make('company_id')
->label('Company')
->options(Company::all()->pluck('name', 'id')->toArray())
->reactive()
->afterStateUpdated(fn (callable $set) => $set('department_id', null)),
 
Forms\Components\Select::make('department_id')
->label('Department')
->options(function (callable $get) {
$company = Company::find($get('company_id'));
 
if (! $company) {
return Department::all()->pluck('name', 'id');
}
 
return $company->departments->pluck('name', 'id');
}),
 
Forms\Components\TextInput::make('code')
->required(),
Forms\Components\TextInput::make('name')
->required()
->maxLength(255),
Forms\Components\Select::make('type')
->required()
->options([
'Current Asset' => 'Current Asset',
'Fixed Asset' => 'Fixed Asset',
'Tangible Asset' => 'Tangible Asset',
'Intangible Asset' => 'Intangible Asset',
'Operating Asset' => 'Operating Asset',
'Non-Operating Asset' => 'Non-Operating Asset',
]),
Forms\Components\TextInput::make('description')
->maxLength(255),
])
];
}
 
protected function isTablePaginationEnabled(): bool
{
return false;
}
 
protected function getTableHeaderActions(): array
{
return [
Tables\Actions\CreateAction::make()
->form([
Forms\Components\Select::make('company_id')
->label('Company')
->options(Company::all()->pluck('name', 'id')->toArray())
->reactive()
->afterStateUpdated(fn (callable $set) => $set('department_id', null)),
 
Forms\Components\Select::make('department_id')
->label('Department')
->options(function (callable $get) {
$company = Company::find($get('company_id'));
 
if (! $company) {
return Department::all()->pluck('name', 'id');
}
 
return $company->departments->pluck('name', 'id');
}),
 
Forms\Components\TextInput::make('code')
->required(),
Forms\Components\TextInput::make('name')
->required()
->maxLength(255),
Forms\Components\Select::make('type')
->required()
->options([
'Current Asset' => 'Current Asset',
'Fixed Asset' => 'Fixed Asset',
'Tangible Asset' => 'Tangible Asset',
'Intangible Asset' => 'Intangible Asset',
'Operating Asset' => 'Operating Asset',
'Non-Operating Asset' => 'Non-Operating Asset',
]),
Forms\Components\TextInput::make('description')
->maxLength(255),
])
];
}
 
}
  1. Create another File inside of Widgets folder (I have created 5 files within the Widgets Folder but they are all very similar):
  • Revenues.php
<?php
 
namespace App\Filament\Pages\Widgets;
 
use App\Models\Revenue;
use Filament\Tables;
use Filament\Forms;
use App\Models\Company;
use App\Models\Department;
use Filament\Widgets\TableWidget as PageWidget;
use Illuminate\Database\Eloquent\Builder;
 
class Revenues extends PageWidget
{
 
protected int | string | array $columnSpan = [
'md' => 2,
'xl' => 3,
];
 
protected function getTableQuery(): Builder
{
return Revenue::query();
}
 
protected function getTableColumns(): array
{
return [
Tables\Columns\TextColumn::make('company.name', 'name')->hidden(),
Tables\Columns\TextColumn::make('department.name', 'name')->hidden(),
Tables\Columns\TextColumn::make('code'),
Tables\Columns\TextColumn::make('name'),
Tables\Columns\TextColumn::make('type'),
Tables\Columns\TextColumn::make('description')->hidden(),
Tables\Columns\TextColumn::make('income_transactions_sum_amount')->sum('income_transactions', 'amount')->money('USD', 2)->label('Amount'),
];
}
 
protected function getTableActions(): array
{
return [
Tables\Actions\ViewAction::make()
->form([
Forms\Components\Select::make('company_id')
->label('Company')
->options(Company::all()->pluck('name', 'id')->toArray())
->reactive()
->afterStateUpdated(fn (callable $set) => $set('department_id', null)),
 
Forms\Components\Select::make('department_id')
->label('Department')
->options(function (callable $get) {
$company = Company::find($get('company_id'));
 
if (! $company) {
return Department::all()->pluck('name', 'id');
}
 
return $company->departments->pluck('name', 'id');
}),
 
Forms\Components\TextInput::make('code')
->required(),
Forms\Components\TextInput::make('name')
->required()
->maxLength(255),
Forms\Components\Select::make('type')
->required()
->options([
'Revenue' => 'Revenue',
]),
Forms\Components\TextInput::make('description')
->maxLength(255),
]),
 
Tables\Actions\EditAction::make()
->form([
Forms\Components\Select::make('company_id')
->label('Company')
->options(Company::all()->pluck('name', 'id')->toArray())
->reactive()
->afterStateUpdated(fn (callable $set) => $set('department_id', null)),
 
Forms\Components\Select::make('department_id')
->label('Department')
->options(function (callable $get) {
$company = Company::find($get('company_id'));
 
if (! $company) {
return Department::all()->pluck('name', 'id');
}
 
return $company->departments->pluck('name', 'id');
}),
 
Forms\Components\TextInput::make('code')
->required(),
Forms\Components\TextInput::make('name')
->required()
->maxLength(255),
Forms\Components\Select::make('type')
->required()
->options([
'Revenue' => 'Revenue',
]),
Forms\Components\TextInput::make('description')
->maxLength(255),
])
];
}
 
protected function isTablePaginationEnabled(): bool
{
return false;
}
 
protected function getTableHeaderActions(): array
{
return [
Tables\Actions\CreateAction::make()
->form([
Forms\Components\Select::make('company_id')
->label('Company')
->options(Company::all()->pluck('name', 'id')->toArray())
->reactive()
->afterStateUpdated(fn (callable $set) => $set('department_id', null)),
 
Forms\Components\Select::make('department_id')
->label('Department')
->options(function (callable $get) {
$company = Company::find($get('company_id'));
 
if (! $company) {
return Department::all()->pluck('name', 'id');
}
 
return $company->departments->pluck('name', 'id');
}),
 
Forms\Components\TextInput::make('code')
->required(),
Forms\Components\TextInput::make('name')
->required()
->maxLength(255),
Forms\Components\Select::make('type')
->required()
->options([
'Revenue' => 'Revenue',
]),
Forms\Components\TextInput::make('description')
->maxLength(255),
])
];
}
 
}
  1. Edit the page you initially created to be able to display all of your tables within one Page:
  • Note: Tables/Widgets are sorted in the order you put them in this File...
<?php
 
namespace App\Filament\Pages;
 
use Filament\Pages\Page;
use App\Filament\Pages\Widgets;
 
class ChartOfAccounts extends Page
{
protected static ?string $navigationIcon = 'heroicon-o-home';
 
protected static ?int $navigationSort = -1;
 
protected static string $view = 'filament.pages.chart-of-accounts';
 
protected function getHeaderWidgets(): array
{
return [
Widgets\Assets::class, <-- 1st
Widgets\Liabilities::class, <-- 2nd
Widgets\Expenses::class, <-- 3rd
Widgets\Revenues::class, <-- 4th
Widgets\Equities::class, <-- 5th
];
}
 
 
}

That's all, hope you find this helpful.. pretty neat trick. It is especially helpful if some of your models relate to each other like mine did, and it also reduces the amount of space you take up in your project especially if you are going to have a lot of content in your Project..

Here is the source code to my gitrepo if that helps you... GithubRepo

No comments yet…