Jon Mason
Jon Mason
FFilament
Created by Jon Mason on 2/8/2025 in #❓┊help
Help with custom field
I have a custom input field that works great in any normal form, but in a repeater, whenever a value is entered into one item in a repeater, the same value is applied to the field in all the other repeater items as well. I suspect that it's related to the blade file, but I have no idea how to go about fixing it. Even some steps to take to investigate would be super helpful.
@php
$prefixLabel = $getPrefixLabel();
$isDisabled = $isDisabled();
@endphp

<x-dynamic-component :component="$getFieldWrapperView()" :field="$field">

<div x-data="{
state: $wire.entangle('{{ $getStatePath() }}'),
updateState() {
this.$wire.set('{{ $getStatePath() }}', this.state);
}
}">
<x-filament::input.wrapper :prefix="$prefixLabel" :disabled="$isDisabled">
<x-filament::input type="text" x-model="state" @blur="updateState" />
</x-filament::input.wrapper>
</div>
</x-dynamic-component>
@php
$prefixLabel = $getPrefixLabel();
$isDisabled = $isDisabled();
@endphp

<x-dynamic-component :component="$getFieldWrapperView()" :field="$field">

<div x-data="{
state: $wire.entangle('{{ $getStatePath() }}'),
updateState() {
this.$wire.set('{{ $getStatePath() }}', this.state);
}
}">
<x-filament::input.wrapper :prefix="$prefixLabel" :disabled="$isDisabled">
<x-filament::input type="text" x-model="state" @blur="updateState" />
</x-filament::input.wrapper>
</div>
</x-dynamic-component>
4 replies
FFilament
Created by Jon Mason on 2/7/2025 in #❓┊help
Persisting repeater changes to DB (TableRepeter)
I have a somewhat complex repeater table where I'm populating the table with custom calculations essentially, so when I use the relationship() method, it doesn't work. What I need to do is in each repeater field's afterStateUpdated method, I want to get the current row and save the changes to the database. It seems like this should be pretty straight-forward, but I think the repeater is relying on a pretty simple relationship and not designed for my use case? Either that or I'm just too dumb to figure it out. I've tried various things trying to get the $component->getContainer() and use that to get the ID of the row? I really just need to get the ID of the row, which is in my data, so if I could get the row data I'd be happy. I tried passing in array $data , array $arguments, Repeater $component, nothing seems to give me anything useful. What am I missing, because it seems like this should be easy.
->afterStateUpdated(function (TextInput $component, ?string $state, ?string $old, Get $get, Set $set) {
???
}
->afterStateUpdated(function (TextInput $component, ?string $state, ?string $old, Get $get, Set $set) {
???
}
3 replies
FFilament
Created by Jon Mason on 2/6/2025 in #❓┊help
repeater deleteAction get deleted record to handle it?
No description
3 replies
FFilament
Created by Jon Mason on 1/27/2025 in #❓┊help
form with multiple fields using the same model property
I have a single amount field on my model, and another field that indicates whether the amount is a credit or a debit. I need to display this data in a form and I'm having trouble creating 2 form fields based on the same model attribute. I'm using this to get my form data (for the sake of this question, i'm leaving out the logic that differentiates between debits and credits):
->fillForm(function (AutomatedSalesJournalEntryHeader $record) {
return dd($record->lines->map(function (AutomatedSalesJournalEntryLine $line) {
return [
'account_ref_id' => $line->account_ref_id,
'debits' => $line->amount,
'credits' => $line->amount,
'description' => $line->description,
];
})->toArray());
})
->fillForm(function (AutomatedSalesJournalEntryHeader $record) {
return dd($record->lines->map(function (AutomatedSalesJournalEntryLine $line) {
return [
'account_ref_id' => $line->account_ref_id,
'debits' => $line->amount,
'credits' => $line->amount,
'description' => $line->description,
];
})->toArray());
})
And then I want to display them in these form fields:
TextInput::make('debits')

TextInput::make('credits')
TextInput::make('debits')

TextInput::make('credits')
I can dd the fillForm method and I get the expected data, but it's not populating in the form fields. The only way I can get any data to populate in the form fields is to name the field the same as my model, and then it just seems to disregard the fillForm method.
4 replies
FFilament
Created by Jon Mason on 1/27/2025 in #❓┊help
TableRepeater / Repeater populating form
Trying to use the TableRepeater plugin and it's working great except I have rows in my database that I want to populate the form with, and I can't seem to get it to do that. I must be missing something. This is an edit modal action on a table row, if that's matters. So the user clicks the table row's edit button, which brings up a modal that should display all the line items of the parent row in the TableRepeater.
Action::make('customEdit')
->model(AutomatedSalesJournalEntryHeader::class)
->form([
TableRepeater::make('lines')
->relationship()
->streamlined()
->headers([
Header::make('Account')->width('200px'),
Header::make('Debits')->width('100px'),
Header::make('Credits')->width('100px'),
Header::make('Description')->width('150px'),
])
->schema([
Select::make('account_ref_id')
->required()
->options(fn() => $this->accounts)
TextInput::make('amount')
->required(),
...
])
]),
Action::make('customEdit')
->model(AutomatedSalesJournalEntryHeader::class)
->form([
TableRepeater::make('lines')
->relationship()
->streamlined()
->headers([
Header::make('Account')->width('200px'),
Header::make('Debits')->width('100px'),
Header::make('Credits')->width('100px'),
Header::make('Description')->width('150px'),
])
->schema([
Select::make('account_ref_id')
->required()
->options(fn() => $this->accounts)
TextInput::make('amount')
->required(),
...
])
]),
10 replies
FFilament
Created by Jon Mason on 10/28/2024 in #❓┊help
conditionally required validation
This doesn't seem to be working:
Select::make('time_entry_category_id')
->required(fn() => !$this->isRunning)
Select::make('time_entry_category_id')
->required(fn() => !$this->isRunning)
I also want to be able to conditionally require if a field is visible, and this also doesn't seem to be working:
Select::make('time_entry_category_id')
->required(fn() => !$this->isRunning)
->visible(fn() => !$this->isRunning)
Select::make('time_entry_category_id')
->required(fn() => !$this->isRunning)
->visible(fn() => !$this->isRunning)
I've tried replacing the variables with just hard coded true/false values in case the variable wasn't getting set up right, but that's not working either.
2 replies
FFilament
Created by Jon Mason on 10/25/2024 in #❓┊help
flatpickr monthSelect disabledDates
I have a flatpickr that is a month selector. I want to disable months in the dropdown, but using the disabledDates method doesn't work. How would I disable months in the dropdown?
2 replies
FFilament
Created by Jon Mason on 10/2/2024 in #❓┊help
multiple widget filters on a dashboard?
How would I accomplish having filters within a widget card on a Dashboard page so that each widget can be filtered independently? Or maybe have a section that has one set of filters that controls a set of widgets, and another section that has a different set of filters for a different group of widgets? Is that possible? I'm envisioning a dashboard with multiple metrics, requiring different filters.
3 replies
FFilament
Created by Jon Mason on 10/2/2024 in #❓┊help
multiple dashboards
i've read in the docs how to create multiple dashboards, but anytime I remove Dashboard::class from my pages() method as instructed, the navigation item disappears, and the next page in in my app just basically becomes the default. I've tried different things to try and make the dashboard show up without being in the pages method, but I haven't been able to. I've gone and looked at the Dashboard base class in the Filament directory and can't glean from that how exactly I should be making this work. It's not entirely clear from the docs (or I'm dumb) how exactly you can go about creating multiple dashboards.
->pages([
Dashboard::class,
])
->pages([
Dashboard::class,
])
15 replies
FFilament
Created by Jon Mason on 9/25/2024 in #❓┊help
Can't access tenant in Test class?
A simple test like this:
public function test_renders_successfully()
{
Livewire::test(TimeSheet::class)
->assertStatus(200);
}
public function test_renders_successfully()
{
Livewire::test(TimeSheet::class)
->assertStatus(200);
}
fails because it says it's missing the tenant parameter in the route. I've tried doing something like Filament::setTenant(Team::factory()->create()); in my setup method, and various other things and can't seem to get it to either use a fake tenant, or to ignore the tenant entirely. This particular page doesn't even rely on tenancy, it just exists in a panel where tenancy is used. Any ideas?
6 replies
FFilament
Created by Jon Mason on 9/14/2024 in #❓┊help
wire:keydown call alpine method
I see I can use etraInputAttributes to add wire:keydown.enter to an input. The method provided there will call a component method in livewire, but what if I want to call a javascript method on the alpine component? Is there a way to do that?
TextInput::make('description')
->placeholder('Enter a description')
->extraInputAttributes(['wire:keydown.enter' => 'startTimer']),
TextInput::make('description')
->placeholder('Enter a description')
->extraInputAttributes(['wire:keydown.enter' => 'startTimer']),
5 replies
FFilament
Created by Jon Mason on 9/7/2024 in #❓┊help
Flux?
This might be a super dumb question.....I was just watching Caleb Porzio's presentation at Laracon for Flux and it looks great (https://fluxui.dev/). Would there be a way (or a future plan) to incorporate the styling of flux into Filament while still taking advantage of the magic of creating forms with Filament? I realize Flux isn't even available yet, just wondering if there are any thoughts on that from the great folks working on/with Filament.
12 replies
FFilament
Created by Jon Mason on 8/29/2024 in #❓┊help
file upload validation - file name must start with value
Is it possible to do something like this on a file upload component? this doesn't work, but it's the idea I'm going for:
->rules([
function ($attribute, $value, $fail) {
$fileName = $value->getClientOriginalName();
$startsWith = 'required_prefix_';

if (!str_starts_with($fileName, $startsWith)) {
$fail("The file name must start with {$startsWith}.");
}
},
])
->rules([
function ($attribute, $value, $fail) {
$fileName = $value->getClientOriginalName();
$startsWith = 'required_prefix_';

if (!str_starts_with($fileName, $startsWith)) {
$fail("The file name must start with {$startsWith}.");
}
},
])
3 replies
FFilament
Created by Jon Mason on 8/27/2024 in #❓┊help
Custom field with HasAffixes trait
Trying to set up this custom field and want to add a prefix $ icon. I can't seem to get a prefix to work on this field and wondering what I'm doing wrong.
use Filament\Forms\Components\Field;
use Filament\Forms\Components\Concerns\HasAffixes;

class MathInput extends Field
{
use HasAffixes;

protected string $view = 'forms.components.math-input';

protected function setUp(): void
{
parent::setUp();

$this->live(onBlur: true)
->afterStateUpdated(function (?string $state, ?string $old) {
if ($state != null) {
$updatedState = $this->performMathOperation($state);
$this->state($updatedState);
}
});
}

public function getPrefixIcon(): ?string
{
return 'heroicon-o-currency-dollar';
}
use Filament\Forms\Components\Field;
use Filament\Forms\Components\Concerns\HasAffixes;

class MathInput extends Field
{
use HasAffixes;

protected string $view = 'forms.components.math-input';

protected function setUp(): void
{
parent::setUp();

$this->live(onBlur: true)
->afterStateUpdated(function (?string $state, ?string $old) {
if ($state != null) {
$updatedState = $this->performMathOperation($state);
$this->state($updatedState);
}
});
}

public function getPrefixIcon(): ?string
{
return 'heroicon-o-currency-dollar';
}
<x-dynamic-component :component="$getFieldWrapperView()" :field="$field">

<div x-data="{
state: $wire.entangle('{{ $getStatePath() }}'),
updateState() {
this.$wire.set('{{ $getStatePath() }}', this.state);
}
}">
<x-filament::input.wrapper>
<x-filament::input type="text" x-model="state" @blur="updateState" />
</x-filament::input.wrapper>
</div>
</x-dynamic-component>
<x-dynamic-component :component="$getFieldWrapperView()" :field="$field">

<div x-data="{
state: $wire.entangle('{{ $getStatePath() }}'),
updateState() {
this.$wire.set('{{ $getStatePath() }}', this.state);
}
}">
<x-filament::input.wrapper>
<x-filament::input type="text" x-model="state" @blur="updateState" />
</x-filament::input.wrapper>
</div>
</x-dynamic-component>
10 replies
FFilament
Created by Jon Mason on 8/24/2024 in #❓┊help
odd form behavior on relationship
From reading through topics and the docs, I've gathered that in order to use a relationship on a form field, I need to use a form layout component and set the relationship. I have a form in a page component that I'm not populating until the user clicks a button. I'm able to populate the form with what I've got right now, but when it comes time to save, $this->form->getState() returns one field but not the one with the relationship. mount method:
$this->form->fill([
'accountNote' => ['notes' => ''],
'preparer_comments' => ''
]);
$this->form->fill([
'accountNote' => ['notes' => ''],
'preparer_comments' => ''
]);
method to populate form:
public function showEditModal($id)
{

// Fetch the row data based on ID
$this->editItem = BalanceSheet::find($id);
// dd($this->editItem);
$this->form->fill([
'preparer_comments' => $this->editItem->preparer_comments,
'accountNote' => $this->editItem->accountNote //can't seem to set just the field on this, have to set the entire relationship.
]);

$this->dispatch('open-modal', id: 'editRowModal');
}
public function showEditModal($id)
{

// Fetch the row data based on ID
$this->editItem = BalanceSheet::find($id);
// dd($this->editItem);
$this->form->fill([
'preparer_comments' => $this->editItem->preparer_comments,
'accountNote' => $this->editItem->accountNote //can't seem to set just the field on this, have to set the entire relationship.
]);

$this->dispatch('open-modal', id: 'editRowModal');
}
the form:
public function form(Form $form): Form
{
return $form->schema([
Grid::make()
->columns(1)
->relationship('accountNote')
->schema([
Textarea::make('notes')
->label('Account Notes')
->rows(6),
]),
Textarea::make('preparer_comments')
->label('Preparer Comments')
->rows(6),

])->statePath('data')
->model(BalanceSheet::class);
}
public function form(Form $form): Form
{
return $form->schema([
Grid::make()
->columns(1)
->relationship('accountNote')
->schema([
Textarea::make('notes')
->label('Account Notes')
->rows(6),
]),
Textarea::make('preparer_comments')
->label('Preparer Comments')
->rows(6),

])->statePath('data')
->model(BalanceSheet::class);
}
5 replies
FFilament
Created by Jon Mason on 8/20/2024 in #❓┊help
Bug in v3.2.103 - unsure if apex plugin or filament
No description
8 replies
FFilament
Created by Jon Mason on 8/19/2024 in #❓┊help
dynamic form schema timing issue
I've got a form that should only be loaded when a user clicks on an item in a list (not on page load). To handle this I have wire:click in my blade file, which calls my loadRecent method. This method loads the necessary data that drives which fields will be on my form. The form data is unknown until the user clicks an item, so having a form with a standard set of hidden fields isn't an option. My issue is that $form->schema($this->getSchema()) seems to be getting called early in the lifecycle before when my data is populated as is evidenced by the logs referenced in the code below.
[2024-08-19 09:50:21] local.DEBUG: getSchema 09:50:21 [[]]
[2024-08-19 09:50:22] local.DEBUG: loadRecent: 09:50:22 [[{"id":773,"
[2024-08-19 09:50:21] local.DEBUG: getSchema 09:50:21 [[]]
[2024-08-19 09:50:22] local.DEBUG: loadRecent: 09:50:22 [[{"id":773,"
//called from blade view with wire:click
public function loadRecent($id)
{
$this->salesRec = SalesReconciliation::find($id);
$items = $this->salesRec->items;

//do other stuff.

$this->items = $items->toArray();

$this->form->fill();
//not empty. seems to run AFTER $form->schema
Log::debug('loadRecent', [$this->items]);
}

public function form(Form $form): Form
{
return $form->schema($this->getSchema())
->statePath('data');
//->model(SalesReconciliationItem::class);
}

public function getSchema(): array
{
$array = [];

//empty array in logs.
Log::debug('getSchema', [$this->items]);

foreach ($this->items as $item) {
$array[] = MathInput::make($item['account_ref_id'])
->label($item['account_name'])
->live()
->afterStateUpdated(function ($state, $old) use ($item) {
//do stuff
});
}

return $array;
}
//called from blade view with wire:click
public function loadRecent($id)
{
$this->salesRec = SalesReconciliation::find($id);
$items = $this->salesRec->items;

//do other stuff.

$this->items = $items->toArray();

$this->form->fill();
//not empty. seems to run AFTER $form->schema
Log::debug('loadRecent', [$this->items]);
}

public function form(Form $form): Form
{
return $form->schema($this->getSchema())
->statePath('data');
//->model(SalesReconciliationItem::class);
}

public function getSchema(): array
{
$array = [];

//empty array in logs.
Log::debug('getSchema', [$this->items]);

foreach ($this->items as $item) {
$array[] = MathInput::make($item['account_ref_id'])
->label($item['account_name'])
->live()
->afterStateUpdated(function ($state, $old) use ($item) {
//do stuff
});
}

return $array;
}
4 replies
FFilament
Created by Jon Mason on 8/18/2024 in #❓┊help
form not rendering until 2nd click
I have a form that's not rendered until the user selects an item from a list, be design. Once an item is selected the form should load and be available when the user clicks another button within the item. The form isn't loaded on the initial button click, it's just a blank modal with only the heading and the close button, no form. If I close out of the modal, then click on another item in the $recentRecs list, the form loads correctly. In the list:
@foreach ($recentRecs as $rec)
<div class="" wire:key="recent-rec-{{ $rec['id'] }}">
<x-filament::section class="cursor-pointer" wire:click="loadRecent({{ $rec['id'] }})">...
@foreach ($recentRecs as $rec)
<div class="" wire:key="recent-rec-{{ $rec['id'] }}">
<x-filament::section class="cursor-pointer" wire:click="loadRecent({{ $rec['id'] }})">...
public function mount(int $locationId): void
{
$this->form->fill();
$this->createNewForm->fill();
}

public function loadRecent($id)
{
$this->selectedRecId = $id;
$this->salesRec = SalesReconciliation::with('items')->find($id);
$this->items = $this->getReportData();
$this->setVars();
}

public function setVars()
{
$itemCollection = collect($this->items);
$this->data = $itemCollection->mapWithKeys(function ($item) {
return [$item['account_ref_id'] => $this->toCurrency($item['sms_amount'])];
})->toArray();
//can dd $this->data here and I get the expected array.
$this->form->fill($this->data);

...
}

public function getForms(): array
{
return [
'createNewForm',
'form'
];
}
public function mount(int $locationId): void
{
$this->form->fill();
$this->createNewForm->fill();
}

public function loadRecent($id)
{
$this->selectedRecId = $id;
$this->salesRec = SalesReconciliation::with('items')->find($id);
$this->items = $this->getReportData();
$this->setVars();
}

public function setVars()
{
$itemCollection = collect($this->items);
$this->data = $itemCollection->mapWithKeys(function ($item) {
return [$item['account_ref_id'] => $this->toCurrency($item['sms_amount'])];
})->toArray();
//can dd $this->data here and I get the expected array.
$this->form->fill($this->data);

...
}

public function getForms(): array
{
return [
'createNewForm',
'form'
];
}
<x-slot name="headerEnd">

<x-filament::modal slide-over id="sales" width="xl">

{{ $this->form }}

<x-slot name="footer">
<button type="button" wire:click="$dispatch('close-modal', { id: 'sales'} )">Close</button>
</x-slot>
</x-filament::modal>
</x-slot>
<x-slot name="headerEnd">

<x-filament::modal slide-over id="sales" width="xl">

{{ $this->form }}

<x-slot name="footer">
<button type="button" wire:click="$dispatch('close-modal', { id: 'sales'} )">Close</button>
</x-slot>
</x-filament::modal>
</x-slot>
4 replies
FFilament
Created by Jon Mason on 8/15/2024 in #❓┊help
Good course on testing?
Are there any good in-depth courses/deep dives on testing within the Filament ecosystem (or even just filament overall). The only course on Filament that I've really seen (aside from just random youtube videos) is on Laracasts, and it's good, but looking for more resources.
5 replies
FFilament
Created by Jon Mason on 8/11/2024 in #❓┊help
Render Hook for inserting livewire custom page into user menu?
I have several panels, and some pages are shared between panels, so I have the panel directories, and then I have a shared directory where all the pages/components live that are used in multiple panels. I want to add a link to a user settings page, and since it will be shared, I want to put it in this shared directory. However, when I create a custom page class like I have with a lot of other use cases, and I use the userMenuItems method on the panel provider, I get a route not defined error and I can't seem to get past it.
->userMenuItems([
MenuItem::make()
->url(fn(): string => Settings::getUrl(['userId' => auth()->id()])),
]);
->userMenuItems([
MenuItem::make()
->url(fn(): string => Settings::getUrl(['userId' => auth()->id()])),
]);
my custom page class:
class Settings extends Page
{

protected static string $view = 'filament.shared.pages.users.settings';
protected static ?string $slug = 'users/{userId}/settings';
protected static bool $shouldRegisterNavigation = false;

public function mount(int $userId)
{
...
class Settings extends Page
{

protected static string $view = 'filament.shared.pages.users.settings';
protected static ?string $slug = 'users/{userId}/settings';
protected static bool $shouldRegisterNavigation = false;

public function mount(int $userId)
{
...
2 questions: first, am I doing something wrong that would allow me to just make this work the way it's laid out above? I don't know why I can't just link to whatever page I want to, whether it's in the panel or not (I'm guessing that's the problem). With the other pages I have that are shared, I have a custom sidebar navigation where I'm creating the NavigationItems manually. This is the first time I've tried to put one of these in the user menu. second, if the above isn't going to work, is there a way to maybe use a renderhook to put a link in my user menu.
I can do this, I think, but just thinking there must be a better way.
FilamentView::registerRenderHook(
PanelsRenderHook::USER_MENU_PROFILE_BEFORE,
fn(): ?String => Blade::render('<div class="fi-dropdown-list p-1">
<a href="http://localhost/users/1/settings"...</a></div>'),
);
FilamentView::registerRenderHook(
PanelsRenderHook::USER_MENU_PROFILE_BEFORE,
fn(): ?String => Blade::render('<div class="fi-dropdown-list p-1">
<a href="http://localhost/users/1/settings"...</a></div>'),
);
6 replies