77use App \Actions \BackAction ;
88use App \Concerns \PreventMultipleSubmit ;
99use App \Concerns \PreventSubmitFormOnEnter ;
10+ use App \Enums \AddressType ;
1011use App \Enums \Applicant ;
12+ use App \Enums \Occupation ;
1113use App \Enums \RecommendationService ;
1214use App \Filament \Organizations \Resources \Cases \CaseResource ;
15+ use App \Forms \Components \CountyCitySelect ;
1316use App \Forms \Components \DatePicker ;
1417use App \Forms \Components \Select ;
1518use App \Models \Beneficiary ;
1619use App \Models \BeneficiaryPartner ;
1720use Filament \Facades \Filament ;
21+ use Filament \Forms \Components \Checkbox ;
1822use Filament \Forms \Components \CheckboxList ;
1923use Filament \Forms \Components \Repeater ;
2024use Filament \Forms \Components \RichEditor ;
2125use Filament \Forms \Components \Textarea ;
2226use Filament \Forms \Components \TextInput ;
2327use Filament \Resources \Pages \CreateRecord \Concerns \HasWizard ;
2428use Filament \Resources \Pages \EditRecord ;
29+ use Filament \Schemas \Components \Grid ;
2530use Filament \Schemas \Components \Section ;
2631use Filament \Schemas \Components \Utilities \Get ;
32+ use Filament \Schemas \Components \Utilities \Set ;
2733use Filament \Schemas \Components \Wizard \Step ;
2834use Illuminate \Contracts \Support \Htmlable ;
2935
@@ -35,6 +41,13 @@ class CreateCaseDetailedEvaluation extends EditRecord
3541
3642 protected static string $ resource = CaseResource::class;
3743
44+ /**
45+ * Partner address data captured before save (form state is cleared of these after save).
46+ *
47+ * @var array{legal_residence?: array, effective_residence?: array, same_as_legal_residence?: bool}|null
48+ */
49+ protected ?array $ pendingPartnerAddressData = null ;
50+
3851 public function getTitle (): string |Htmlable
3952 {
4053 return __ ('beneficiary.page.create_detailed_evaluation.title ' );
@@ -85,13 +98,166 @@ public function getSteps(): array
8598 ];
8699 }
87100
101+ /**
102+ * @param array<string, mixed> $data
103+ * @return array<string, mixed>
104+ */
105+ protected function mutateFormDataBeforeFill (array $ data ): array
106+ {
107+ $ record = $ this ->getRecord ();
108+ if (! $ record instanceof Beneficiary) {
109+ return $ data ;
110+ }
111+
112+ $ partner = $ record ->partner ;
113+ if (! $ partner ) {
114+ return $ data ;
115+ }
116+
117+ $ partner ->loadMissing (['legal_residence ' , 'effective_residence ' ]);
118+ $ partnerData = $ data ['partner ' ] ?? [];
119+
120+ if ($ partner ->legal_residence ) {
121+ $ partnerData ['legal_residence ' ] = array_merge (
122+ $ partnerData ['legal_residence ' ] ?? [],
123+ $ partner ->legal_residence ->only (['county_id ' , 'city_id ' , 'address ' ])
124+ );
125+ }
126+ if ($ partner ->effective_residence ) {
127+ $ partnerData ['effective_residence ' ] = array_merge (
128+ $ partnerData ['effective_residence ' ] ?? [],
129+ $ partner ->effective_residence ->only (['county_id ' , 'city_id ' , 'address ' ])
130+ );
131+ }
132+
133+ $ data ['partner ' ] = $ partnerData ;
134+
135+ return $ data ;
136+ }
137+
138+ /**
139+ * @param array<string, mixed> $data
140+ * @return array<string, mixed>
141+ */
142+ protected function mutateFormDataBeforeSave (array $ data ): array
143+ {
144+ $ partnerData = $ data ['partner ' ] ?? [];
145+
146+ $ rawState = method_exists ($ this ->form , 'getRawState ' ) ? $ this ->form ->getRawState () : (array ) $ this ->data ;
147+ $ partnerFromState = $ data ['partner ' ] ?? $ rawState ['partner ' ] ?? [];
148+ $ partnerFromLivewire = $ rawState ['partner ' ] ?? $ this ->data ['partner ' ] ?? [];
149+ $ legal = $ partnerFromState ['legal_residence ' ] ?? $ partnerFromLivewire ['legal_residence ' ] ?? [];
150+ $ effective = $ partnerFromState ['effective_residence ' ] ?? $ partnerFromLivewire ['effective_residence ' ] ?? [];
151+ $ this ->pendingPartnerAddressData = [
152+ 'legal_residence ' => is_array ($ legal ) ? $ legal : [],
153+ 'effective_residence ' => is_array ($ effective ) ? $ effective : [],
154+ 'same_as_legal_residence ' => (bool ) ($ partnerFromState ['same_as_legal_residence ' ] ?? $ partnerFromLivewire ['same_as_legal_residence ' ] ?? false ),
155+ ];
156+
157+ unset($ partnerData ['legal_residence ' ], $ partnerData ['effective_residence ' ]);
158+ $ data ['partner ' ] = $ partnerData ;
159+
160+ return $ data ;
161+ }
162+
88163 public function afterSave (): void
89164 {
90- $ partnerRecord = $ this ->getRecord ()->partner ;
165+ $ record = $ this ->getRecord ();
166+ if (! $ record instanceof Beneficiary) {
167+ return ;
168+ }
169+
170+ $ partnerRecord = $ record ->partner ;
91171 if ($ partnerRecord && $ partnerRecord ->same_as_legal_residence ) {
92172 $ partnerRecord ->loadMissing (['legal_residence ' , 'effective_residence ' ]);
93173 BeneficiaryPartner::copyLegalResidenceToEffectiveResidence ($ partnerRecord );
94174 }
175+
176+ $ this ->savePartnerAddressesFromFormState ();
177+ }
178+
179+ private function savePartnerAddressesFromFormState (): void
180+ {
181+ $ record = $ this ->getRecord ();
182+ if (! $ record instanceof Beneficiary) {
183+ return ;
184+ }
185+
186+ $ partner = $ record ->partner ;
187+ if (! $ partner ) {
188+ return ;
189+ }
190+
191+ $ partnerState = $ this ->pendingPartnerAddressData ?? [];
192+ $ this ->pendingPartnerAddressData = null ;
193+
194+ $ legalData = $ partnerState ['legal_residence ' ] ?? [];
195+ $ effectiveData = $ partnerState ['effective_residence ' ] ?? [];
196+ $ sameAsLegal = (bool ) ($ partnerState ['same_as_legal_residence ' ] ?? false );
197+
198+ if ($ this ->hasPartnerAddressData ($ legalData )) {
199+ $ attrs = array_merge (
200+ $ this ->buildPartnerAddressAttributes ($ legalData , AddressType::LEGAL_RESIDENCE ),
201+ [
202+ 'address_type ' => AddressType::LEGAL_RESIDENCE ,
203+ 'addressable_id ' => $ partner ->getKey (),
204+ 'addressable_type ' => $ partner ->getMorphClass (),
205+ ]
206+ );
207+ $ partner ->legal_residence ()->updateOrCreate (
208+ ['addressable_id ' => $ partner ->getKey (), 'addressable_type ' => $ partner ->getMorphClass ()],
209+ $ attrs
210+ );
211+ } else {
212+ $ partner ->legal_residence ?->delete();
213+ }
214+
215+ $ effectivePayload = $ sameAsLegal ? $ legalData : $ effectiveData ;
216+ if ($ this ->hasPartnerAddressData ($ effectivePayload )) {
217+ $ attrs = array_merge (
218+ $ this ->buildPartnerAddressAttributes ($ effectivePayload , AddressType::EFFECTIVE_RESIDENCE ),
219+ [
220+ 'address_type ' => AddressType::EFFECTIVE_RESIDENCE ,
221+ 'addressable_id ' => $ partner ->getKey (),
222+ 'addressable_type ' => $ partner ->getMorphClass (),
223+ ]
224+ );
225+ $ partner ->effective_residence ()->updateOrCreate (
226+ ['addressable_id ' => $ partner ->getKey (), 'addressable_type ' => $ partner ->getMorphClass ()],
227+ $ attrs
228+ );
229+ } else {
230+ $ partner ->effective_residence ?->delete();
231+ }
232+ }
233+
234+ /**
235+ * @param array<string, mixed> $data
236+ */
237+ private function hasPartnerAddressData (array $ data ): bool
238+ {
239+ $ countyId = $ data ['county_id ' ] ?? null ;
240+ $ cityId = $ data ['city_id ' ] ?? null ;
241+ $ address = $ data ['address ' ] ?? null ;
242+
243+ return $ countyId !== null || $ cityId !== null || filled ($ address );
244+ }
245+
246+ /**
247+ * @param array<string, mixed> $data
248+ * @return array<string, mixed>
249+ */
250+ private function buildPartnerAddressAttributes (array $ data , AddressType $ type ): array
251+ {
252+ $ attrs = [
253+ 'country_id ' => $ data ['country_id ' ] ?? null ,
254+ 'county_id ' => $ data ['county_id ' ] ?? null ,
255+ 'city_id ' => $ data ['city_id ' ] ?? null ,
256+ 'address ' => $ data ['address ' ] ?? null ,
257+ 'environment ' => $ data ['environment ' ] ?? null ,
258+ ];
259+
260+ return array_filter ($ attrs , fn ($ v ) => $ v !== null && $ v !== '' );
95261 }
96262
97263 /**
@@ -106,7 +272,6 @@ protected function getDetailedEvaluationStepSchema(): array
106272 Repeater::make ('detailedEvaluationSpecialists ' )
107273 ->relationship ('detailedEvaluationSpecialists ' )
108274 ->label (__ ('beneficiary.section.detailed_evaluation.labels.specialists ' ))
109- ->minItems (3 )
110275 ->addActionLabel (__ ('beneficiary.action.add_row ' ))
111276 ->deletable ()
112277 ->columns (4 )
@@ -163,10 +328,96 @@ protected function getPartnerStepSchema(): array
163328 ->relationship ('partner ' )
164329 ->maxWidth ('3xl ' )
165330 ->schema ([
166- \Filament \Forms \Components \Textarea::make ('observations ' )
331+ Grid::make (2 )
332+ ->schema ([
333+ TextInput::make ('last_name ' )
334+ ->label (__ ('field.last_name ' ))
335+ ->placeholder (__ ('beneficiary.placeholder.partner_last_name ' ))
336+ ->maxLength (50 ),
337+ TextInput::make ('first_name ' )
338+ ->label (__ ('field.first_name ' ))
339+ ->placeholder (__ ('beneficiary.placeholder.partner_first_name ' ))
340+ ->maxLength (50 ),
341+ TextInput::make ('age ' )
342+ ->label (__ ('field.age ' ))
343+ ->placeholder (__ ('beneficiary.placeholder.partner_age ' ))
344+ ->numeric ()
345+ ->minValue (0 )
346+ ->maxValue (99 )
347+ ->validationAttribute (__ ('field.age ' )),
348+ Select::make ('occupation ' )
349+ ->label (__ ('field.occupation ' ))
350+ ->placeholder (__ ('beneficiary.placeholder.occupation ' ))
351+ ->options (Occupation::options ())
352+ ->enum (Occupation::class),
353+ ]),
354+ Grid::make ()
355+ ->schema ([
356+ ...CountyCitySelect::make ()
357+ ->countyField ('legal_residence.county_id ' )
358+ ->cityField ('legal_residence.city_id ' )
359+ ->countyLabel (__ ('field.legal_residence_county ' ))
360+ ->cityLabel (__ ('field.legal_residence_city ' ))
361+ ->countyPlaceholder (__ ('placeholder.county ' ))
362+ ->cityPlaceholder (__ ('placeholder.city ' ))
363+ ->required (false )
364+ ->countyAfterStateUpdated (function (Set $ set , Get $ get ): void {
365+ if ($ get ('same_as_legal_residence ' )) {
366+ $ set ('effective_residence.county_id ' , $ get ('legal_residence.county_id ' ));
367+ $ set ('effective_residence.city_id ' , null );
368+ }
369+ })
370+ ->cityAfterStateUpdated (function (Set $ set , Get $ get , $ state ): void {
371+ if ($ get ('same_as_legal_residence ' )) {
372+ $ set ('effective_residence.city_id ' , $ state );
373+ }
374+ })
375+ ->schema (),
376+ TextInput::make ('legal_residence.address ' )
377+ ->label (__ ('field.legal_residence_address ' ))
378+ ->placeholder (__ ('placeholder.address ' ))
379+ ->maxLength (50 ),
380+ ]),
381+ Checkbox::make ('same_as_legal_residence ' )
382+ ->label (__ ('field.same_as_legal_residence ' ))
383+ ->live ()
384+ ->afterStateUpdated (function (bool $ state , Set $ set , Get $ get ): void {
385+ if (! $ state ) {
386+ $ set ('effective_residence.county_id ' , null );
387+ $ set ('effective_residence.city_id ' , null );
388+ $ set ('effective_residence.address ' , null );
389+ }
390+ if ($ state ) {
391+ $ set ('effective_residence.county_id ' , $ get ('legal_residence.county_id ' ));
392+ $ set ('effective_residence.city_id ' , $ get ('legal_residence.city_id ' ));
393+ $ set ('effective_residence.address ' , $ get ('legal_residence.address ' ));
394+ }
395+ })
396+ ->columnSpanFull (),
397+ Grid::make ()
398+ ->schema ([
399+ ...CountyCitySelect::make ()
400+ ->countyField ('effective_residence.county_id ' )
401+ ->cityField ('effective_residence.city_id ' )
402+ ->countyLabel (__ ('field.effective_residence_county ' ))
403+ ->cityLabel (__ ('field.effective_residence_city ' ))
404+ ->countyPlaceholder (__ ('placeholder.county ' ))
405+ ->cityPlaceholder (__ ('placeholder.city ' ))
406+ ->required (false )
407+ ->countyDisabled (fn (Get $ get ): bool => (bool ) $ get ('same_as_legal_residence ' ))
408+ ->cityDisabled (fn (Get $ get ): bool => $ get ('same_as_legal_residence ' ) || ! $ get ('effective_residence.county_id ' ))
409+ ->schema (),
410+ TextInput::make ('effective_residence.address ' )
411+ ->label (__ ('field.effective_residence_address ' ))
412+ ->placeholder (__ ('placeholder.address ' ))
413+ ->maxLength (50 )
414+ ->disabled (fn (Get $ get ): bool => (bool ) $ get ('same_as_legal_residence ' )),
415+ ]),
416+ Textarea::make ('observations ' )
167417 ->label (__ ('beneficiary.section.detailed_evaluation.labels.observations ' ))
168418 ->placeholder (__ ('beneficiary.placeholder.partner_relevant_observations ' ))
169- ->maxLength (500 ),
419+ ->maxLength (500 )
420+ ->columnSpanFull (),
170421 ]),
171422 ];
172423 }
0 commit comments