Jon Mason
Jon Mason
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
FFilament
Created by Jon Mason on 8/11/2024 in #❓┊help
UserMenu order of items
No description
6 replies
FFilament
Created by Jon Mason on 8/9/2024 in #❓┊help
FileUpload in form resets form fields on upload
I have a form that contains a FileUpload field. It works fine, except when a user fills out the form, and then subsequently uploads a file, it resets the fields on the form to their defaults. So you have to remember to upload the file first, then set your form fields, then click save.
Radio::make('my_radio_field_that_gets_reset')
->required()
->default(1)
->boolean(),
FileUpload::make('document')
->label('Upload Document')
->preserveFilenames()
->acceptedFileTypes(['application/pdf'])
->storeFiles(false)
Radio::make('my_radio_field_that_gets_reset')
->required()
->default(1)
->boolean(),
FileUpload::make('document')
->label('Upload Document')
->preserveFilenames()
->acceptedFileTypes(['application/pdf'])
->storeFiles(false)
The file upload seems to be treated as its own form within a form or something, and I don't know how to tell it to just maybe do it all at once....is there a good work-around? Here's my save method:
public function save(): void
{
$data = $this->form->getState();

if (isset($data['document']) && $data['document'] instanceof TemporaryUploadedFile) {
try {

//save the file.
} catch (\InvalidArgumentException $e) {

//...
}
}

}
public function save(): void
{
$data = $this->form->getState();

if (isset($data['document']) && $data['document'] instanceof TemporaryUploadedFile) {
try {

//save the file.
} catch (\InvalidArgumentException $e) {

//...
}
}

}
3 replies
FFilament
Created by Jon Mason on 8/8/2024 in #❓┊help
Theoretical/Performance Question
Let's say I have a livewire component that's a list of items. The list has a foreach loop that iterates through livewire component items in the list. So something like this:
@foreach($list as $item)
<livewire:my-component ...
@endforeach
@foreach($list as $item)
<livewire:my-component ...
@endforeach
In the above scenario, which is generally going to be the most performant? 1) Pass in the ID of the item and query the database within the item component to get any add'l fields. 2) Pass in the entire $item. This would mean you're passing a much larger object, but you're not querying the database for each item. 3) Query once in the outer list component and pass in an array of only the necessary data elements to each item? I was doing #2 and having performance issues, so I moved to #1, also performance issues. I've also tried passing in each individual field that's needed, which is generally better performance-wise, but if I need almost all the fields on the object, then I'm passing in a ton of fields. How do you guys generally handle these types of challenges? Hitting some walls performance-wise with Filament and I feel confident it's my own lack of skills and not the framework itself.
5 replies
FFilament
Created by Jon Mason on 7/26/2024 in #❓┊help
disabled button still clickable
I'm using a button blade component, but disabling it makes the appearance change but not the interactivity. The button looks to be disabled, but when you click on it, it still triggers the modal.
<x-filament::modal id="myModal" width="6xl">
<x-slot name="trigger">
@if ($sum != 0)
<x-filament::icon-button size="lg" icon="heroicon-o-check-circle" class="text-gray-600" :disabled="true" style="margin-left: -14px; margin-right: 0px;" />
@else
<x-filament::icon-button size="lg" icon="heroicon-o-check-circle" class="text-green-600 animate-bounce" :disabled="true" style="margin-left: -14px; margin-right: 0px;" />
@endif
</x-slot>
<x-filament::modal id="myModal" width="6xl">
<x-slot name="trigger">
@if ($sum != 0)
<x-filament::icon-button size="lg" icon="heroicon-o-check-circle" class="text-gray-600" :disabled="true" style="margin-left: -14px; margin-right: 0px;" />
@else
<x-filament::icon-button size="lg" icon="heroicon-o-check-circle" class="text-green-600 animate-bounce" :disabled="true" style="margin-left: -14px; margin-right: 0px;" />
@endif
</x-slot>
5 replies
FFilament
Created by Jon Mason on 7/24/2024 in #❓┊help
updating the state of custom field
I can' t get the state to update, I've tried this method, as well as just calling $this->state($updtedState); without the $component.
protected function setUp(): void
{
parent::setUp();

$this->live(onBlur: true)
->afterStateUpdated(function (Component $component, ?string $state, ?string $old) {
if ($state !== null) {
$updatedState = $this->performMathOperation($state);
Log::debug($updatedState); //this is the correct value, want to reflect in text input field.

$component->state($updatedState); //input not getting updated.
}
});
}
protected function setUp(): void
{
parent::setUp();

$this->live(onBlur: true)
->afterStateUpdated(function (Component $component, ?string $state, ?string $old) {
if ($state !== null) {
$updatedState = $this->performMathOperation($state);
Log::debug($updatedState); //this is the correct value, want to reflect in text input field.

$component->state($updatedState); //input not getting updated.
}
});
}
8 replies