Kiran Timsina
Kiran Timsina
FFilament
Created by Kiran Timsina on 9/16/2024 in #❓┊help
Move Header Actions to Footer
Is there a way I can move the header actions of edit resource page to footer? I want to swap the positions of save/cancel with the header actions.
8 replies
FFilament
Created by Kiran Timsina on 8/16/2024 in #❓┊help
Tab Badge not updating
I have a tab in ListBlogPosts where i have two tabs with badge that has count. I want the count to be dyanamically updated based on my active filter. Sadly, the count does not update. Probably the code to generate the badge does not run on filter change. Is there a way to achieve it?
public function getTabs(): array
{
$filterYear = $this->getTableFilterState('publish_year')['value'];
$filterMonth = $this->getTableFilterState('publish_month')['value'];

return [
'all' => Tab::make(),

'waiting_for_assignees' => Tab::make()
->modifyQueryUsing(fn (Builder $query) => $query
->where(fn ($q) => $q->whereDoesntHave('website.blogAssigned')->orDoesntHave('website.seoSupportAssigned'))
)
->label('Waiting for Assignees')
->badge(fn () => BlogPost::where(fn ($q) => $q->whereDoesntHave('website.blogAssigned')->orDoesntHave('website.seoSupportAssigned'))
->whereYear('publish_date', $filterYear)
->whereMonth('publish_date', $filterMonth)
->count()),

'waiting_for_title' => Tab::make()
->modifyQueryUsing(fn (Builder $query) => $query
->whereNull('title')
)
->label('Waiting for Title')
->badge(fn () => BlogPost::whereNull('title')
->whereYear('publish_date', $filterYear)
->whereMonth('publish_date', $filterMonth)
->count()
),
];
}
public function getTabs(): array
{
$filterYear = $this->getTableFilterState('publish_year')['value'];
$filterMonth = $this->getTableFilterState('publish_month')['value'];

return [
'all' => Tab::make(),

'waiting_for_assignees' => Tab::make()
->modifyQueryUsing(fn (Builder $query) => $query
->where(fn ($q) => $q->whereDoesntHave('website.blogAssigned')->orDoesntHave('website.seoSupportAssigned'))
)
->label('Waiting for Assignees')
->badge(fn () => BlogPost::where(fn ($q) => $q->whereDoesntHave('website.blogAssigned')->orDoesntHave('website.seoSupportAssigned'))
->whereYear('publish_date', $filterYear)
->whereMonth('publish_date', $filterMonth)
->count()),

'waiting_for_title' => Tab::make()
->modifyQueryUsing(fn (Builder $query) => $query
->whereNull('title')
)
->label('Waiting for Title')
->badge(fn () => BlogPost::whereNull('title')
->whereYear('publish_date', $filterYear)
->whereMonth('publish_date', $filterMonth)
->count()
),
];
}
1 replies
FFilament
Created by Kiran Timsina on 8/12/2024 in #❓┊help
Do not show rows with computed data
I have a table with two columns, both are computed values based on some complex queries. Now i need a way to not show these rows if certain computed value which is shown in the table is 0. Is there a way to do this ? For example in my screenshot, i do not want to show those rows where 0.0 is the only value.
2 replies
FFilament
Created by Kiran Timsina on 2/1/2024 in #❓┊help
Validation not working when state change occurs
I am trying to use regex validation with my slug TextInput. The value of this field is also automatically updated based on name. Now the issue is when i change the slug and save, it validates just fine. But if I change the nam, the slug also updates and even if it is all right, I get validation error. I am inclining towards a bug in the core that's causing this. If anyone have faced similar issue and figured a workaround, please help me with this. Thank You
TextInput::make('name')
->live(onBlur: true)
->unique(ignoreRecord: true)
->placeholder('Enter Brand Name')
->maxLength(255)
->afterStateUpdated(function ($get, $set, ?string $state) {
if (!$get('is_slug_changed_manually') && filled($state)) {
$set('slug', str($state)->slug());
}
}),
TextInput::make()
->afterStateUpdated(function ($set) {
$set('is_slug_changed_manually', true);
})
->unique(ignoreRecord: true)
->required()
->regex('/^[a-z0-9]+(-[a-z0-9]+)*$/')
->validationMessages([
'regex' => 'Slug must start/end with a letter/number and may include only lowercase letters, numbers, and hyphens.',
])
->maxLength(255),
TextInput::make('name')
->live(onBlur: true)
->unique(ignoreRecord: true)
->placeholder('Enter Brand Name')
->maxLength(255)
->afterStateUpdated(function ($get, $set, ?string $state) {
if (!$get('is_slug_changed_manually') && filled($state)) {
$set('slug', str($state)->slug());
}
}),
TextInput::make()
->afterStateUpdated(function ($set) {
$set('is_slug_changed_manually', true);
})
->unique(ignoreRecord: true)
->required()
->regex('/^[a-z0-9]+(-[a-z0-9]+)*$/')
->validationMessages([
'regex' => 'Slug must start/end with a letter/number and may include only lowercase letters, numbers, and hyphens.',
])
->maxLength(255),
2 replies
FFilament
Created by Kiran Timsina on 11/22/2023 in #❓┊help
Updating data of getNavigationBadge()
Is there a way to trigger updating the value returned by getNavigationBadge(). For example, I have a number shown which changes on deleting a row. And that number should now decrease by 1. It does when refreshed, but is there a way to do it without refresing the whole page?
2 replies
FFilament
Created by Kiran Timsina on 8/16/2023 in #❓┊help
A way to make lazy search ?
I have a db with hundred thousand products. the in-built search freezes the table as it searches on type. I made a text input to make implement the search but it also searches on every letter typed. Is there a way to make the search happen only when the the focus changes or enter is pressed?
Filter::make('search')
->form([
Forms\Components\TextInput::make('product_query')->label('Search Product (Name, SKU)'),
])
->columnSpanFull()
->query(function (Builder $query, array $data): Builder {
return $query->when(
$data['product_query'],
fn (Builder $query): Builder => $query->where('name', 'ilike', "%{$data['product_query']}%")
->orWhere('sku', 'ilike', "%{$data['product_query']}%")
);
}),
Filter::make('search')
->form([
Forms\Components\TextInput::make('product_query')->label('Search Product (Name, SKU)'),
])
->columnSpanFull()
->query(function (Builder $query, array $data): Builder {
return $query->when(
$data['product_query'],
fn (Builder $query): Builder => $query->where('name', 'ilike', "%{$data['product_query']}%")
->orWhere('sku', 'ilike', "%{$data['product_query']}%")
);
}),
4 replies
FFilament
Created by Kiran Timsina on 7/29/2023 in #❓┊help
Unable to fetch value of filter on table and update widget
// SupplierTransactionWidget.php
protected $listeners = ['updateSupplierTransactionWidget' => '$refresh'];

protected function getViewData(): array
{
$supplierId = request()->has('tableFilters')
? request('tableFilters')['supplier_id']['value'] : null;
if ($supplierId) {
// The problem is that we are not able to read value from query string on when filter is changed
// dd('here');
$payable = Supplier::find($supplierId)->remainingPayable();
} else {
$bill = SupplierHistory::where('type', 'bill')->sum('amount');
$cheque = SupplierHistory::where('type', 'cheque')->sum('amount');
$payable = round($bill - $cheque, 2);
}
return ['payable' => formatMoney($payable)];
}

// ManagerSupplierHistories.php
public function updated($name)
{
if (Str::of($name)->contains(['tableFilters'])) {
$this->emit('updateSupplierTransactionWidget');
}
}
// SupplierTransactionWidget.php
protected $listeners = ['updateSupplierTransactionWidget' => '$refresh'];

protected function getViewData(): array
{
$supplierId = request()->has('tableFilters')
? request('tableFilters')['supplier_id']['value'] : null;
if ($supplierId) {
// The problem is that we are not able to read value from query string on when filter is changed
// dd('here');
$payable = Supplier::find($supplierId)->remainingPayable();
} else {
$bill = SupplierHistory::where('type', 'bill')->sum('amount');
$cheque = SupplierHistory::where('type', 'cheque')->sum('amount');
$payable = round($bill - $cheque, 2);
}
return ['payable' => formatMoney($payable)];
}

// ManagerSupplierHistories.php
public function updated($name)
{
if (Str::of($name)->contains(['tableFilters'])) {
$this->emit('updateSupplierTransactionWidget');
}
}
9 replies
FFilament
Created by Kiran Timsina on 7/27/2023 in #❓┊help
Resize Images after Upload
I have many models that have "image" column. Each image is uploaded using the FileUpload of filament, but I need to resize it to 5 different sizes. I couldn't use Spatie's package as I have "image" field in many models instead of creating one "media" table and creating all images there.
FileUpload::make('image_file_name')
->image()
->required(),
FileUpload::make('image_file_name')
->image()
->required(),
Right now the solution I am using is writing code in CreateProduct and EditProduct to handle image resize (and deletion of resized images) Here is an example, which works, but doing this for other 50 models would require duplicating this code 50 times. What else can i do to make sure image resize is handled by a single code base. I could think of probably through an event listener, or a middleware or confuguring FileUpload to do it for all. But I'm unaware how I can achieve this in filament.
protected function beforeSave(): void
{
// Runs before the form fields are saved to the database.
$oldPrimaryImage = $this->record->image_file_name;
$newPrimaryImage = Arr::first($this->data['image_file_name']);

// Do this only if new image is received
if ($oldPrimaryImage !== $newPrimaryImage) {
ResizeImages::dispatch([$newPrimaryImage]);
DeleteImages::dispatch([$oldPrimaryImage]);
}

// Getting only the values of both arrays to compare
$oldSecImages = array_values($this->record->sec_images ?: []);
$newSecImages = array_values($this->data['sec_images'] ?? []);
$deletedImages = array_diff($oldSecImages, $newSecImages);
$addedImages = array_diff($newSecImages, $oldSecImages);

if (count($addedImages)) {
ResizeImages::dispatch($addedImages);
}
if (count($deletedImages)) {
DeleteImages::dispatch($deletedImages);
}
}
protected function beforeSave(): void
{
// Runs before the form fields are saved to the database.
$oldPrimaryImage = $this->record->image_file_name;
$newPrimaryImage = Arr::first($this->data['image_file_name']);

// Do this only if new image is received
if ($oldPrimaryImage !== $newPrimaryImage) {
ResizeImages::dispatch([$newPrimaryImage]);
DeleteImages::dispatch([$oldPrimaryImage]);
}

// Getting only the values of both arrays to compare
$oldSecImages = array_values($this->record->sec_images ?: []);
$newSecImages = array_values($this->data['sec_images'] ?? []);
$deletedImages = array_diff($oldSecImages, $newSecImages);
$addedImages = array_diff($newSecImages, $oldSecImages);

if (count($addedImages)) {
ResizeImages::dispatch($addedImages);
}
if (count($deletedImages)) {
DeleteImages::dispatch($deletedImages);
}
}
27 replies
FFilament
Created by Kiran Timsina on 7/18/2023 in #❓┊help
Custom Reports using Filament Tables
I am trying to create views for reports. And in these view I am trying to load filament table with the report data. I tried creating a filament-page and load table but not able to do so due to the error Property [$table] not found on component:...
26 replies
FFilament
Created by Kiran Timsina on 6/24/2023 in #❓┊help
Help with reactive view
I created a basic greeting card maker that uses two models Design (for the content) and Template (for design and structure). The view is rendered from a blade file that has css and html and uses $this->record and $this->record->template for the values. I have been trying to break the view into two parts where there is a form at one since and rendered content at other side so I can create/edit the design in realtime. I am sure this is possible but I am not able to figure out where to begin with. I made two livewire compoenents CardForm and CardDisplay. I moved the whole view blade to card-display. And now I'm stuck right there. Any heads up or a relevant tutorial (Which I am unable to find) would really mean a lot. Thank You
2 replies
FFilament
Created by Kiran Timsina on 6/20/2023 in #❓┊help
Help accessing parent form field value
Since I could not $get the field value when trying to access the field value I found a hack to set form variables like this:
public static function getCakeFlavorsFormField(): Select
{
return Select::make('flavors_list')
->relationship('flavors', 'name') // We need this here since it must load the set data from pivot table
->multiple()
->options(Flavor::getFlavorsPluck('active')->toArray())
->preload()
->reactive()
->afterStateUpdated(function (callable $set, $state) {
// $state is the selected flavors
if (!$state) {
$flavors = Flavor::getFlavorsPluck('active')->toArray();
} else {
$flavors = Flavor::getFlavorsPluck('active')->only($state)->toArray();
}

// Set 'flavors' for the 'cake' group
$set('cake.selectedFlavors', $flavors);
$set('cake.flavor_id', null); // Reset the default flavor whenever flavors_list changes
})->required();
}
public static function getDefaultCakeFlavorFormField(): Group
{
return Group::make()->schema([
Select::make('flavor_id')->label('Default Flavor')
->options(function (callable $get): array {

// I could not do $get('flavors_list') and get the values directly. This is always null

$flavors = $get('selectedFlavors');
if ($flavors) {
return $get('selectedFlavors');
}
return Flavor::getFlavorsPluck('active')->toArray();
})
])->relationship('cake');
}
public static function getCakeFlavorsFormField(): Select
{
return Select::make('flavors_list')
->relationship('flavors', 'name') // We need this here since it must load the set data from pivot table
->multiple()
->options(Flavor::getFlavorsPluck('active')->toArray())
->preload()
->reactive()
->afterStateUpdated(function (callable $set, $state) {
// $state is the selected flavors
if (!$state) {
$flavors = Flavor::getFlavorsPluck('active')->toArray();
} else {
$flavors = Flavor::getFlavorsPluck('active')->only($state)->toArray();
}

// Set 'flavors' for the 'cake' group
$set('cake.selectedFlavors', $flavors);
$set('cake.flavor_id', null); // Reset the default flavor whenever flavors_list changes
})->required();
}
public static function getDefaultCakeFlavorFormField(): Group
{
return Group::make()->schema([
Select::make('flavor_id')->label('Default Flavor')
->options(function (callable $get): array {

// I could not do $get('flavors_list') and get the values directly. This is always null

$flavors = $get('selectedFlavors');
if ($flavors) {
return $get('selectedFlavors');
}
return Flavor::getFlavorsPluck('active')->toArray();
})
])->relationship('cake');
}
But a much easier way would be if I could simply $get the field value from another formgroup. Does anyone have idea about this?
5 replies
FFilament
Created by Kiran Timsina on 6/19/2023 in #❓┊help
Issue with non-skippable wizard when a step is hidden
Clicking the next on the 2nd step does not do anything when product_type is not cake. It works fine if it is a cake. public static function form(Form $form): Form { return $form ->schema([ Wizard::make([ Wizard\Step::make('Information') ->description(fn ($context) => $context === 'create' ? 'Basic Info!' : null) ->schema([ static::getTypeFormField(), ... ])->columns(2), Wizard\Step::make('Cake') ->description(fn ($context) => $context === 'create' ? 'Cake Details!' : null) ->schema([ static::getCakeFormField() ])->hidden( fn (callable $get) => $get('product_type') !== 'cake' ), Wizard\Step::make('Price') ->description(fn ($context) => $context === 'create' ? 'Price and Sale' : null) ->schema([ static::getPriceSetFormField(), ... ]), Wizard\Step::make('SEO/Search') ->description(fn ($context) => $context === 'create' ? 'Search Related Info' : null) ->schema([ static::getMetaGroupFormField(), ]), ... ])->columnSpanFull() ->skippable(fn ($context) => $context === 'edit') ]); }
3 replies
FFilament
Created by Kiran Timsina on 6/17/2023 in #❓┊help
Can't get the colors to work
I am trying to follow the statsoverview docs and create some cards like this: Card::make( 'Idle Bakers', "$idleBakers" )->color($idleBakers > 0 ? 'warning' : 'success'), Card::make( 'Unprocessed Orders', "$unprocessedOrders" )->color(($idleBakers && $unprocessedOrders) ? 'danger' : (($unprocessedOrders && !$idleBakers) ? 'warning' : 'success')), Card::make( 'Ready Orders', "$readyOrders" ), Card::make( 'Idle Riders', "$idleRiders" )->color(($readyOrders && $idleRiders) ? 'danger' : (($readyOrders && !$idleRiders) ? 'warning' : 'success')), My tailwind.config.js, vite.config.js and postcss.config.js are as per this doc: https://filamentphp.com/docs/2.x/admin/appearance#building-themes Here is my AppServiceProvider public function boot(): void { // Filament::serving(function () { // // Using Vite // Filament::registerViteTheme('resources/css/filament.css'); // }); } I have commented the code becuase it makes the whole ui break as filament.css is not present
10 replies
FFilament
Created by Kiran Timsina on 6/14/2023 in #❓┊help
Custom View Page using Filament Table Builder
I want to create a custom view page that can use Filament's Table Builder and build tables instead of using HTML/CSS . I am sure that's why FIlmanet tables is for. but I am confused about how to do it. I have a ViewOrder.php where I have done this: protected static string $view = 'filament.resources.orders.pages.view-order'; </x-filament::page> html and css code here </x-filament::page> It is working fine but I am using html/css to manually build the page.
13 replies
FFilament
Created by Kiran Timsina on 6/12/2023 in #❓┊help
Clickable grid layout table
2 replies
FFilament
Created by Kiran Timsina on 6/10/2023 in #❓┊help
How to run a query based on fields and show output in another field
This is my form : return $form ->schema([ Group::make([ Toggle::make('has_verified_email')->reactive(), Toggle::make('has_verified_phone')->reactive(), Toggle::make('new_users_only')->reactive() ->afterStateUpdated(fn (Closure $set, $get, $state) => $get('new_users_only') ? $set('old_users_only', !$state) : ''), Toggle::make('old_users_only')->reactive() ->afterStateUpdated(fn (Closure $set, $get, $state) => $get('old_users_only') ? $set('new_users_only', !$state) : ''), Forms\Components\TextInput::make('test')->afterStateUpdated( function (Closure $get) { $count = User::when($get('has_verified_email'), function ($q) { $q->whereNotNull('email_verified_at'); })->when($get('has_verified_phone'), function ($q) { $q->whereNotNull('has_verified_phone'); })->when($get('new_users_only'), function ($q) { $q->whereDate('created_at', '>=', now()->subMonth()); })->when($get('old_users_only'), function ($q) { $q->whereDate('created_at', '<=', now()->subMonth()); })->count(); return "$count users selected!"; } ) ->columnSpanFull()->disabled()->disableLabel() ])->columnSpan(12)->columns(4), I want to fill up the field 'test' with the count of users based on the other toggles set. I could set it using default but it works in first load only. I want to update the 'test' every time a toggle is changed. Is there a way to achieve this? Thank You
4 replies