Skip to content

Commit c0816b6

Browse files
feat: add configurable date display formats [3.x] (#129)
* feat: add configurable date display formats [3.x] Allow applications to set global date/datetime display formats via CustomFieldsPlugin or CustomFields facade. All Filament components (forms, tables, infolists) and validation messages respect the configured format while preserving BC with null defaults. * fix: improve date display format components - Use sample date as placeholder instead of raw PHP format string - Use ->date() for date-only fields in DateTimeEntry infolist - Accept nullable format in facade setters to allow resetting to defaults * docs: add date display formats to configuration page
1 parent eb734ad commit c0816b6

8 files changed

Lines changed: 178 additions & 12 deletions

File tree

docs/content/2.essentials/1.configuration.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,54 @@ class AppServiceProvider extends ServiceProvider
2828
}
2929
```
3030

31+
## Date Display Formats
32+
33+
By default, date and date-time fields use different formats depending on context (forms use `Y-m-d`, tables use `M j, Y`, etc.). You can set a single consistent format across all Filament components.
34+
35+
### Via Plugin (recommended)
36+
37+
```php
38+
use Relaticle\CustomFields\CustomFieldsPlugin;
39+
40+
CustomFieldsPlugin::make()
41+
->dateDisplayFormat('m/d/Y')
42+
->dateTimeDisplayFormat('m/d/Y h:i A')
43+
```
44+
45+
### Via Facade
46+
47+
```php
48+
use Relaticle\CustomFields\CustomFields;
49+
50+
class AppServiceProvider extends ServiceProvider
51+
{
52+
public function boot()
53+
{
54+
CustomFields::useDateDisplayFormat('m/d/Y');
55+
CustomFields::useDateTimeDisplayFormat('m/d/Y h:i A');
56+
}
57+
}
58+
```
59+
60+
The configured format applies to:
61+
62+
| Component | Without config | With config |
63+
|-----------|---------------|-------------|
64+
| Form date pickers | `Y-m-d` / `Y-m-d H:i:s` | Your format |
65+
| Table columns | `M j, Y` / `M j, Y H:i` | Your format |
66+
| Infolist entries | `Y-m-d` / `Y-m-d H:i:s` | Your format |
67+
| Validation messages | `M j, Y` | Your format |
68+
69+
::alert{type="info"}
70+
This only affects how dates are **displayed**. Storage formats (`Y-m-d` and `Y-m-d H:i:s`) remain unchanged.
71+
::
72+
73+
Pass `null` to reset back to component defaults:
74+
75+
```php
76+
CustomFields::useDateDisplayFormat(null);
77+
```
78+
3179
## Configuration File
3280

3381
The configuration file (`config/custom-fields.php`) allows you to customize all aspects of the Custom Fields package. It uses modern fluent configurators for type safety and better IDE support.

src/CustomFields.php

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,20 @@ final class CustomFields
3333
*/
3434
public static string $sectionModel = CustomFieldSection::class;
3535

36+
/**
37+
* The display format for date fields (e.g., 'm/d/Y', 'Y-m-d', 'd.m.Y').
38+
*
39+
* When null, each component uses its own default format.
40+
*/
41+
public static ?string $dateDisplayFormat = null;
42+
43+
/**
44+
* The display format for date-time fields (e.g., 'm/d/Y h:i A', 'Y-m-d H:i:s').
45+
*
46+
* When null, each component uses its own default format.
47+
*/
48+
public static ?string $dateTimeDisplayFormat = null;
49+
3650
/**
3751
* Get the name of the custom field model used by the application.
3852
*
@@ -160,6 +174,58 @@ public static function useSectionModel(string $model): static
160174
return new self;
161175
}
162176

177+
/**
178+
* Set the display format for date custom fields.
179+
*
180+
* This format is used across all Filament components (forms, tables, infolists)
181+
* when rendering date custom field values.
182+
*
183+
* Example:
184+
* ```
185+
* CustomFields::useDateDisplayFormat('m/d/Y');
186+
* ```
187+
*/
188+
public static function useDateDisplayFormat(?string $format): static
189+
{
190+
self::$dateDisplayFormat = $format;
191+
192+
return new self;
193+
}
194+
195+
/**
196+
* Set the display format for date-time custom fields.
197+
*
198+
* This format is used across all Filament components (forms, tables, infolists)
199+
* when rendering date-time custom field values.
200+
*
201+
* Example:
202+
* ```
203+
* CustomFields::useDateTimeDisplayFormat('m/d/Y h:i A');
204+
* ```
205+
*/
206+
public static function useDateTimeDisplayFormat(?string $format): static
207+
{
208+
self::$dateTimeDisplayFormat = $format;
209+
210+
return new self;
211+
}
212+
213+
/**
214+
* Get the configured date display format, or null for component defaults.
215+
*/
216+
public static function dateDisplayFormat(): ?string
217+
{
218+
return self::$dateDisplayFormat;
219+
}
220+
221+
/**
222+
* Get the configured date-time display format, or null for component defaults.
223+
*/
224+
public static function dateTimeDisplayFormat(): ?string
225+
{
226+
return self::$dateTimeDisplayFormat;
227+
}
228+
163229
/**
164230
* Register a custom tenant resolver callback.
165231
*

src/CustomFieldsPlugin.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ class CustomFieldsPlugin implements Plugin
2222

2323
protected bool|Closure $authorizeUsing = true;
2424

25+
protected ?string $dateDisplayFormat = null;
26+
27+
protected ?string $dateTimeDisplayFormat = null;
28+
2529
public function getId(): string
2630
{
2731
return 'custom-fields';
@@ -38,6 +42,14 @@ public function register(Panel $panel): void
3842

3943
public function boot(Panel $panel): void
4044
{
45+
if ($this->dateDisplayFormat !== null) {
46+
CustomFields::useDateDisplayFormat($this->dateDisplayFormat);
47+
}
48+
49+
if ($this->dateTimeDisplayFormat !== null) {
50+
CustomFields::useDateTimeDisplayFormat($this->dateTimeDisplayFormat);
51+
}
52+
4153
if (FeatureManager::isEnabled(CustomFieldsFeature::SYSTEM_MULTI_TENANCY)) {
4254
Action::configureUsing(
4355
fn (Action $action): Action => $action->before(
@@ -82,4 +94,18 @@ public function isAuthorized(): bool
8294
{
8395
return $this->evaluate($this->authorizeUsing) === true;
8496
}
97+
98+
public function dateDisplayFormat(?string $format): static
99+
{
100+
$this->dateDisplayFormat = $format;
101+
102+
return $this;
103+
}
104+
105+
public function dateTimeDisplayFormat(?string $format): static
106+
{
107+
$this->dateTimeDisplayFormat = $format;
108+
109+
return $this;
110+
}
85111
}

src/Filament/Integration/Components/Forms/DateComponent.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,22 @@
44

55
namespace Relaticle\CustomFields\Filament\Integration\Components\Forms;
66

7+
use Carbon\Carbon;
78
use Filament\Forms\Components\DatePicker;
9+
use Relaticle\CustomFields\CustomFields;
810
use Relaticle\CustomFields\Filament\Integration\Base\AbstractFormComponent;
911
use Relaticle\CustomFields\Models\CustomField;
1012

1113
final readonly class DateComponent extends AbstractFormComponent
1214
{
1315
public function create(CustomField $customField): DatePicker
1416
{
17+
$configuredFormat = CustomFields::dateDisplayFormat();
18+
1519
return DatePicker::make($customField->getFieldName())
1620
->native(false)
1721
->format('Y-m-d')
18-
->displayFormat('Y-m-d')
19-
->placeholder('YYYY-MM-DD');
22+
->displayFormat($configuredFormat ?? 'Y-m-d')
23+
->placeholder($configuredFormat ? Carbon::now()->format($configuredFormat) : 'YYYY-MM-DD');
2024
}
2125
}

src/Filament/Integration/Components/Forms/DateTimeComponent.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,22 @@
44

55
namespace Relaticle\CustomFields\Filament\Integration\Components\Forms;
66

7+
use Carbon\Carbon;
78
use Filament\Forms\Components\DateTimePicker;
9+
use Relaticle\CustomFields\CustomFields;
810
use Relaticle\CustomFields\Filament\Integration\Base\AbstractFormComponent;
911
use Relaticle\CustomFields\Models\CustomField;
1012

1113
final readonly class DateTimeComponent extends AbstractFormComponent
1214
{
1315
public function create(CustomField $customField): DateTimePicker
1416
{
17+
$configuredFormat = CustomFields::dateTimeDisplayFormat();
18+
1519
return DateTimePicker::make($customField->getFieldName())
1620
->native(false)
1721
->format('Y-m-d H:i:s')
18-
->displayFormat('Y-m-d H:i:s')
19-
->placeholder('YYYY-MM-DD HH:MM:SS');
22+
->displayFormat($configuredFormat ?? 'Y-m-d H:i:s')
23+
->placeholder($configuredFormat ? Carbon::now()->format($configuredFormat) : 'YYYY-MM-DD HH:MM:SS');
2024
}
2125
}

src/Filament/Integration/Components/Infolists/DateTimeEntry.php

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,28 @@
55
namespace Relaticle\CustomFields\Filament\Integration\Components\Infolists;
66

77
use Filament\Infolists\Components\TextEntry;
8+
use Relaticle\CustomFields\CustomFields;
89
use Relaticle\CustomFields\Filament\Integration\Base\AbstractInfolistEntry;
910
use Relaticle\CustomFields\Models\CustomField;
1011

1112
final class DateTimeEntry extends AbstractInfolistEntry
1213
{
1314
public function make(CustomField $customField): TextEntry
1415
{
15-
return TextEntry::make($customField->getFieldName())
16-
->dateTime('Y-m-d H:i:s')
17-
->placeholder('Y-m-d H:i:s')
16+
$isDateTime = $customField->isDateTimeField();
17+
18+
$format = $isDateTime
19+
? (CustomFields::dateTimeDisplayFormat() ?? 'Y-m-d H:i:s')
20+
: (CustomFields::dateDisplayFormat() ?? 'Y-m-d');
21+
22+
$entry = TextEntry::make($customField->getFieldName())
23+
->placeholder($format);
24+
25+
$isDateTime
26+
? $entry->dateTime($format)
27+
: $entry->date($format);
28+
29+
return $entry
1830
->label($customField->name)
1931
->state(fn (mixed $record) => $record->getCustomFieldValue($customField));
2032
}

src/Filament/Integration/Components/Tables/Columns/DateTimeColumn.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Closure;
88
use Filament\Tables\Columns\Column as BaseColumn;
99
use Filament\Tables\Columns\TextColumn as BaseTextColumn;
10+
use Relaticle\CustomFields\CustomFields;
1011
use Relaticle\CustomFields\Filament\Integration\Base\AbstractTableColumn;
1112
use Relaticle\CustomFields\Filament\Integration\Concerns\Tables\ConfiguresColumnLabel;
1213
use Relaticle\CustomFields\Filament\Integration\Concerns\Tables\ConfiguresSearchable;
@@ -36,12 +37,16 @@ public function make(CustomField $customField): BaseColumn
3637
$value = $this->locale->call($this, $value);
3738
}
3839

39-
if ($value && $customField->type === 'date_time') {
40-
return $value->format('M j, Y H:i');
40+
if ($value && $customField->isDateTimeField()) {
41+
$format = CustomFields::dateTimeDisplayFormat() ?? 'M j, Y H:i';
42+
43+
return $value->format($format);
4144
}
4245

43-
if ($value && $customField->type === 'date') {
44-
return $value->format('M j, Y');
46+
if ($value && $customField->isDateField()) {
47+
$format = CustomFields::dateDisplayFormat() ?? 'M j, Y';
48+
49+
return $value->format($format);
4550
}
4651

4752
return $value;

src/Validation/Rules/DateConstraintRule.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Closure;
99
use Illuminate\Contracts\Validation\ValidationRule;
1010
use Illuminate\Database\Eloquent\Model;
11+
use Relaticle\CustomFields\CustomFields;
1112
use Relaticle\CustomFields\Data\DateConstraintValue;
1213

1314
final readonly class DateConstraintRule implements ValidationRule
@@ -49,7 +50,7 @@ public function validate(string $attribute, mixed $value, Closure $fail): void
4950

5051
private function getMessage(Carbon $boundary): string
5152
{
52-
$formattedDate = $boundary->format('M j, Y');
53+
$formattedDate = $boundary->format(CustomFields::dateDisplayFormat() ?? 'M j, Y');
5354

5455
return match ($this->comparison) {
5556
'after_or_equal' => sprintf('The :attribute must be on or after %s.', $formattedDate),

0 commit comments

Comments
 (0)