diff --git a/app/Filament/Shelter/Resources/BeneficiaryResource.php b/app/Filament/Shelter/Resources/BeneficiaryResource.php index a877f55..ececddf 100644 --- a/app/Filament/Shelter/Resources/BeneficiaryResource.php +++ b/app/Filament/Shelter/Resources/BeneficiaryResource.php @@ -71,9 +71,17 @@ public static function infolist(Infolist $infolist): Infolist ->columns(3) ->heading(__('app.beneficiary.steps.personal_details')) ->headerActions([ + Action::make('data') + ->label(__('app.form.actions.history')) + ->url(fn (Beneficiary $record) => static::getUrl('versions.index', ['record' => $record])) + ->visible(fn (Beneficiary $record) => $record->hasMoreThanOneForm()) + ->icon('heroicon-o-clock') + ->link() + ->outlined(), + Action::make('edit') ->label(__('filament-actions::edit.single.label')) - ->url(fn ($record) => static::getUrl('edit', ['record' => $record])) + ->url(fn (Beneficiary $record) => static::getUrl('edit', ['record' => $record])) ->icon('heroicon-o-pencil-square') ->color('gray') ->outlined(), @@ -171,8 +179,12 @@ public static function getPages(): array 'create' => Pages\CreateBeneficiary::route('/create'), 'view' => Pages\ViewBeneficiary::route('/{record}'), 'edit' => Pages\EditBeneficiary::route('/{record}/edit'), + 'stay' => Pages\ViewStay::route('/{record}/stay/{stay}'), 'document' => Pages\ViewDocument::route('/{record}/document/{document}'), + + 'versions.index' => Pages\ListPersonalDataVersions::route('/{record}/data'), + 'versions.view' => Pages\ViewPersonalDataVersion::route('/{record}/data/{response}'), ]; } } diff --git a/app/Filament/Shelter/Resources/BeneficiaryResource/Pages/EditBeneficiary.php b/app/Filament/Shelter/Resources/BeneficiaryResource/Pages/EditBeneficiary.php index 96a450e..aa29330 100644 --- a/app/Filament/Shelter/Resources/BeneficiaryResource/Pages/EditBeneficiary.php +++ b/app/Filament/Shelter/Resources/BeneficiaryResource/Pages/EditBeneficiary.php @@ -8,6 +8,7 @@ use App\Filament\Shelter\Resources\BeneficiaryResource; use App\Models\Form; use App\Models\Form\FieldResponse; +use App\Models\Form\Response; use Filament\Actions; use Filament\Resources\Pages\EditRecord; @@ -36,24 +37,23 @@ protected function mutateFormDataBeforeFill(array $data): array protected function mutateFormDataBeforeSave(array $data): array { + $form = Form::query() + ->latestPublished(Type::PERSONAL) + ->first(['id']); + + /** @var Response */ $response = $this->getRecord() ->latestPersonal() - ->firstOr(function () { - $form = Form::query() - ->latestPublished(Type::PERSONAL) - ->first(['id']); - - if (blank($form)) { - return; - } + ->where('form_id', $form->id) + ->firstOr( + fn () => $this->getRecord() + ->personal()->create([ + 'form_id' => $form->id, + ]) + ); - return $this->getRecord()->personal()->create([ - 'form_id' => $form->id, - ]); - }); - - $this->wrapInDatabaseTransaction( - fn () => collect(data_get($data, 'form'))->each( + $this->wrapInDatabaseTransaction(function () use ($data, $response) { + collect(data_get($data, 'form'))->each( fn ($value, int $field_id) => FieldResponse::updateOrCreate( [ 'response_id' => $response->id, @@ -63,8 +63,10 @@ protected function mutateFormDataBeforeSave(array $data): array 'value' => $value, ] ) - ) - ); + ); + + $response->touch(); + }); return $data; } diff --git a/app/Filament/Shelter/Resources/BeneficiaryResource/Pages/ListPersonalDataVersions.php b/app/Filament/Shelter/Resources/BeneficiaryResource/Pages/ListPersonalDataVersions.php new file mode 100644 index 0000000..8481ef0 --- /dev/null +++ b/app/Filament/Shelter/Resources/BeneficiaryResource/Pages/ListPersonalDataVersions.php @@ -0,0 +1,55 @@ +getRecord()->hasMoreThanOneForm(), 404); + } + + public function getTitle(): string + { + return __('app.form.actions.history'); + } + + protected function fillForm(): void + { + // + } + + public function infolist(Infolist $infolist): Infolist + { + return $infolist; + } + + protected function getForms(): array + { + return [ + // Disables form + 'form' => $this->makeForm(), + ]; + } + + protected function getHeaderWidgets(): array + { + return [ + BeneficiaryResource\Widgets\PersonalDataVersionsWidget::class, + ]; + } +} diff --git a/app/Filament/Shelter/Resources/BeneficiaryResource/Pages/ViewDocument.php b/app/Filament/Shelter/Resources/BeneficiaryResource/Pages/ViewDocument.php index 5b23155..e73497b 100644 --- a/app/Filament/Shelter/Resources/BeneficiaryResource/Pages/ViewDocument.php +++ b/app/Filament/Shelter/Resources/BeneficiaryResource/Pages/ViewDocument.php @@ -25,7 +25,6 @@ class ViewDocument extends ViewRecord protected function getHeaderActions(): array { return [ - Actions\DeleteAction::make() ->label(__('app.documents.actions.delete')) ->record($this->document) @@ -59,7 +58,6 @@ protected function getHeaderActions(): array return response()->download($mediaItem->getPath(), $mediaItem->file_name); }), - ]; } diff --git a/app/Filament/Shelter/Resources/BeneficiaryResource/Pages/ViewPersonalDataVersion.php b/app/Filament/Shelter/Resources/BeneficiaryResource/Pages/ViewPersonalDataVersion.php new file mode 100644 index 0000000..95aa147 --- /dev/null +++ b/app/Filament/Shelter/Resources/BeneficiaryResource/Pages/ViewPersonalDataVersion.php @@ -0,0 +1,53 @@ +response->id; + } + + public function infolist(Infolist $infolist): Infolist + { + return $infolist + ->record($this->response) + ->schema(fn (Response $record) => [ + Section::make() + ->columns(3) + ->schema([ + TextEntry::make('id') + ->label(__('app.field.id')) + ->prefix('#'), + + TextEntry::make('created_at') + ->label(__('app.field.created_at')) + ->dateTime(), + + TextEntry::make('updated_at') + ->label(__('app.field.updated_at')) + ->dateTime(), + ]), + + ...BeneficiaryDynamicInfolist::getSchemaForResponse($record), + ]); + } +} diff --git a/app/Filament/Shelter/Resources/BeneficiaryResource/Schemas/BeneficiaryDynamicInfolist.php b/app/Filament/Shelter/Resources/BeneficiaryResource/Schemas/BeneficiaryDynamicInfolist.php index 8d52345..bc32a03 100644 --- a/app/Filament/Shelter/Resources/BeneficiaryResource/Schemas/BeneficiaryDynamicInfolist.php +++ b/app/Filament/Shelter/Resources/BeneficiaryResource/Schemas/BeneficiaryDynamicInfolist.php @@ -25,47 +25,52 @@ public static function getSchema(): array return []; } - $state->loadMissing('form.sections.fields:id,label,type,section_id'); + return static::getSchemaForResponse($state); + }), + ]; + } - $fieldIndex = 0; + public static function getSchemaForResponse(Response $response): array + { + $fieldIndex = 0; - return $state->form - ->sections - ->map(function ($section) use (&$fieldIndex) { - return Section::make($section->name) - ->description($section->description) - ->columns(3) - ->collapsible() - ->schema( - $section->fields->map(function (Field $field) use (&$fieldIndex) { - $component = TextEntry::make("fields.{$fieldIndex}.value") - ->label($field->label); + $response->loadMissing('form.sections.fields'); - $fieldIndex++; + return $response->form + ->sections + ->map(function ($section) use (&$fieldIndex) { + return Section::make($section->name) + ->description($section->description) + ->columns(3) + ->collapsible() + ->schema( + $section->fields->map(function (Field $field) use (&$fieldIndex) { + $component = TextEntry::make("fields.{$fieldIndex}.value") + ->label($field->label); - switch ($field->type) { - case FieldType::CHECKBOX: - case FieldType::RADIO: - case FieldType::SELECT: - $component->listWithLineBreaks(); - break; + $fieldIndex++; - case FieldType::NUMBER: - $component->numeric(); - break; + switch ($field->type) { + case FieldType::CHECKBOX: + case FieldType::RADIO: + case FieldType::SELECT: + $component->listWithLineBreaks(); + break; - case FieldType::DATE: - $component->formatStateUsing(function ($state) { - return rescue(fn () => Carbon::parse($state)->toFormattedDate(), $state, false); - }); - break; - } + case FieldType::NUMBER: + $component->numeric(); + break; - return $component; - })->all() - ); - })->all(); - }), - ]; + case FieldType::DATE: + $component->formatStateUsing(function ($state) { + return rescue(fn () => Carbon::parse($state)->toFormattedDate(), $state, false); + }); + break; + } + + return $component; + })->all() + ); + })->all(); } } diff --git a/app/Filament/Shelter/Resources/BeneficiaryResource/Widgets/PersonalDataVersionsWidget.php b/app/Filament/Shelter/Resources/BeneficiaryResource/Widgets/PersonalDataVersionsWidget.php new file mode 100644 index 0000000..f9f0017 --- /dev/null +++ b/app/Filament/Shelter/Resources/BeneficiaryResource/Widgets/PersonalDataVersionsWidget.php @@ -0,0 +1,58 @@ +query(fn () => $this->record->personal()) + ->heading(__('app.form.actions.history')) + ->columns([ + TextColumn::make('id') + ->label(__('app.field.id')) + ->prefix('#') + ->sortable() + ->shrink(), + + TextColumn::make('form.sections.name') + ->label(__('app.field.form_sections')) + ->wrap(), + + TextColumn::make('created_at') + ->label(__('app.field.created_at')) + ->dateTime() + ->sortable() + ->shrink(), + + TextColumn::make('updated_at') + ->label(__('app.field.updated_at')) + ->dateTime() + ->sortable() + ->shrink(), + + ]) + ->actions([ + ViewAction::make() + ->url(fn (Response $record) => BeneficiaryResource::getUrl('versions.view', [ + 'record' => $this->record, + 'response' => $record, + ])), + ]); + } +} diff --git a/app/Models/Beneficiary.php b/app/Models/Beneficiary.php index a2cf8ce..6184b00 100644 --- a/app/Models/Beneficiary.php +++ b/app/Models/Beneficiary.php @@ -123,4 +123,9 @@ public function scopeWhereInShelter(Builder $query, Shelter $shelter): Builder { return $query->whereRelation('stays', 'shelter_id', $shelter->id); } + + public function hasMoreThanOneForm(): bool + { + return $this->personal()->count() > 1; + } } diff --git a/lang/en/app.php b/lang/en/app.php index 903f2f4..91f1aa3 100644 --- a/lang/en/app.php +++ b/lang/en/app.php @@ -39,6 +39,7 @@ 'enabled' => 'Enabled', 'end_date' => 'End date', 'fields' => 'Fields', + 'form_sections' => 'Form sections', 'form_type' => 'Form type', 'gender' => 'Gender', 'group_size' => 'No.', @@ -165,6 +166,7 @@ 'obsolete' => 'Obsolete', ], 'actions' => [ + 'history' => 'Form history', 'draft' => [ 'button' => 'Draft', 'confirm' => [