Creating a Custom Resume Page in Filament Php: A Step-by-Step Guide
    Learn How to Build a Tailored Resume Page in Your Filament Admin Panel Using Laravel
    June 26, 2024

    Filament is a robust framework for building beautiful admin panels in Laravel. Customizing Filament allows you to tailor its components to your specific needs, such as creating a custom page. In this blog, we'll walk through how to create a custom "Resume" page in Filament using a practical example.

    For a visual guide, check out my YouTube tutorial

    We'll cover:

    • Setting up the custom page class.
    • Defining the form schema.
    • Handling form actions.

    Let's dive in!


    Prerequisites

    Ensure you have the following before starting:

    • Filament installed and set up in your Laravel project.
    • Knowledge of Laravel and Filament basics.
    • Enum for Page Types (optional but recommended).

    Subscribe to my Newsletter

    Get the latest updates right to your inbox. Subscribe now!

    We respect your privacy. Unsubscribe at any time.

    Example Code Overview

    Here's a complete example of the Resume page we'll create. We'll dissect it step by step to understand each part:

    <?php
    
    namespace App\Filament\Pages;
    
    use App\Enums\PageType;
    use App\Models\User;
    use Filament\Facades\Filament;
    use Filament\Forms\Components\Actions\Action;
    use Filament\Forms\Components\DatePicker;
    use Filament\Forms\Components\Repeater;
    use Filament\Forms\Components\RichEditor;
    use Filament\Forms\Components\TextInput;
    use Filament\Forms\Concerns\InteractsWithForms;
    use Filament\Forms\Contracts\HasForms;
    use Filament\Forms\Form;
    use Filament\Notifications\Notification;
    use Filament\Pages\Page;
    use Filament\Support\Exceptions\Halt;
    use Illuminate\Support\Str;
    
    class Resume extends Page implements HasForms
    {
        use InteractsWithForms;
    
        public ?array $data = [];
    
        protected static ?string $navigationIcon = 'heroicon-o-briefcase';
    
        protected static ?string $title = 'Resume Page';
    
        protected static ?string $navigationGroup = 'CMS';
    
        protected static ?int $navigationSort = 2;
    
        protected static string $view = 'filament.pages.custom';
    
        public User $user;
    
        public function mount(): void
        {
            $this->user = Filament::getCurrentPanel()
                ->auth()
                ->user();
    
            if ($record = $this->user->pages->where('type', PageType::RESUME)->first()) {
                $this->form->fill($record->data);
            }
        }
    
        public function form(Form $form): Form
        {
            return $form
                ->schema([
                    Repeater::make('education')
                        ->schema([
                            DatePicker::make('date_of_starting')
                                ->required()
                                ->native(false),
                            DatePicker::make('date_of_ending')
                                ->native(false),
                            TextInput::make('course')
                                ->required(),
                            TextInput::make('institute')
                                ->required(),
                        ])
                        ->itemLabel(fn (array $state): ?string => (Str::title($state['course']) ?? null).' ('.(Str::title($state['institute']) ?? null).')')
                        ->collapsible()
                        ->collapsed()
                        ->columns(2)
                        ->required(),
    
                    Repeater::make('experience')
                        ->schema([
                            DatePicker::make('date_of_joining')
                                ->required()
                                ->native(false),
                            DatePicker::make('date_of_leaving')
                                ->native(false),
                            TextInput::make('position')
                                ->required(),
                            TextInput::make('organization')
                                ->required(),
                            RichEditor::make('role')
                                ->toolbarButtons([
                                    'bold',
                                    'bulletList',
                                    'h2',
                                    'h3',
                                    'italic',
                                    'orderedList',
                                    'redo',
                                    'undo',
                                ])->columnSpanFull(),
                        ])
                        ->itemLabel(fn (array $state): ?string => (Str::title($state['position']) ?? null).' ('.(Str::title($state['organization']) ?? null).')')
                        ->collapsible()
                        ->collapsed()
                        ->columns(2)
                        ->required(),
    
                    Repeater::make('skills')
                        ->schema([
                            TextInput::make('name')
                                ->required(),
                            TextInput::make('percentage')
                                ->suffix('%')
                                ->numeric()
                                ->step(1)
                                ->minValue(0)
                                ->maxValue(100)
                                ->required(),
                        ])
                        ->itemLabel(fn (array $state): ?string => Str::title($state['name']) ?? null)
                        ->collapsible()
                        ->collapsed()
                        ->columns(2)
                        ->required(),
    
                    Repeater::make('knowledge')
                        ->schema([
                            TextInput::make('name')
                                ->hiddenLabel()
                                ->required(),
                        ])
                        ->itemLabel(fn (array $state): ?string => Str::title($state['name']) ?? null)
                        ->collapsible()
                        ->collapsed()
                        ->columns(1)
                        ->grid(4)
                        ->required(),
                ])
                ->statePath('data');
        }
    
        protected function getFormActions(): array
        {
            return [
                Action::make('save')
                    ->label(__('filament-panels::resources/pages/edit-record.form.actions.save.label'))
                    ->submit('save'),
            ];
        }
    
        public function save(): void
        {
            try {
                $data = $this->form->getState();
    
                $this->user->pages()->updateOrCreate(
                    ['type' => PageType::RESUME],
                    ['data' => $data]
                );
            } catch (Halt $exception) {
                return;
            }
    
            Notification::make()
                ->success()
                ->title(__('filament-panels::resources/pages/edit-record.notifications.saved.title'))
                ->send();
        }
    }
    

    Step-by-Step Breakdown

    1. Setting Up the Custom Page Class

    Create a new class in the App\Filament\Pages namespace. This class should extend Filament\Pages\Page and implement Filament\Forms\Contracts\HasForms.

    namespace App\Filament\Pages;
    
    use App\Models\User;
    use Filament\Pages\Page;
    use Filament\Forms\Contracts\HasForms;
    use Filament\Forms\Concerns\InteractsWithForms;
    
    class Resume extends Page implements HasForms
    {
        use InteractsWithForms;
    
        public User $user;
    
        public function mount(): void
        {
            $this->user = Filament::getCurrentPanel()->auth()->user();
        }
    }
    

    Explanation:

    • Namespace: Ensure it's under the correct namespace.
    • Inheritance: Extends Page and implements HasForms to integrate form functionalities.
    • Mount Method: Initializes the user using Filament's authentication.

    2. Defining the Form Schema

    Define the form's structure in the form method. Use Filament's form components to build the form schema.

    public function form(Form $form): Form
    {
        return $form->schema([
            Repeater::make('education')
                ->schema([
                    DatePicker::make('date_of_starting')->required()->native(false),
                    DatePicker::make('date_of_ending')->native(false),
                    TextInput::make('course')->required(),
                    TextInput::make('institute')->required(),
                ])
                ->itemLabel(fn (array $state): ?string => (Str::title($state['course']) ?? null).' ('.(Str::title($state['institute']) ?? null).')')
                ->collapsible()
                ->collapsed()
                ->columns(2)
                ->required(),
    
            // Similar Repeater fields for 'experience', 'skills', 'knowledge'
        ])->statePath('data');
    }
    

    Explanation:

    • Repeater: Creates repeating fields for entries like education, experience, etc.
    • DatePicker and TextInput: Define various input fields.
    • itemLabel: Generates labels for each repeater item.

    3. Handling Form Actions

    Define form actions for handling form submissions. In this example, we have a "Save" action.

    protected function getFormActions(): array
    {
        return [
            Action::make('save')
                ->label('Save')
                ->submit('save'),
        ];
    }
    

    4. Saving the Form Data

    Implement the save method to handle data saving. This method updates or creates the user’s resume data.

    public function save(): void
    {
        try {
            $data = $this->form->getState();
    
            $this->user->pages()->updateOrCreate(
                ['type' => PageType::RESUME],
                ['data' => $data]
            );
        } catch (Halt $exception) {
            return;
        }
    
        Notification::make()
            ->success()
            ->title('Saved Successfully')
            ->send();
    }
    

    Explanation:

    • Form State: Retrieves the form's current state.
    • Update or Create: Updates existing resume data or creates a new entry.
    • Notification: Sends a success notification upon saving.

    5. Configuring Navigation

    Configure the page's navigation properties such as icon, title, and group.

    protected static ?string $navigationIcon = 'heroicon-o-briefcase';
    protected static ?string $title = 'Resume Page';
    protected static ?string $navigationGroup = 'CMS';
    protected static ?int $navigationSort = 2;
    protected static string $view = 'filament.pages.custom';
    

    Explanation:

    • Icon: Sets the navigation icon.
    • Title: Sets the page title.
    • Group: Groups the page under CMS in the navigation.
    • Sort:

    Defines the sort order.

    • View: Specifies the Blade view for rendering.

    Conclusion

    Creating a custom page in Filament PHP involves defining a new page class, building a form schema, and handling form actions. This example provides a solid foundation for building more complex custom pages in your Filament admin panel.

    Feel free to experiment with different form components and actions to tailor your pages to your specific needs. Happy coding!

    Refrence : Custom pages - Panel Builder - Filament

    If you have any questions or need further assistance, leave a comment below or reach out through my YouTube channel.

    Share with the post url and description