components ( [ Fieldset ::make ( 'Trip Type / Insurance Type / Plan Type' ) -> columnSpanFull () -> columns ( 5 ) -> schema ( [ Hidden ::make ( 'user_id' ) -> default ( fn () => Auth ::id () ) -> required (), Select ::make ( 'trip_type_id' ) -> label ( 'Trip Type' ) -> required () -> relationship ( 'tripType', 'name' ) -> live () -> preload () -> searchable () -> native ( false ) -> required () -> afterStateUpdated ( function ( Set $set ) { $set( 'annual_days', null ); $set( 'country_id', null ); $set( 'insurance_type_id', null ); $set( 'date_from', null ); $set( 'date_to', null ); } ), Select ::make ( 'annual_days' ) -> label ( 'Days Count For Single Trip' ) -> live () -> native ( false ) -> preload () -> searchable () -> required () -> options ( [ '30' => '30 Days', '45' => '45 Days', '60' => '60 Days', '90' => '90 Days', ] ) -> visible ( fn ( $get ) => $get( 'trip_type_id' ) == 2 ) -> afterStateUpdated ( function ( Set $set ) { $set( 'country_id', null ); $set( 'insurance_type_id', 1 ); $set( 'date_from', null ); $set( 'date_to', null ); } ), Select ::make ( 'country_id' ) -> label ( 'Country' ) -> options ( Country ::orderBy ( 'name' ) -> pluck ( 'name', 'id' ) -> toArray () ) -> searchable () -> multiple () -> native ( false ) -> optionsLimit ( 5 ) -> preload () -> live ( onBlur : true ) -> required () -> afterStateUpdated ( function ( $state, callable $set ) { self ::handleCountrySelection ( $state, $set ); $set( 'date_from', null ); $set( 'date_to', null ); } ), Select ::make ( 'insurance_type_id' ) -> label ( 'Insurance Type' ) -> disabled () -> live ( onBlur : true ) -> relationship ( 'insuranceType', 'name' ) -> required () -> afterStateUpdated ( function ( Set $set ) { $set( 'date_from', null ); $set( 'date_to', null ); } ), ] ), Grid ::make ( [ 'default' => 1 ] ) -> columns ( 4 ) -> columnSpanFull () -> schema ( [ DatePicker ::make ( 'date_from' ) -> label ( 'Date From' ) -> required () -> reactive () -> live ( onBlur : true ) -> native ( false ) // JS calendar, not browser -> minDate ( today () ) -> displayFormat ( 'd-M-Y' ) -> closeOnDateSelection () -> afterStateUpdated ( function ( Set $set, Get $get ) { $set( 'date_to', null ); $set( 'passenger_count', null ); if ( $get( 'trip_type_id' ) == 2 && $get( 'date_from' ) ) { $dateTo = Carbon ::parse ( $get( 'date_from' ) ) -> addDays ( 364 ); $set( 'date_to', $dateTo -> toDateString () ); $set( 'duration', 365 ); } } ), DatePicker ::make ( 'date_to' ) -> label ( 'Date To' ) -> live ( onBlur : true ) -> native ( false ) // JS calendar, not browser -> required () -> closeOnDateSelection () -> displayFormat ( 'd-M-Y' ) -> readonly ( fn ( Get $get ) => $get( 'trip_type_id' ) == 2 ) -> minDate ( fn ( Get $get ) => $get( 'date_from' ) ? Carbon ::parse ( $get( 'date_from' ) ) : null ) -> maxDate ( function ( Get $get ) { $from = $get( 'date_from' ); if ( ! $from ) return null; return Carbon ::parse ( $from ) -> addDays ( 364 ); // Or a different limit if needed } ) -> afterStateUpdated ( function ( ?string $state, Set $set, Get $get ) { $set( 'duration', Carbon ::parse ( $get( 'date_from' ) ) -> diffInDays ( Carbon ::parse ( $state ) ) + 1 ); $set( 'passenger_count', null ); } ), TextInput ::make ( 'duration' ) -> label ( 'Duration' ) -> hidden ( fn ( Get $get ) => $get( 'trip_type_id' ) == 2 ) -> disabled () -> reactive () -> required (), Select ::make ( 'passenger_count' ) -> label ( 'Number of Passengers' ) -> native ( false ) -> preload () -> searchable () -> options ( collect ( range ( 1, 10 ) ) -> mapWithKeys ( fn ( $number ) => [ $number => $number . ' ' . ( $number === 1 ? 'Passenger' : 'Passengers' ), ] ) -> toArray () ) -> default ( 0 ) -> live ( true ) -> afterStateUpdated ( function ( Set $set, $state ) { $passengerList = array_fill ( 0, max ( (int) $state, 1 ), [ 'date_of_birth' => null, 'age' => null, 'premium' => null, ] ); $set( 'passengers', $passengerList ); } ) -> required (), ] ), Grid ::make ( [ 'default' => 1 ] ) -> columnSpanFull () -> hidden ( fn ( Get $get ) => ! $get( 'passenger_count' ) ) -> schema ( [ Repeater ::make ( 'passengers' ) -> live () -> reactive () -> deletable ( false ) -> reorderable ( false ) -> grid ( 2 ) -> columns ( 3 ) -> addable ( fn ( Get $get, ?array $state ) => count ( $state ?? [] ) < (int) $get( 'passenger_count' ) ) -> defaultItems ( fn ( Get $get ) => min ( (int) $get( 'passenger_count' ) ? : 1, 4 ) ) -> schema ( [ DatePicker ::make ( 'date_of_birth' ) -> label ( 'Date of Birth' ) -> required () -> reactive () -> native ( false ) -> live ( onBlur : true ) -> displayFormat ( 'd-M-Y' ) -> maxDate ( fn ( Get $get ) => $get( '../../trip_type_id' ) == 2 ? today () -> subYears ( 18 ) : today () ) ->afterStateUpdated(function (?string $state, Set $set, Get $get) { $age = self::calculatePreciseAge($state, $get('../../date_from')); $index = $get('__index'); $set('age', $age); $set("../../passengers.{$index}.matching_plans_per_passenger", self::getMatchingPlans([ 'trip_type_id' => $get('../../trip_type_id'), 'insurance_type_id' => $get('../../insurance_type_id'), 'duration' => $get('../../duration'), 'age_min' => $age, 'age_max' => $age, ])); }), TextInput ::make ( 'age' ) -> label ( 'Age' ) -> reactive () -> disabled () -> dehydrated () -> required (), Repeater ::make ( 'matching_plans_per_passenger' ) -> label ( 'Matching Plans' ) -> columnSpanFull () -> disabled () -> schema ( [ Grid ::make ( [ 'default' => 3 ] ) -> schema ( [ TextInput ::make ( 'insurance_company_name' ) -> label ( 'Company' ) -> disabled (), TextInput ::make ( 'insurance_plan_name' ) -> label ( 'Plan' ) -> disabled (), TextInput ::make ( 'premium' ) -> label ( 'Premium' ) -> disabled (), ] ), ] ), ] ), ] ), ] ); } private static function handleCountrySelection ( array $selectedCountryIds, callable $set ) : void { $selectedIds = collect ( $selectedCountryIds ) -> toArray (); // Disable check $disabledCountries = Country ::whereIn ( 'id', $selectedIds ) -> where ( 'status', 'disable' ) -> get (); if ( $disabledCountries -> isNotEmpty () ) { $disabledIds = $disabledCountries -> pluck ( 'id' ) -> toArray (); $selectedIds = collect ( $selectedIds ) -> diff ( $disabledIds ) -> values () -> toArray (); $set( 'country_id', $selectedIds ); Notification ::make () -> title ( '🌍 Country Restrictions Applied' ) -> body ( "These countries are restricted:\n• " . $disabledCountries -> pluck ( 'name' ) -> join ( "\n• " ) ) -> icon ( 'heroicon-o-shield-exclamation' ) -> iconColor ( 'warning' ) -> danger () -> duration ( 10000 ) -> send (); } // Auto insurance type based on countries $set( 'insurance_type_id', collect ( $selectedIds ) -> intersect ( Country ::whereIn ( 'name', [ 'USA', 'Canada' ] ) -> pluck ( 'id' ) -> toArray () ) -> isNotEmpty () ? 1 : 2 ); } public static function calculatePreciseAge ( string $dob, string $onDate ) : int { $birth = Carbon ::parse ( $dob ); $on = Carbon ::parse ( $onDate ); $age = $on -> year - $birth -> year; // Adjust age if the birth month/day hasn't occurred yet in the 'onDate' year if ( $on -> month < $birth -> month || ( $on -> month === $birth -> month && $on -> day < $birth -> day ) ) { $age --; } return $age; } public static function getMatchingPlans ( array $data ) : Collection | array { $rates = InsurancePlanRate ::query () -> where ( 'trip_type_id', $data[ 'trip_type_id' ] ) -> where ( 'insurance_type_id', $data[ 'insurance_type_id' ] ) -> where ( 'duration', '>=', $data[ 'duration' ] ) -> where ( 'age_min', '<=', $data[ 'age_min' ] ) -> where ( 'age_max', '>=', $data[ 'age_max' ] ) -> with ( [ 'insuranceCompany', 'insurancePlan' ] ) -> get (); return $rates -> map ( fn ( $rate ) => [ 'insurance_company_id' => $rate -> insurance_company_id, 'insurance_company_name' => optional ( $rate -> insuranceCompany ) -> name, 'insurance_plan_id' => $rate -> insurance_plan_id, 'insurance_plan_name' => optional ( $rate -> insurancePlan ) -> name, 'premium' => $rate -> premium, ] ); } }