diff --git a/demo/app/Sharp/Utils/Filters/PeriodRequiredFilter.php b/demo/app/Sharp/Utils/Filters/PeriodRequiredFilter.php
index ef01d716b..60274eb93 100644
--- a/demo/app/Sharp/Utils/Filters/PeriodRequiredFilter.php
+++ b/demo/app/Sharp/Utils/Filters/PeriodRequiredFilter.php
@@ -2,13 +2,19 @@
namespace App\Sharp\Utils\Filters;
+use Code16\Sharp\Filters\DateRange\DateRangePreset;
use Code16\Sharp\Filters\DateRangeRequiredFilter;
class PeriodRequiredFilter extends DateRangeRequiredFilter
{
public function buildFilterConfig(): void
{
- $this->configureLabel('Period')->configureShowPresets();
+ $this->configureLabel('Period')
+ ->configureShowPresets(presets: [
+ DateRangePreset::make(today()->subDays(3), today(), 'Last 3 days'),
+ DateRangePreset::thisMonth(),
+ DateRangePreset::thisYear(),
+ ]);
}
public function defaultValue(): array
diff --git a/docs/guide/filters.md b/docs/guide/filters.md
index 68fc164c7..e11b54254 100644
--- a/docs/guide/filters.md
+++ b/docs/guide/filters.md
@@ -161,6 +161,39 @@ class ProductCreationDateFilter extends DateRangeFilter
}
```
+You can also define specific presets with the `DateRangePreset` class :
+
+```php
+use Code16\Sharp\Filters\DateRange\DateRangePreset;
+
+class ProductCreationDateFilter extends DateRangeFilter
+{
+ public function buildFilterConfig(): void
+ {
+ $this->configureShowPresets(presets: [
+ DateRangePreset::make(today()->subDays(3), today(), 'Last 3 days'),
+ DateRangePreset::thisMonth(),
+ ]);
+ }
+}
+```
+
+Following methods are available (default presets) :
+```php
+[
+ DateRangePreset::today(),
+ DateRangePreset::yesterday(),
+ DateRangePreset::last7days(),
+ DateRangePreset::last30days(),
+ DateRangePreset::last365days(),
+ DateRangePreset::thisMonth(),
+ DateRangePreset::lastMonth(),
+ DateRangePreset::thisYear(),
+ DateRangePreset::lastYear(),
+]
+```
+
+
## Autocomplete remote filter
If you want to use a remote filter, you can use the `Code16\Sharp\Filters\AutocompleteRemoteFilter` class. It is very similar to the `Code16\Sharp\Filters\SelectFilter` class, but it uses a remote endpoint to fetch the values.
diff --git a/resources/js/filters/components/filters/DateRangeFilter.vue b/resources/js/filters/components/filters/DateRangeFilter.vue
index 5e1a4ad14..e6b5f2409 100644
--- a/resources/js/filters/components/filters/DateRangeFilter.vue
+++ b/resources/js/filters/components/filters/DateRangeFilter.vue
@@ -1,6 +1,6 @@
+
+
+ {{ preset.label }}:
+
+
+
- {{ props.value.formatted.start }} - {{ props.value.formatted.end }}
+ {{ props.value.formatted.start }} - {{ props.value.formatted.end }}
diff --git a/resources/js/types/generated.d.ts b/resources/js/types/generated.d.ts
index e33b0dd8c..92be9a871 100644
--- a/resources/js/types/generated.d.ts
+++ b/resources/js/types/generated.d.ts
@@ -107,14 +107,18 @@ export type DateRangeFilterData = {
start: string;
end: string;
formatted?: { start: string; end: string };
- preset?: string;
} | null;
key: string;
label: string | null;
type: "daterange";
required: boolean;
mondayFirst: boolean;
- presets: Array<{ key: string; label: string }> | null;
+ presets: Array | null;
+};
+export type DateRangePresetData = {
+ label: string | null;
+ start: string | null;
+ end: string | null;
};
export type EmbedData = {
value?: FormData["data"] & { slot: string; _html: string };
diff --git a/src/Data/Filters/DateRangeFilterData.php b/src/Data/Filters/DateRangeFilterData.php
index f8ff49082..235a5ea03 100644
--- a/src/Data/Filters/DateRangeFilterData.php
+++ b/src/Data/Filters/DateRangeFilterData.php
@@ -17,7 +17,6 @@ final class DateRangeFilterData extends Data
start: string,
end: string,
formatted?: { start: string, end: string },
- preset?: string,
} | null')]
public ?array $value;
@@ -28,7 +27,7 @@ public function __construct(
public FilterType $type,
public bool $required,
public bool $mondayFirst,
- #[LiteralTypeScriptType('Array<{ key:string, label:string }>|null')]
+ /** @var DateRangePresetData[] */
public ?array $presets,
) {}
diff --git a/src/Data/Filters/DateRangePresetData.php b/src/Data/Filters/DateRangePresetData.php
new file mode 100644
index 000000000..d0cfdd2a6
--- /dev/null
+++ b/src/Data/Filters/DateRangePresetData.php
@@ -0,0 +1,22 @@
+end;
}
+ public static function make(Carbon $start, Carbon $end, string $label): self
+ {
+ return new static($start, $end, $label);
+ }
+
+ public static function today(): self
+ {
+ return new static(
+ Carbon::today(),
+ Carbon::today(),
+ __('sharp::filters.daterange.preset.today'),
+ );
+ }
+
+ public static function yesterday(): self
+ {
+ return new static(
+ Carbon::yesterday(),
+ Carbon::yesterday(),
+ __('sharp::filters.daterange.preset.yesterday'),
+ );
+ }
+
+ public static function last7days(): self
+ {
+ return new static(
+ Carbon::today()->subDays(6),
+ Carbon::today(),
+ __('sharp::filters.daterange.preset.last_7_days'),
+ );
+ }
+
+ public static function last30days(): self
+ {
+ return new static(
+ Carbon::today()->subDays(29),
+ Carbon::today(),
+ __('sharp::filters.daterange.preset.last_30_days'),
+ );
+ }
+
+ public static function last365days(): self
+ {
+ return new static(
+ Carbon::today()->subDays(364),
+ Carbon::today(),
+ __('sharp::filters.daterange.preset.last_365_days'),
+ );
+ }
+
+ public static function thisMonth(): self
+ {
+ return new static(
+ Carbon::today()->startOfMonth(),
+ Carbon::today()->endOfMonth(),
+ __('sharp::filters.daterange.preset.this_month'),
+ );
+ }
+
+ public static function lastMonth(): self
+ {
+ return new static(
+ Carbon::today()->subMonth()->startOfMonth(),
+ Carbon::today()->subMonth()->endOfMonth(),
+ __('sharp::filters.daterange.preset.last_month'),
+ );
+ }
+
+ public static function thisYear(): self
+ {
+ return new static(
+ Carbon::today()->startOfYear(),
+ Carbon::today()->endOfYear(),
+ __('sharp::filters.daterange.preset.this_year'),
+ );
+ }
+
+ public static function lastYear(): self
+ {
+ return new static(
+ Carbon::today()->subYear()->startOfYear(),
+ Carbon::today()->subYear()->endOfYear(),
+ __('sharp::filters.daterange.preset.last_year'),
+ );
+ }
+
public function toArray()
{
return [
'label' => $this->label,
+ 'start' => $this->start->format('Y-m-d'),
+ 'end' => $this->end->format('Y-m-d'),
];
}
}
diff --git a/src/Filters/DateRangeFilter.php b/src/Filters/DateRangeFilter.php
index 4557bc9d2..115638444 100644
--- a/src/Filters/DateRangeFilter.php
+++ b/src/Filters/DateRangeFilter.php
@@ -11,7 +11,9 @@ abstract class DateRangeFilter extends Filter
{
private string $dateFormat = 'YYYY-MM-DD';
private bool $isMondayFirst = true;
- private bool $showPresets = false;
+
+ /** @var DateRangePreset[] */
+ private ?array $presets = null;
final public function configureDateFormat(string $dateFormat): self
{
@@ -27,9 +29,25 @@ final public function configureMondayFirst(bool $isMondayFirst = true): self
return $this;
}
- final public function configureShowPresets(bool $showPresets = true): self
+ final public function configureShowPresets(bool $showPresets = true, ?array $presets = null): self
{
- $this->showPresets = $showPresets;
+ if ($showPresets) {
+ $this->presets = $presets !== null
+ ? $presets
+ : [
+ DateRangePreset::today(),
+ DateRangePreset::yesterday(),
+ DateRangePreset::last7days(),
+ DateRangePreset::last30days(),
+ DateRangePreset::last365days(),
+ DateRangePreset::thisMonth(),
+ DateRangePreset::lastMonth(),
+ DateRangePreset::thisYear(),
+ DateRangePreset::lastYear(),
+ ];
+ } else {
+ $this->presets = [];
+ }
return $this;
}
@@ -44,64 +62,6 @@ final public function isMondayFirst(): bool
return $this->isMondayFirst;
}
- /**
- * @return array
- */
- final public function getPresets(): array
- {
- if (! $this->showPresets) {
- return [];
- }
-
- return [
- 'today' => new DateRangePreset(
- start: today(),
- end: today(),
- label: __('sharp::filters.daterange.preset.today')
- ),
- 'yesterday' => new DateRangePreset(
- start: today()->subDay(),
- end: today()->subDay(),
- label: __('sharp::filters.daterange.preset.yesterday')
- ),
- 'last_7_days' => new DateRangePreset(
- start: today()->subDays(7),
- end: today(),
- label: __('sharp::filters.daterange.preset.last_7_days')
- ),
- 'last_30_days' => new DateRangePreset(
- start: today()->subDays(30),
- end: today(),
- label: __('sharp::filters.daterange.preset.last_30_days')
- ),
- 'last_365_days' => new DateRangePreset(
- start: today()->subDays(365),
- end: today(),
- label: __('sharp::filters.daterange.preset.last_365_days')
- ),
- 'this_month' => new DateRangePreset(
- start: today()->startOfMonth(),
- end: today()->endOfMonth(),
- label: __('sharp::filters.daterange.preset.this_month')
- ),
- 'last_month' => new DateRangePreset(
- start: today()->subMonth()->startOfMonth(),
- end: today()->subMonth()->endOfMonth(),
- label: __('sharp::filters.daterange.preset.last_month')
- ),
- 'this_year' => new DateRangePreset(
- start: today()->startOfYear(),
- end: today()->endOfYear(),
- label: __('sharp::filters.daterange.preset.this_year')
- ),
- 'last_year' => new DateRangePreset(
- start: today()->subYear()->startOfYear(),
- end: today()->subYear()->endOfYear(),
- label: __('sharp::filters.daterange.preset.last_year')
- ),
- ];
- }
-
/**
* @internal
*/
@@ -114,9 +74,6 @@ final public function fromQueryParam($value): ?array
[$start, $end] = explode('..', $value);
$start = Carbon::createFromFormat('Ymd', $start)->startOfDay();
$end = Carbon::createFromFormat('Ymd', $end)->endOfDay();
- $presetKey = collect($this->getPresets())
- ->search(fn (DateRangePreset $preset) => $preset->getStart()->isSameDay($start)
- && $preset->getEnd()->isSameDay($end));
return [
'start' => $start->format('Y-m-d'),
@@ -125,7 +82,6 @@ final public function fromQueryParam($value): ?array
'start' => $start->isoFormat($this->getDateFormat()),
'end' => $end->isoFormat($this->getDateFormat()),
],
- 'preset' => $presetKey ?: null,
];
}
@@ -138,18 +94,6 @@ final public function toQueryParam($value): ?string
return null;
}
- if (isset($value['preset'])) {
- if ($preset = $this->getPresets()[$value['preset']] ?? null) {
- return sprintf(
- '%s..%s',
- $preset->getStart()->format('Ymd'),
- $preset->getEnd()->format('Ymd'),
- );
- }
-
- return null;
- }
-
return sprintf(
'%s..%s',
Carbon::parse($value['start'])->format('Ymd'),
@@ -163,9 +107,8 @@ public function toArray(): array
'type' => FilterType::DateRange->value,
'required' => $this instanceof DateRangeRequiredFilter,
'mondayFirst' => $this->isMondayFirst(),
- 'presets' => collect($this->getPresets())
- ->map(fn ($preset, $key) => ['key' => $key, ...$preset->toArray()])
- ->values()
+ 'presets' => collect($this->presets)
+ ->map(fn (DateRangePreset $preset) => $preset->toArray())
->toArray(),
]);
}
diff --git a/tests/Unit/EntityList/SharpEntityListFilterTest.php b/tests/Unit/EntityList/SharpEntityListFilterTest.php
index 4e76e1ca1..6e78ebfa8 100644
--- a/tests/Unit/EntityList/SharpEntityListFilterTest.php
+++ b/tests/Unit/EntityList/SharpEntityListFilterTest.php
@@ -4,6 +4,7 @@
use Code16\Sharp\EntityList\Filters\HiddenFilter;
use Code16\Sharp\Filters\AutocompleteRemoteFilter;
use Code16\Sharp\Filters\CheckFilter;
+use Code16\Sharp\Filters\DateRange\DateRangePreset;
use Code16\Sharp\Filters\DateRangeFilter;
use Code16\Sharp\Filters\DateRangeRequiredFilter;
use Code16\Sharp\Filters\SelectFilter;
@@ -391,6 +392,158 @@ public function defaultValue(): mixed
]);
});
+it('allows to declare date range filter', function () {
+ $list = new class() extends FakeSharpEntityList
+ {
+ public function getFilters(): array
+ {
+ return [
+ new class() extends DateRangeFilter
+ {
+ public function buildFilterConfig(): void
+ {
+ $this->configureKey('test_22')
+ ->configureLabel('Test filter');
+ }
+ },
+ ];
+ }
+ };
+
+ $list->buildListConfig();
+
+ expect($list->listConfig()['filters']['_root'][0])->toEqual([
+ 'key' => 'test_22',
+ 'label' => 'Test filter',
+ 'type' => 'daterange',
+ 'required' => false,
+ 'mondayFirst' => true,
+ 'presets' => [],
+ ]);
+});
+
+it('allows to declare date range filter with default presets', function () {
+ $list = new class() extends FakeSharpEntityList
+ {
+ public function getFilters(): array
+ {
+ return [
+ new class() extends DateRangeFilter
+ {
+ public function buildFilterConfig(): void
+ {
+ $this->configureKey('test_22')
+ ->configureLabel('Test filter')
+ ->configureShowPresets();
+ }
+ },
+ ];
+ }
+ };
+
+ $list->buildListConfig();
+
+ expect($list->listConfig()['filters']['_root'][0])->toEqual([
+ 'key' => 'test_22',
+ 'label' => 'Test filter',
+ 'type' => 'daterange',
+ 'required' => false,
+ 'mondayFirst' => true,
+ 'presets' => [
+ [
+ 'label' => 'Today',
+ 'start' => today()->format('Y-m-d'),
+ 'end' => today()->format('Y-m-d'),
+ ],
+ [
+ 'label' => 'Yesterday',
+ 'start' => today()->subDay()->format('Y-m-d'),
+ 'end' => today()->subDay()->format('Y-m-d'),
+ ],
+ [
+ 'label' => 'Last 7 days',
+ 'start' => today()->subDays(6)->format('Y-m-d'),
+ 'end' => today()->format('Y-m-d'),
+ ],
+ [
+ 'label' => 'Last 30 days',
+ 'start' => today()->subDays(29)->format('Y-m-d'),
+ 'end' => today()->format('Y-m-d'),
+ ],
+ [
+ 'label' => 'Last 365 days',
+ 'start' => today()->subDays(364)->format('Y-m-d'),
+ 'end' => today()->format('Y-m-d'),
+ ],
+ [
+ 'label' => 'This month',
+ 'start' => today()->startOfMonth()->format('Y-m-d'),
+ 'end' => today()->endOfMonth()->format('Y-m-d'),
+ ],
+ [
+ 'label' => 'Last month',
+ 'start' => today()->subMonth()->startOfMonth()->format('Y-m-d'),
+ 'end' => today()->subMonth()->endOfMonth()->format('Y-m-d'),
+ ],
+ [
+ 'label' => 'This year',
+ 'start' => today()->startOfYear()->format('Y-m-d'),
+ 'end' => today()->endOfYear()->format('Y-m-d'),
+ ],
+ [
+ 'label' => 'Last year',
+ 'start' => today()->subYear()->startOfYear()->format('Y-m-d'),
+ 'end' => today()->subYear()->endOfYear()->format('Y-m-d'),
+ ],
+ ],
+ ]);
+});
+
+it('allows to declare date range filter with custom presets', function () {
+ $list = new class() extends FakeSharpEntityList
+ {
+ public function getFilters(): array
+ {
+ return [
+ new class() extends DateRangeFilter
+ {
+ public function buildFilterConfig(): void
+ {
+ $this->configureKey('test_22')
+ ->configureLabel('Test filter')
+ ->configureShowPresets(presets: [
+ DateRangePreset::make(today()->subDays(3), today(), 'Last 3 days'),
+ DateRangePreset::thisMonth(),
+ ]);
+ }
+ },
+ ];
+ }
+ };
+
+ $list->buildListConfig();
+
+ expect($list->listConfig()['filters']['_root'][0])->toEqual([
+ 'key' => 'test_22',
+ 'label' => 'Test filter',
+ 'type' => 'daterange',
+ 'required' => false,
+ 'mondayFirst' => true,
+ 'presets' => [
+ [
+ 'label' => 'Last 3 days',
+ 'start' => today()->subDays(3)->format('Y-m-d'),
+ 'end' => today()->format('Y-m-d'),
+ ],
+ [
+ 'label' => 'This month',
+ 'start' => today()->startOfMonth()->format('Y-m-d'),
+ 'end' => today()->endOfMonth()->format('Y-m-d'),
+ ],
+ ],
+ ]);
+});
+
it('formats date range filter retained value', function () {
$list = new class() extends FakeSharpEntityList
{
@@ -419,7 +572,6 @@ public function buildFilterConfig(): void
'test_22' => [
'start' => '2019-09-22',
'end' => '2019-09-25',
- 'preset' => null,
'formatted' => [
'start' => '2019-09-22',
'end' => '2019-09-25',
@@ -460,7 +612,6 @@ public function defaultValue(): array
'test' => [
'start' => Carbon::now()->subDay()->format('Y-m-d'),
'end' => Carbon::now()->format('Y-m-d'),
- 'preset' => null,
'formatted' => [
'start' => Carbon::now()->subDay()->format('Y-m-d'),
'end' => Carbon::now()->format('Y-m-d'),
@@ -471,7 +622,6 @@ public function defaultValue(): array
'test' => [
'start' => Carbon::now()->subDay()->format('Y-m-d'),
'end' => Carbon::now()->format('Y-m-d'),
- 'preset' => null,
'formatted' => [
'start' => Carbon::now()->subDay()->format('Y-m-d'),
'end' => Carbon::now()->format('Y-m-d'),
@@ -511,7 +661,6 @@ public function buildFilterConfig(): void
'test_22' => [
'start' => '2019-09-22',
'end' => '2019-09-25',
- 'preset' => null,
'formatted' => [
'start' => '2019_09_22',
'end' => '2019_09_25',