gizmojo
gizmojo
FFilament
Created by gizmojo on 7/28/2024 in #❓┊help
How to change action view based on arguments?
Is it possible to use tap to apply multiple options or does Filament have another way?
Action::make('test')
->tap(function (Action $action) {
$arguments = $action->getArguments();
if (isset($arguments['type']) && $arguments['type'] === 'button') {
$action->grouped()->groupedIcon('heroicon-o-chevron-right');
} else {
$action->button()->color('primary');
}
})
Action::make('test')
->tap(function (Action $action) {
$arguments = $action->getArguments();
if (isset($arguments['type']) && $arguments['type'] === 'button') {
$action->grouped()->groupedIcon('heroicon-o-chevron-right');
} else {
$action->button()->color('primary');
}
})
Arguments is always null
@if ($displayActionButton)
{{ ($this->testAction)(['type' => 'button']) }}
@else
{{ $this->testAction }}
@endif
@if ($displayActionButton)
{{ ($this->testAction)(['type' => 'button']) }}
@else
{{ $this->testAction }}
@endif
2 replies
FFilament
Created by gizmojo on 7/11/2024 in #❓┊help
Change filament button display/icon in alpinejs?
Is there a way of using Filament button to change the outline and icon after clicking? But without making a livewire request to the server
<div x-data="{ isReordering: false }">
<x-filament::button
icon="heroicon-o-arrows-up-down"
size="xs"
color="info"
outlined
x-on:click="isReordering = ! isReordering"
x-bind:class="{ 'bg-primary-500 text-white': isReordering }"
>
Reorder
</x-filament::button>
</div>
<div x-data="{ isReordering: false }">
<x-filament::button
icon="heroicon-o-arrows-up-down"
size="xs"
color="info"
outlined
x-on:click="isReordering = ! isReordering"
x-bind:class="{ 'bg-primary-500 text-white': isReordering }"
>
Reorder
</x-filament::button>
</div>
1 replies
FFilament
Created by gizmojo on 6/9/2024 in #❓┊help
How do we overwrite all resource pages?
I'm trying to change how the heading works across all pages without editing them all. Tried using macro but this doesn't get called. Also tried aliasing the BasePage class. Is there another way?
\Filament\Pages\BasePage::macro('getHeading', function (): string|Htmlable {
return 'Test Heading';
});
\Filament\Pages\BasePage::macro('getHeading', function (): string|Htmlable {
return 'Test Heading';
});
6 replies
FFilament
Created by gizmojo on 1/29/2024 in #❓┊help
Replicate action with success redirect
This doesn't work because replica is a protected property but it needs to be set for the successRedirectUrl to evaluate it.
ReplicateAction::make()
->using(function ($action, Product $record): Product {
$action->replica = $record->customRelpicate();
return $action->replica;
})
->successRedirectUrl(
fn (Product $replica): string => ProductResource::getUrl('edit', ['record' => $replica])
)
ReplicateAction::make()
->using(function ($action, Product $record): Product {
$action->replica = $record->customRelpicate();
return $action->replica;
})
->successRedirectUrl(
fn (Product $replica): string => ProductResource::getUrl('edit', ['record' => $replica])
)
1 replies
FFilament
Created by gizmojo on 1/27/2024 in #❓┊help
Create select option form without relationship
Trying to use the createOptionForm so that it works without a relationship. This almost works apart from the select value/select option, is not updated even though the state is updated correctly.
Forms\Components\Select::make('group')
->native()
->required()
->options(function ($state): array {
$options = [];// fetch unique groups from DB
if ($state && ! isset($options[$state])) {
$options[$state] = $state;
}
return $options;
})
->createOptionForm(fn (): array => [
Forms\Components\TextInput::make('group')
])
->createOptionUsing(fn (array $data) => $data['group']),
Forms\Components\Select::make('group')
->native()
->required()
->options(function ($state): array {
$options = [];// fetch unique groups from DB
if ($state && ! isset($options[$state])) {
$options[$state] = $state;
}
return $options;
})
->createOptionForm(fn (): array => [
Forms\Components\TextInput::make('group')
])
->createOptionUsing(fn (array $data) => $data['group']),
2 replies
FFilament
Created by gizmojo on 1/27/2024 in #❓┊help
Update field state from alpine
Is there a way of setting the wire state from alpine? E.g here the state remains the one step behind the last letter is capital.
Forms\Components\TextInput::make('group')
->extraAlpineAttributes([
'x-on:keyup' => <<<'JS'
$event.target.value = $event.target.value.toLowerCase()
JS
])
->rules(['lowercase'])

// Test code to show what's happening
->helperText(fn ($state) => $state)
->reactive()
Forms\Components\TextInput::make('group')
->extraAlpineAttributes([
'x-on:keyup' => <<<'JS'
$event.target.value = $event.target.value.toLowerCase()
JS
])
->rules(['lowercase'])

// Test code to show what's happening
->helperText(fn ($state) => $state)
->reactive()
I know I can do all this easily with formatStateUsing but I need it to be instant
5 replies
FFilament
Created by gizmojo on 1/25/2024 in #❓┊help
Change column sort direction to descending by default
For a numeric column on first click to sort the column we want to show the largest first but can't find a way of doing this without writing the full sortBy query every time. Note this is per column and not the defaultSort for the table.
2 replies
FFilament
Created by gizmojo on 1/24/2024 in #❓┊help
How to hook into 'create another' action
Trying to pre-fill the form when you click 'create another' action. It's within a relation manager's table CreateAction. If I overwrite the footer actions when calling makeModalSubmitAction it's returning a StaticAction without access to the form.
6 replies
FFilament
Created by gizmojo on 1/23/2024 in #❓┊help
Use table alias in select filter
Need to add a table alias due to joins and ambiguous columns but adding this assumes it's a relationship. Is there another way without writing the where condition manually?
Tables\Filters\SelectFilter::make('deleted')
->attribute('products.deleted')
Tables\Filters\SelectFilter::make('deleted')
->attribute('products.deleted')
1 replies
FFilament
Created by gizmojo on 12/30/2023 in #❓┊help
Test form component's action
Is there a way of interacting with individual form components inside a form? E.g how to call add action on a repeater? mountFormComponentAction('data.categories', 'add') Or testing a select's createOptionForm mountFormComponentAction('data.categories.record-183.language_id', 'editOption') In v2 you could
livewire(EditProduct::class, [
'record' => $product->getKey(),
])
->call(
'dispatchFormEvent',
'repeater::createItem',
'data.categories',
)
livewire(EditProduct::class, [
'record' => $product->getKey(),
])
->call(
'dispatchFormEvent',
'repeater::createItem',
'data.categories',
)
Also is there a way of asserting that an action was called or is there a way to test notifications for an action. How do test the replicate action redirects to the newly created product in assertRedirect?
livewire(EditProduct::class, [
'record' => $product->id,
])
->callAction('replicate', data: [
'name' => $name = $this->faker->unique()->realText(),
])
->assertHasNoActionErrors()
->assertNotified('Replicated')
->assertRedirect();
livewire(EditProduct::class, [
'record' => $product->id,
])
->callAction('replicate', data: [
'name' => $name = $this->faker->unique()->realText(),
])
->assertHasNoActionErrors()
->assertNotified('Replicated')
->assertRedirect();
This shows 'A notification was not sent'
5 replies
FFilament
Created by gizmojo on 12/28/2023 in #❓┊help
Fieldset with single column
There doesn't appear to be a way of overwriting a fieldset to have one column (without extending the class and overwrite the setup method). This code is defaulting to columns ['default' => 1, 'lg' => 2]
Fieldset::make()
->columnSpan(1)
->columns(['default' => 1])
Fieldset::make()
->columnSpan(1)
->columns(['default' => 1])
12 replies
FFilament
Created by gizmojo on 12/23/2023 in #❓┊help
Testing table set activeTab and toggle all columns
Currently setting the toggle session manually but wondering if that's a way of making this dynamic?
session(['tables' => ['ListProducts_toggled_columns' => ['name' => true]]]);
session(['tables' => ['ListProducts_toggled_columns' => ['name' => true]]]);
But how would you set the active tab?
$products = Collection::factory()->count(3)->create([
'type' => 'Group'
]);

Collection::factory()->count(3)->create([
'type' => 'Another Group'
])

livewire(ListProducts::class)
->assertCountTableRecords(3)
->assertCanSeeTableRecords($products)
->sortTable('name')
$products = Collection::factory()->count(3)->create([
'type' => 'Group'
]);

Collection::factory()->count(3)->create([
'type' => 'Another Group'
])

livewire(ListProducts::class)
->assertCountTableRecords(3)
->assertCanSeeTableRecords($products)
->sortTable('name')
E.g
public function getTabs(): array
{
return [
Tab::make('Test')
->modifyQueryUsing(fn (Builder $query) => $query->where('type', 'Group'))
];
}
public function getTabs(): array
{
return [
Tab::make('Test')
->modifyQueryUsing(fn (Builder $query) => $query->where('type', 'Group'))
];
}
5 replies
FFilament
Created by gizmojo on 12/11/2023 in #❓┊help
Syntax error with text-input-column.blade.php
No description
4 replies
FFilament
Created by gizmojo on 12/8/2023 in #❓┊help
Only show header action if table has records
Is there a better(performance/cached) way of only showing a header action if the table has records?
$table->query()
->columns([])
->headerActions([
ExportAction::make('Export')
->visible(fn () => $this->table->getRecords()->isNotEmpty()),
$table->query()
->columns([])
->headerActions([
ExportAction::make('Export')
->visible(fn () => $this->table->getRecords()->isNotEmpty()),
5 replies
FFilament
Created by gizmojo on 8/22/2023 in #❓┊help
How to modify the form opened from a select option?
E.g I want to adjust the form columns to two, by default it's one. I don't want to use Grid components everywhere. Is there a way of modifying the form from within editOptionAction?
Select::make()
->editOptionForm(CustomResource::getFormSchema())
->editOptionAction(fn (Forms\Components\Actions\Action $action) => $action->modalWidth('screen'))
Select::make()
->editOptionForm(CustomResource::getFormSchema())
->editOptionAction(fn (Forms\Components\Actions\Action $action) => $action->modalWidth('screen'))
6 replies
FFilament
Created by gizmojo on 8/15/2023 in #❓┊help
Re-filling a form with select value
I have an issue where the select option is not being selected when the value is set for the second time. E.g I've got a custom form inside a modal - when the modal is opened I fill the form based on the record you've clicked on. Works fine on the first opening, but not if you close the modal and then reopen a different record the fill is not populating the form select value. - It's only an issue when a select. Works fine for text input. - The fill is working because the $state is correct e.g ->helperText(fn ($state) => $state) - Inspecting the DOM select options the value exists but the select value is blank.
1 replies
FFilament
Created by gizmojo on 8/8/2023 in #❓┊help
Possible to combine JS loadedOnRequest with Alpine.data?
This gives undefined foo error - which's a function that's defined inside the foo.js file.
<div
x-load-js="[@js(\Filament\Support\Facades\FilamentAsset::getScriptSrc('foo'))]"
x-data="foo()"
>
<div
x-load-js="[@js(\Filament\Support\Facades\FilamentAsset::getScriptSrc('foo'))]"
x-data="foo()"
>
const foo = () => ({
// ...
});
window.Alpine.data("foo", foo);
const foo = () => ({
// ...
});
window.Alpine.data("foo", foo);
3 replies
FFilament
Created by gizmojo on 8/7/2023 in #❓┊help
How to read the lang file/translation when registering navigationItems in a panel?
E.g this displays the 'filament-panels::layout.actions.billing.label' instead of the translated label.
public function panel(Panel $panel): Panel
{
return $panel
// ...
->navigationItems([
NavigationItem::make('billing')
->label(__('filament-panels::layout.actions.billing.label'))
]);
}
public function panel(Panel $panel): Panel
{
return $panel
// ...
->navigationItems([
NavigationItem::make('billing')
->label(__('filament-panels::layout.actions.billing.label'))
]);
}
Another issue when registering a custom navigation you've unable to use the resource getUrl method. This used to work in v2 but getting an error that the panel has not been setup yet.
Call to a member function getId() on null
public static function getRouteBaseName(?string $panel = null): string
{
$panel ??= Filament::getCurrentPanel()->getId();
public static function getRouteBaseName(?string $panel = null): string
{
$panel ??= Filament::getCurrentPanel()->getId();
NavigationItem::make('product-redirects')
->url(ProductResource::getUrl('redirects'))
->isActiveWhen(fn () => request()->routeIs(ProductResource::getRouteBaseName() . '.redirects'))
NavigationItem::make('product-redirects')
->url(ProductResource::getUrl('redirects'))
->isActiveWhen(fn () => request()->routeIs(ProductResource::getRouteBaseName() . '.redirects'))
Redirects is a custom list resource page which needs registering manually
public static function getPages(): array
{
return [
'index' => Pages\ListProductRedirect::route('/'),
'redirects' => Pages\ListProductRedirect::route('/redirects'),
];
}
public static function getPages(): array
{
return [
'index' => Pages\ListProductRedirect::route('/'),
'redirects' => Pages\ListProductRedirect::route('/redirects'),
];
}
Tried to manually use route() helper function instead of the getting the resources but that does not work either.
Route [filament.admin.resources.products.index] not defined.
->navigationItems([
NavigationItem::make()
->label('Settings')
->url(route('filament.admin.resources.products.index'))
->icon('heroicon-o-cog-6-tooth'),
])
->navigationItems([
NavigationItem::make()
->label('Settings')
->url(route('filament.admin.resources.products.index'))
->icon('heroicon-o-cog-6-tooth'),
])
Confirmed the route name is correct and visible when I run php artisan route:list. Adding debug code to test the routes collection is empty at the point of registering the panel configuration dd(Route::getRoutes());
8 replies
FFilament
Created by gizmojo on 8/6/2023 in #❓┊help
Eager loaded relationship based on table filter
I'm trying to load a relationship based on the table filter. In v2 I could do this but in v3 throws error:
Typed property Filament\Widgets\TableWidget::$table must not be accessed before initialization
protected function getTableQuery(): Builder
{
$filter_language = $this->getCachedTableFilter('language_id') ?? null;

return Product::when(
$filter_language,
fn (Builder $query, $language_id): Builder => $query->withWhereHas('language', fn ($query) => $query->where('id', $language_id)),
)
->when(
! $filter_language,
fn (Builder $query): Builder => $query->with('language', fn ($query) => $query->where('default', 1)),
);
}
protected function getTableQuery(): Builder
{
$filter_language = $this->getCachedTableFilter('language_id') ?? null;

return Product::when(
$filter_language,
fn (Builder $query, $language_id): Builder => $query->withWhereHas('language', fn ($query) => $query->where('id', $language_id)),
)
->when(
! $filter_language,
fn (Builder $query): Builder => $query->with('language', fn ($query) => $query->where('default', 1)),
);
}
When I switch over to using table method with filters the when is called but the eager relationship is never loaded causing error:
Attempted to lazy load [language] on model
$table->columsn([])
->filters([
Tables\Filters\SelectFilter::make('language_id')
->options(fn () => Language::options())
->query(function (Builder $query, $state): Builder {
// Or simplified down to this doesn't get eager loaded
// return $query->with('language');

$language_id = $state['value'] ?? null;

return $query
->when(
$language_id,
fn (Builder $query, $language_id): Builder => $query->withWhereHas('language', fn ($query) => $query->where('id', $language_id)),
)
->when(
! $language_id,
fn (Builder $query): Builder => $query->with('language', fn ($query) => $query->orderByRaw('FIELD(`default`, 0), `name` ASC')),
);
})
$table->columsn([])
->filters([
Tables\Filters\SelectFilter::make('language_id')
->options(fn () => Language::options())
->query(function (Builder $query, $state): Builder {
// Or simplified down to this doesn't get eager loaded
// return $query->with('language');

$language_id = $state['value'] ?? null;

return $query
->when(
$language_id,
fn (Builder $query, $language_id): Builder => $query->withWhereHas('language', fn ($query) => $query->where('id', $language_id)),
)
->when(
! $language_id,
fn (Builder $query): Builder => $query->with('language', fn ($query) => $query->orderByRaw('FIELD(`default`, 0), `name` ASC')),
);
})
Tried to access the filter via livewire $table->getLivewire()->getTableFilterState('language_id') but this causes a recursive loop 500 error. Also tried $this->getTableFilterState('language_id')['value'] ?? null;
3 replies
FFilament
Created by gizmojo on 8/1/2023 in #❓┊help
Testing auth with actingAs inside panel
Updating tests for v3 but how do you register a panel because the auth check is always null?
it('throws auth exception called without auth', function () {
Livewire::test(CustomLivewireComponet::class)
->call('run');
})->throws(AuthenticationException::class);

it('can run with auth', function () {
Livewire::actingAs($this->adminUser);

Livewire::test(CustomLivewireComponet::class)
->call('run')
->assertNotified();
});
it('throws auth exception called without auth', function () {
Livewire::test(CustomLivewireComponet::class)
->call('run');
})->throws(AuthenticationException::class);

it('can run with auth', function () {
Livewire::actingAs($this->adminUser);

Livewire::test(CustomLivewireComponet::class)
->call('run')
->assertNotified();
});
public function mount(): void
{
throw_if(
! Filament::auth()->check(),
AuthenticationException::class
);
}
public function mount(): void
{
throw_if(
! Filament::auth()->check(),
AuthenticationException::class
);
}
at vendor/filament/filament/src/FilamentManager.php:50
46▕ protected ?Model $tenant = null;
47▕
48▕ public function auth(): Guard
49▕ {
➜ 50▕ return $this->getCurrentPanel()->auth();
51▕ }
at vendor/filament/filament/src/FilamentManager.php:50
46▕ protected ?Model $tenant = null;
47▕
48▕ public function auth(): Guard
49▕ {
➜ 50▕ return $this->getCurrentPanel()->auth();
51▕ }
6 replies