|
18 | 18 | use Filament\QueryBuilder\Constraints\SelectConstraint; |
19 | 19 | use Filament\QueryBuilder\Constraints\SelectConstraint\Operators\IsOperator as SelectIsOperator; |
20 | 20 | use Filament\QueryBuilder\Forms\Components\RuleBuilder; |
| 21 | +use Filament\Support\Facades\FilamentTimezone; |
21 | 22 | use Filament\Tables\Filters\QueryBuilder; |
22 | 23 | use Filament\Tables\Filters\QueryBuilder\Constraints\TextConstraint; |
23 | 24 | use Filament\Tests\Fixtures\Livewire\PostsQueryBuilderTable; |
@@ -3806,6 +3807,119 @@ function applyQueryBuilderFilter(array $rules) |
3806 | 3807 | ->assertCanNotSeeTableRecords([$beforeThresholdPost]); |
3807 | 3808 | }); |
3808 | 3809 |
|
| 3810 | + it('can filter records using datetime constraint with is after operator when the Filament timezone differs from the app timezone', function (): void { |
| 3811 | + config(['app.timezone' => 'UTC']); |
| 3812 | + FilamentTimezone::set('America/New_York'); |
| 3813 | + |
| 3814 | + $beforeThresholdPost = Post::factory()->create([ |
| 3815 | + 'published_at' => '2026-07-13 15:00:00', |
| 3816 | + ]); |
| 3817 | + |
| 3818 | + $afterThresholdPost = Post::factory()->create([ |
| 3819 | + 'published_at' => '2026-07-13 17:00:00', |
| 3820 | + ]); |
| 3821 | + |
| 3822 | + livewire(PostsQueryBuilderTable::class) |
| 3823 | + ->assertCanSeeTableRecords([$beforeThresholdPost, $afterThresholdPost]) |
| 3824 | + ->tap(applyQueryBuilderFilter([ |
| 3825 | + [ |
| 3826 | + 'type' => 'published_at', |
| 3827 | + 'data' => [ |
| 3828 | + 'operator' => 'isAfter', |
| 3829 | + 'settings' => [ |
| 3830 | + 'mode' => 'absolute', |
| 3831 | + 'date' => '2026-07-13 12:00:00', |
| 3832 | + ], |
| 3833 | + ], |
| 3834 | + ], |
| 3835 | + ])) |
| 3836 | + ->assertCanSeeTableRecords([$afterThresholdPost]) |
| 3837 | + ->assertCanNotSeeTableRecords([$beforeThresholdPost]) |
| 3838 | + ->assertSee('Published at is after Mon, Jul 13, 2026 12:00:00'); |
| 3839 | + }); |
| 3840 | + |
| 3841 | + it('can filter records using datetime constraint with is before operator when the Filament timezone differs from the app timezone', function (): void { |
| 3842 | + config(['app.timezone' => 'UTC']); |
| 3843 | + FilamentTimezone::set('America/New_York'); |
| 3844 | + |
| 3845 | + // The cutoff of `12:00:00` is entered in the Filament timezone (`America/New_York`), so it |
| 3846 | + // must be dehydrated to `16:00:00` in the app timezone (`UTC`) before querying, while the |
| 3847 | + // summary continues to display the raw `12:00:00` the user entered. |
| 3848 | + $beforeThresholdPost = Post::factory()->create([ |
| 3849 | + 'published_at' => '2026-07-13 15:00:00', |
| 3850 | + ]); |
| 3851 | + |
| 3852 | + $afterThresholdPost = Post::factory()->create([ |
| 3853 | + 'published_at' => '2026-07-13 17:00:00', |
| 3854 | + ]); |
| 3855 | + |
| 3856 | + livewire(PostsQueryBuilderTable::class) |
| 3857 | + ->assertCanSeeTableRecords([$beforeThresholdPost, $afterThresholdPost]) |
| 3858 | + ->tap(applyQueryBuilderFilter([ |
| 3859 | + [ |
| 3860 | + 'type' => 'published_at', |
| 3861 | + 'data' => [ |
| 3862 | + 'operator' => 'isBefore', |
| 3863 | + 'settings' => [ |
| 3864 | + 'mode' => 'absolute', |
| 3865 | + 'date' => '2026-07-13 12:00:00', |
| 3866 | + ], |
| 3867 | + ], |
| 3868 | + ], |
| 3869 | + ])) |
| 3870 | + ->assertCanSeeTableRecords([$beforeThresholdPost]) |
| 3871 | + ->assertCanNotSeeTableRecords([$afterThresholdPost]) |
| 3872 | + ->assertSee('Published at is before Mon, Jul 13, 2026 12:00:00'); |
| 3873 | + }); |
| 3874 | + |
| 3875 | + it('applies datetime constraints and summaries from the applied state, not unapplied deferred edits', function (): void { |
| 3876 | + $earlyPost = Post::factory()->create([ |
| 3877 | + 'published_at' => '2026-07-13 10:00:00', |
| 3878 | + ]); |
| 3879 | + |
| 3880 | + $latePost = Post::factory()->create([ |
| 3881 | + 'published_at' => '2026-07-13 20:00:00', |
| 3882 | + ]); |
| 3883 | + |
| 3884 | + livewire(PostsQueryBuilderTable::class) |
| 3885 | + ->assertCanSeeTableRecords([$earlyPost, $latePost]) |
| 3886 | + // Apply a rule that only matches the late post. |
| 3887 | + ->tap(applyQueryBuilderFilter([ |
| 3888 | + [ |
| 3889 | + 'type' => 'published_at', |
| 3890 | + 'data' => [ |
| 3891 | + 'operator' => 'isAfter', |
| 3892 | + 'settings' => [ |
| 3893 | + 'mode' => 'absolute', |
| 3894 | + 'date' => '2026-07-13 15:00:00', |
| 3895 | + ], |
| 3896 | + ], |
| 3897 | + ], |
| 3898 | + ])) |
| 3899 | + ->assertCanSeeTableRecords([$latePost]) |
| 3900 | + ->assertCanNotSeeTableRecords([$earlyPost]) |
| 3901 | + ->assertSee('Published at is after Mon, Jul 13, 2026 15:00:00') |
| 3902 | + // Edit the deferred form to a looser threshold that would match both posts, *without* applying it. |
| 3903 | + ->set('tableDeferredFilters.query_builder.rules', [ |
| 3904 | + [ |
| 3905 | + 'type' => 'published_at', |
| 3906 | + 'data' => [ |
| 3907 | + 'operator' => 'isAfter', |
| 3908 | + 'settings' => [ |
| 3909 | + 'mode' => 'absolute', |
| 3910 | + 'date' => '2026-07-13 05:00:00', |
| 3911 | + ], |
| 3912 | + ], |
| 3913 | + ], |
| 3914 | + ]) |
| 3915 | + // The query and the summary must still reflect the applied threshold (`15:00:00`), |
| 3916 | + // not the unapplied deferred edit (`05:00:00`). |
| 3917 | + ->assertCanSeeTableRecords([$latePost]) |
| 3918 | + ->assertCanNotSeeTableRecords([$earlyPost]) |
| 3919 | + ->assertSee('Published at is after Mon, Jul 13, 2026 15:00:00') |
| 3920 | + ->assertDontSee('Published at is after Mon, Jul 13, 2026 05:00:00'); |
| 3921 | + }); |
| 3922 | + |
3809 | 3923 | it('can filter records using datetime constraint with is after operator with `this_minute` preset', function (): void { |
3810 | 3924 | $currentMinutePosts = Post::factory()->count(3)->create([ |
3811 | 3925 | 'published_at' => now()->startOfMinute()->addSeconds(30), |
|
0 commit comments