feat(post-form): native datetime-local picker + require date client-side

The stock Grav `datetime` field template is deprecated and falls back to a
plain text box, so `type: datetime` + `default: now` rendered a raw input
showing the literal word "now" — unusable. Add a theme override at
templates/forms/fields/datetime/datetime.html.twig that renders a native
<input type="datetime-local"> (real calendar+clock, great on mobile), drop
the `default: now`, and prefill the current local time from post-form.js.

Also add `date` to the existing client-side validator. Together with the
picker (which can't hold an invalid value) this stops a bad/empty date from
round-tripping to the server — which was the trigger that made Grav re-render
the managed FilePond field from the session flash as filename-only inputs and
resurrect a photo the user had removed. Grav still reformats the submitted
value to the blueprint `format: 'Y-m-d H:i'` on save, so stored dates and
folder slugs are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-04 20:41:49 +02:00
co-authored by Claude Opus 4.8
parent d17d1256ba
commit 1cde137daa
4 changed files with 58 additions and 18 deletions
File diff suppressed because one or more lines are too long
+19 -1
View File
@@ -433,11 +433,29 @@ function initGeo() {
* Replaces the template's inline validator. Reads the EasyMDE-synced content
* value (initEditor keeps the textarea current) and shows per-field messages.
*/
// Current local wall-clock time as the value a datetime-local input expects
// ("YYYY-MM-DDTHH:MM"). Uses the browser's timezone — the server has no idea
// where the traveller is — and strips seconds so the value round-trips cleanly.
function localNowDatetime() {
var d = new Date();
d.setSeconds(0, 0);
d.setMinutes(d.getMinutes() - d.getTimezoneOffset());
return d.toISOString().slice(0, 16);
}
function initValidation() {
var REQUIRED = { title: 'Title', content: 'Content' };
var REQUIRED = { title: 'Title', date: 'Date & time', content: 'Content' };
var form = document.querySelector('form[name="new-entry"]');
if (!form) return;
// Prefill the date/time picker with "now" (local) unless draft restore
// already put a value there. initDraft runs before initValidation in boot(),
// so an empty field here means there was no saved draft date to keep.
var dateEl = field('date');
if (dateEl && !String(dateEl.value).trim()) {
dateEl.value = localNowDatetime();
}
function clearErrors() {
form.querySelectorAll('.field-error').forEach(function (el) { el.remove(); });
form.querySelectorAll('.field-invalid').forEach(function (el) { el.classList.remove('field-invalid'); });