diff --git a/frontend/src/components/admin/InstallmentsPanel.tsx b/frontend/src/components/admin/InstallmentsPanel.tsx index 39c2c8af..e735274c 100644 --- a/frontend/src/components/admin/InstallmentsPanel.tsx +++ b/frontend/src/components/admin/InstallmentsPanel.tsx @@ -26,10 +26,9 @@ import React, { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { Trash2, Plus } from 'lucide-react'; -import { Button, Input } from '../common'; +import { Button, Input, LocalizedDateInput } from '../common'; import type { PaymentTermInstallment } from '../../services/quotes.service'; import { useInstallmentDefaults } from '../../hooks/useInstallmentDefaults'; -import { useLocalizedDate } from '../../hooks/useLocalizedDate'; export type InstallmentPlan = PaymentTermInstallment[]; @@ -69,7 +68,6 @@ export const InstallmentsPanel: React.FC = ({ value, onChange, onValidityChange, eventDate, disabled, }) => { const { t } = useTranslation(); - const { dateInputLang } = useLocalizedDate(); const defaults = useInstallmentDefaults(); const [advanced, setAdvanced] = React.useState(false); @@ -230,12 +228,9 @@ export const InstallmentsPanel: React.FC = ({ 'On delivery — admin releases manually. Switch to advanced to change.')} ) : ( - { - const next = e.target.value; + onChange={(next) => { if (!next) return; const offset = daysBetween(todayIso(), next); update(idx, { trigger: 'fixed_date', offset_days: offset }); diff --git a/frontend/src/pages/admin/bills/BillEditorPage.tsx b/frontend/src/pages/admin/bills/BillEditorPage.tsx index 81cfd8d3..228f5281 100644 --- a/frontend/src/pages/admin/bills/BillEditorPage.tsx +++ b/frontend/src/pages/admin/bills/BillEditorPage.tsx @@ -566,8 +566,31 @@ export const BillEditorPage: React.FC = () => { : t('bills.field.dueDateOverrideOff', 'Auto from send date + payment term — tick to set manually')} - setScheduledSendAt(e.target.value)} /> +
+ + {/* Localized date + time (honours general_date_format + + general_time_format) instead of a native datetime-local, which + renders in the browser locale (US date + 12h). Recombined into + the "YYYY-MM-DDTHH:MM" the payload + scheduler expect. */} +
+ { + if (!iso) { setScheduledSendAt(''); return; } + const time = scheduledSendAt.length >= 16 ? scheduledSendAt.slice(11, 16) : '09:00'; + setScheduledSendAt(`${iso}T${time}`); + }} + /> + = 16 ? scheduledSendAt.slice(11, 16) : ''} + onChange={(hhmm) => { + const date = scheduledSendAt ? scheduledSendAt.slice(0, 10) : ''; + if (!date) return; + setScheduledSendAt(`${date}T${hhmm || '09:00'}`); + }} + /> +
+
setProfile({ ...profile, defaultLocale: e.target.value })} /> - {/* Migration 137 — IANA timezone string for the admin calendar. - Free-text; backend caps at 64 chars. When blank the calendar - UI falls back to the browser's `Intl.DateTimeFormat() - .resolvedOptions().timeZone`. */} - setProfile({ ...profile, timezone: e.target.value || null })} - /> + {/* Migration 137 — IANA timezone for the admin calendar + the + scheduled-email business-hours snapping. Dropdown of the full + IANA list; blank = fall back to the server/browser tz. */} +
+ + +
setProfile({ ...profile, vatLabel: e.target.value })} />