From f4076f0f658b706fb7153ecadd992c27019801c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrei=20Ioni=C8=9B=C4=83?= Date: Wed, 2 Apr 2025 17:37:51 +0100 Subject: [PATCH 1/2] feat: indefinite stays --- .../BeneficiaryResource/Schemas/StayForm.php | 27 ++++++++++++++++--- app/Models/Stay.php | 16 ++++++++--- database/factories/ShelterFactory.php | 7 +++++ database/factories/StayFactory.php | 7 +++++ ...nd_date_column_nullable_on_stays_table.php | 19 +++++++++++++ lang/en/app.php | 1 + 6 files changed, 70 insertions(+), 7 deletions(-) create mode 100644 database/migrations/0001_01_04_000007_make_end_date_column_nullable_on_stays_table.php diff --git a/app/Filament/Shelter/Resources/BeneficiaryResource/Schemas/StayForm.php b/app/Filament/Shelter/Resources/BeneficiaryResource/Schemas/StayForm.php index 50c8fa6..0c3122c 100644 --- a/app/Filament/Shelter/Resources/BeneficiaryResource/Schemas/StayForm.php +++ b/app/Filament/Shelter/Resources/BeneficiaryResource/Schemas/StayForm.php @@ -15,6 +15,7 @@ use Filament\Forms\Components\Textarea; use Filament\Forms\Components\TextInput; use Filament\Forms\Get; +use Filament\Forms\Set; use Illuminate\Support\Str; class StayForm @@ -32,10 +33,7 @@ public static function getSchema(?int $beneficiary_id = null): array ->label(__('app.field.start_date')) ->required(), - DatePicker::make('end_date') - ->label(__('app.field.end_date')) - ->afterOrEqual('start_date') - ->required(), + static::getEndDateGroup(), Checkbox::make('has_children') ->label(__('app.field.has_children')) @@ -87,4 +85,25 @@ public static function getSchema(?int $beneficiary_id = null): array ]), ]; } + + public static function getEndDateGroup(): Group + { + return Group::make() + ->schema([ + DatePicker::make('end_date') + ->label(__('app.field.end_date')) + ->afterOrEqual('start_date') + ->required() + ->disabled(fn (Get $get) => $get('is_indefinite')), + + Checkbox::make('is_indefinite') + ->label(__('app.field.is_indefinite')) + ->live() + ->afterStateUpdated(function (bool $state, Set $set) { + if ($state) { + $set('end_date', null); + } + }), + ]); + } } diff --git a/app/Models/Stay.php b/app/Models/Stay.php index 2549332..21ac28f 100644 --- a/app/Models/Stay.php +++ b/app/Models/Stay.php @@ -54,7 +54,10 @@ public function scopeWhereCurrent(Builder $query): Builder { return $query ->whereDate('start_date', '<=', today()) - ->whereDate('end_date', '>=', today()); + ->where(function (Builder $query) { + $query->whereDate('end_date', '>=', today()) + ->orWhereNull('end_date'); + }); } public function scopeWhereInShelter(Builder $query, Shelter $shelter): Builder @@ -62,6 +65,13 @@ public function scopeWhereInShelter(Builder $query, Shelter $shelter): Builder return $query->where('shelter_id', $shelter->id); } + protected function isIndefinite(): Attribute + { + return Attribute::make( + get: fn (mixed $value, array $attributes) => blank($attributes['end_date']), + ); + } + public function hasChildren(): Attribute { return Attribute::make( @@ -80,10 +90,10 @@ public function title(): Attribute { return Attribute::make( fn () => \sprintf( - '#%s %s–%s', + '#%s %s - %s', $this->id, $this->start_date->toFormattedDate(), - $this->end_date->toFormattedDate() + $this->end_date?->toFormattedDate() ?? __('app.stay.indefinite') ) ); } diff --git a/database/factories/ShelterFactory.php b/database/factories/ShelterFactory.php index 594b536..d899e95 100644 --- a/database/factories/ShelterFactory.php +++ b/database/factories/ShelterFactory.php @@ -57,6 +57,13 @@ public function configure(): static ->for($shelter) ->create(); + Stay::factory() + ->count($beneficiaries->count()) + ->sequence(...$beneficiaries) + ->for($shelter) + ->indefinite() + ->create(); + Request::factory() ->count(10) ->for($shelter) diff --git a/database/factories/StayFactory.php b/database/factories/StayFactory.php index 485da6b..c6a8526 100644 --- a/database/factories/StayFactory.php +++ b/database/factories/StayFactory.php @@ -33,4 +33,11 @@ public function definition(): array 'children_notes' => $children ? fake()->optional()->sentence() : null, ]; } + + public function indefinite(): static + { + return $this->state([ + 'end_date' => null, + ]); + } } diff --git a/database/migrations/0001_01_04_000007_make_end_date_column_nullable_on_stays_table.php b/database/migrations/0001_01_04_000007_make_end_date_column_nullable_on_stays_table.php new file mode 100644 index 0000000..0319249 --- /dev/null +++ b/database/migrations/0001_01_04_000007_make_end_date_column_nullable_on_stays_table.php @@ -0,0 +1,19 @@ +date('end_date')->nullable()->change(); + }); + } +}; diff --git a/lang/en/app.php b/lang/en/app.php index a75cb93..572ba29 100644 --- a/lang/en/app.php +++ b/lang/en/app.php @@ -130,6 +130,7 @@ 'singular' => 'stay', 'plural' => 'stays', ], + 'indefinite' => 'Indefinite', 'details' => 'Stay details', 'actions' => [ 'extend' => [ From b62c5d4cb8ab2a03c9c47e1d58125d630d7a1518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrei=20Ioni=C8=9B=C4=83?= Date: Thu, 3 Apr 2025 14:41:19 +0100 Subject: [PATCH 2/2] wip --- .../Shelter/Resources/BeneficiaryResource.php | 2 +- .../Actions/ExtendStayAction.php | 22 +++++++++++-------- .../BeneficiaryResource/Schemas/StayForm.php | 2 +- .../Schemas/StayInfolist.php | 22 ++++++++++++++++--- .../Widgets/StaysWidget.php | 12 +++++++++- app/Models/Beneficiary.php | 2 +- lang/en/app.php | 3 ++- lang/es/app.php | 2 +- 8 files changed, 49 insertions(+), 18 deletions(-) diff --git a/app/Filament/Shelter/Resources/BeneficiaryResource.php b/app/Filament/Shelter/Resources/BeneficiaryResource.php index ececddf..7f29ea0 100644 --- a/app/Filament/Shelter/Resources/BeneficiaryResource.php +++ b/app/Filament/Shelter/Resources/BeneficiaryResource.php @@ -124,7 +124,7 @@ public static function table(Table $table): Table ->formatStateUsing(fn (Stay $state) => \sprintf( '%s – %s', $state->start_date->toFormattedDate(), - $state->end_date->toFormattedDate(), + $state->end_date?->toFormattedDate() ?? __('app.stay.indefinite') )), ]) ->filters([ diff --git a/app/Filament/Shelter/Resources/BeneficiaryResource/Actions/ExtendStayAction.php b/app/Filament/Shelter/Resources/BeneficiaryResource/Actions/ExtendStayAction.php index 7d27a43..b825a83 100644 --- a/app/Filament/Shelter/Resources/BeneficiaryResource/Actions/ExtendStayAction.php +++ b/app/Filament/Shelter/Resources/BeneficiaryResource/Actions/ExtendStayAction.php @@ -4,9 +4,10 @@ namespace App\Filament\Shelter\Resources\BeneficiaryResource\Actions; +use App\Filament\Shelter\Resources\BeneficiaryResource\Schemas\StayForm; use App\Models\Stay; use Filament\Actions\Action; -use Filament\Forms\Components\DatePicker; +use Filament\Forms\Components\Hidden; class ExtendStayAction extends Action { @@ -29,9 +30,7 @@ protected function setUp(): void $this->modalHeading( fn (Stay $record) => __('app.stay.actions.extend.confirm.title', [ - 'stay' => $record->id, - 'start_date' => $record->start_date->toFormattedDate(), - 'end_date' => $record->end_date->toFormattedDate(), + 'title' => $record->title, ]) ); @@ -39,12 +38,17 @@ protected function setUp(): void $this->modalWidth('md'); + $this->fillForm(fn (Stay $record) => [ + 'start_date' => $record->start_date, + 'end_date' => $record->end_date, + 'is_indefinite' => $record->is_indefinite, + ]); + $this->form([ - DatePicker::make('end_date') - ->label(__('app.field.end_date')) - ->after(fn (Stay $record) => $record->end_date->toDateString()) - ->default(fn (Stay $record) => $record->end_date->toDateString()) - ->required(), + Hidden::make('start_date') + ->label(__('app.field.start_date')), + + StayForm::getEndDateGroup(), ]); $this->action(function (Stay $record, array $data) { diff --git a/app/Filament/Shelter/Resources/BeneficiaryResource/Schemas/StayForm.php b/app/Filament/Shelter/Resources/BeneficiaryResource/Schemas/StayForm.php index 0c3122c..da798c1 100644 --- a/app/Filament/Shelter/Resources/BeneficiaryResource/Schemas/StayForm.php +++ b/app/Filament/Shelter/Resources/BeneficiaryResource/Schemas/StayForm.php @@ -93,7 +93,7 @@ public static function getEndDateGroup(): Group DatePicker::make('end_date') ->label(__('app.field.end_date')) ->afterOrEqual('start_date') - ->required() + ->required(fn (Get $get) => ! $get('is_indefinite')) ->disabled(fn (Get $get) => $get('is_indefinite')), Checkbox::make('is_indefinite') diff --git a/app/Filament/Shelter/Resources/BeneficiaryResource/Schemas/StayInfolist.php b/app/Filament/Shelter/Resources/BeneficiaryResource/Schemas/StayInfolist.php index bc58ff0..084165a 100644 --- a/app/Filament/Shelter/Resources/BeneficiaryResource/Schemas/StayInfolist.php +++ b/app/Filament/Shelter/Resources/BeneficiaryResource/Schemas/StayInfolist.php @@ -6,9 +6,11 @@ use App\Filament\Shelter\Resources\RequestResource; use App\Models\Stay; +use Carbon\Carbon; use Filament\Infolists\Components\Group; use Filament\Infolists\Components\Section; use Filament\Infolists\Components\TextEntry; +use Filament\Infolists\Infolist; class StayInfolist { @@ -34,9 +36,7 @@ public static function getSchema(): array ->label(__('app.field.start_date')) ->date(), - TextEntry::make('end_date') - ->label(__('app.field.end_date')) - ->date(), + static::getEndDateTextEntry(), TextEntry::make('has_children') ->label(__('app.field.has_children')) @@ -65,4 +65,20 @@ public static function getSchema(): array ]), ]; } + + public static function getEndDateTextEntry(): TextEntry + { + return TextEntry::make('end_date') + ->label(__('app.field.end_date')) + ->date() + ->formatStateUsing(function (TextEntry $component, $state) { + if (blank($state) || $component->getDefaultState() === $state) { + return __('app.stay.indefinite'); + } + + return Carbon::parse($state) + ->setTimezone($component->getTimezone()) + ->translatedFormat($component->evaluate(Infolist::$defaultDateDisplayFormat)); + }); + } } diff --git a/app/Filament/Shelter/Resources/BeneficiaryResource/Widgets/StaysWidget.php b/app/Filament/Shelter/Resources/BeneficiaryResource/Widgets/StaysWidget.php index ff0e8ac..5ead1bc 100644 --- a/app/Filament/Shelter/Resources/BeneficiaryResource/Widgets/StaysWidget.php +++ b/app/Filament/Shelter/Resources/BeneficiaryResource/Widgets/StaysWidget.php @@ -9,6 +9,7 @@ use App\Filament\Shelter\Resources\RequestResource; use App\Models\Beneficiary; use App\Models\Stay; +use Carbon\Carbon; use Filament\Tables\Actions\CreateAction; use Filament\Tables\Actions\ViewAction; use Filament\Tables\Columns\TextColumn; @@ -40,7 +41,16 @@ public function table(Table $table): Table TextColumn::make('end_date') ->label(__('app.field.end_date')) - ->date() + ->default(__('app.stay.indefinite')) + ->formatStateUsing(function (TextColumn $column, $state) { + if (blank($state) || $state === __('app.stay.indefinite')) { + return $state; + } + + return Carbon::parse($state) + ->setTimezone($column->getTimezone()) + ->translatedFormat($column->evaluate(Table::$defaultDateDisplayFormat)); + }) ->sortable() ->shrink(), diff --git a/app/Models/Beneficiary.php b/app/Models/Beneficiary.php index 6184b00..a6c822d 100644 --- a/app/Models/Beneficiary.php +++ b/app/Models/Beneficiary.php @@ -77,7 +77,7 @@ public function stays(): HasMany public function latestStay(): HasOne { - return $this->hasOne(Stay::class)->latestOfMany('end_date'); + return $this->hasOne(Stay::class)->latestOfMany(); } public function documents(): HasMany diff --git a/lang/en/app.php b/lang/en/app.php index 572ba29..dc4de8b 100644 --- a/lang/en/app.php +++ b/lang/en/app.php @@ -52,6 +52,7 @@ 'id_type' => 'ID type', 'id' => 'ID', 'identifier' => 'Identifier', + 'is_indefinite' => 'Indefinite end date', 'label' => 'Label', 'latest_stay' => 'Latest stay', 'legal_documents' => 'Legal documents', @@ -137,7 +138,7 @@ 'button' => 'Extend stay', 'confirm' => [ 'description' => 'Extend a beneficiary\'s stay by updating the end date.', - 'title' => 'Extend stay #:stay :start_date—:end_date', + 'title' => 'Extend stay :title', 'success' => 'Stay extended successfully.', ], ], diff --git a/lang/es/app.php b/lang/es/app.php index c836dd2..8901b5b 100644 --- a/lang/es/app.php +++ b/lang/es/app.php @@ -449,7 +449,7 @@ 'button' => 'Prolongar estancia', 'confirm' => [ 'description' => 'Amplíe la estancia de un beneficiario actualizando la fecha de finalización.', - 'title' => 'Prolongar estancia #:stay:start_date—:end_date', + 'title' => 'Prolongar estancia :title', 'success' => 'Manténgase extendido con éxito.', ], ],