Next, we want to build an info list (View) page for our Customers. This page has to display all the information we have on our Customer along with a list of Pipeline Status logs:
In this lesson, we will do the following:
- Create an InfoList View
- Update Table row click to point to the View page
- Create a custom component to display the Pipeline Status logs
Creating InfoList Page
Let's get to work and create a new file for our View:
php artisan make:filament-page ViewCustomer --resource=CustomerResource --type=ViewRecord
This should create the following file:
app/Filament/Resources/CustomerResource/Pages/ViewCustomer.php
namespace App\Filament\Resources\CustomerResource\Pages; use App\Filament\Resources\CustomerResource;use Filament\Resources\Pages\CreateRecord;use Filament\Resources\Pages\ViewRecord; class ViewCustomer extends ViewRecord{ protected static string $resource = CustomerResource::class;}
Once this is done, we can go ahead and create links to the View page:
app/Filament/Resources/CustomerResource.php
// ... public static function table(Table $table): Table{ return $table ->columns([ // ... ]) ->filters([ // ]) ->actions([ // ... ]) ->recordUrl(function ($record) { if ($record->trashed()) { return null; } return Pages\EditCustomer::getUrl([$record->id]); return Pages\ViewCustomer::getUrl([$record->id]); }) ->bulkActions([ Tables\Actions\BulkActionGroup::make([ Tables\Actions\DeleteBulkAction::make(), ]), ]);} public static function getPages(): array{ return [ 'index' => Pages\ListCustomers::route('/'), 'create' => Pages\CreateCustomer::route('/create'), 'edit' => Pages\EditCustomer::route('/{record}/edit'), 'view' => Pages\ViewCustomer::route('/{record}'), ];}
Now you should be able to open the table and click on a row to view the Customer's details:
All of these fields were auto-guessed by Filament and displayed as is from the Form definition. We can customize them by using the infoList()
method. Let's do that now.
Customizing the View Page
While the auto-guessed fields are great, we can customize them to our liking by simply defining the structure just like we do with Forms:
app/Filament/Resources/CustomerResource.php
use Filament\Infolists\Components\RepeatableEntry;use Filament\Infolists\Components\Section;use Filament\Infolists\Components\TextEntry;use Filament\Infolists\Components\ViewEntry;use Filament\Infolists\Infolist;use Filament\Support\Colors\Color; // ... public static function infoList(Infolist $infolist): Infolist{ return $infolist ->schema([ Section::make('Personal Information') ->schema([ TextEntry::make('first_name'), TextEntry::make('last_name'), ]) ->columns(), Section::make('Contact Information') ->schema([ TextEntry::make('email'), TextEntry::make('phone_number'), ]) ->columns(), Section::make('Additional Details') ->schema([ TextEntry::make('description'), ]), Section::make('Lead and Stage Information') ->schema([ TextEntry::make('leadSource.name'), TextEntry::make('pipelineStage.name'), ]) ->columns(), Section::make('Pipeline Stage History and Notes') ->schema([ ViewEntry::make('pipelineStageLogs') ->label('') ->view('infolists.components.pipeline-stage-history-list') ]) ->collapsible() ]);} // ...
If you try to load the page - you will see that we have an error:
This is due to us using ViewEntry
and not having a defined view. Let's create one now:
resources/views/infolists/components/pipeline-stage-history-list.blade.php
<x-dynamic-component :component="$getEntryWrapperView()" :entry="$entry" class="grid grid-cols-[--cols-default] fi-in-component-ctn gap-6"> @foreach($getState() as $pipelineLog) <div class="mb-4"> <div class=""> <span class="font-bold">{{ $pipelineLog->user?->name ?? 'System' }}</span>, <span x-data="{}" x-tooltip="{ content: '{{ $pipelineLog->created_at }}', theme: $store.theme, }">{{ $pipelineLog->created_at->diffForHumans() }}</span> </div> <div class=""> <span class="font-bold">Pipeline Stage:</span> {{ $pipelineLog->pipelineStage->name }} </div> @if($pipelineLog->notes) <div class=""> <span class="font-bold">Note:</span> {{ $pipelineLog->notes }} </div> @endif </div> @endforeach</x-dynamic-component>
Now, if we reload the page, we should see a nicer design for our View plus Pipeline Stage History:
That's it! We have our View page ready.
We will create a Document Resource for our Customers in the next lesson.
Hello there, i got this
foreach() argument must be of type array|object, null given
C:\laragon\www\crm\resources\views\infolists\components\pipeline-stage-history-list.blade.php: 2
Hi, this seems like a missed step somewhere. Could you add a bit more code on what you did or flare report share?
The error message says that there are no pipeline stages on your Customer, which is due to bad relationship name or a typo somewhere (could be missed code piece or anything else like that)
Having a problem if the customer has not been moved up from a lead and you click on that client in the list then you get the
foreach() argument must be of type array|object, null given Error
But if the customer has been moved up to say Contact Made you no longer get the error and the customer data is displayed
What can we do to fix this problem?
Also gitting a N+1 Error message but if you click on the "ok" button it puls up the page
I see that this problem is fixed in your github repository however.
The problem that you are describing is from misconfiguration. You have to create a customer with at least 1 pipeline stage and observer has to catch that it was there. This will create 1 entry and prevent this error.
What you might have done differently from this tutorial - skipped the fresh migration run after implementing the observer. Try to run
php artisan migrate:fresh --seed
and see if you can open customers view. If not, then simply add@if(count($getState()) > 0)
around the foreach.As for N+1 - could you expand where that happens? And does it still exist on the last lesson that we have?
hello for me It works with both modifications, the relationship in tables was wrong, which is solved with the refresh and correct functioning is ensured by testing if getState > 0