Enhance event creation with locale-aware date formatting and improved translations

This commit is contained in:
MartinBraquet
2026-02-18 21:02:26 +01:00
parent dbdaa9d7ec
commit 28f33da6d0
5 changed files with 116 additions and 16 deletions

View File

@@ -8,7 +8,7 @@ import {api} from 'web/lib/api'
import {APIError} from "common/api/utils";
import clsx from "clsx";
import {Col} from "web/components/layout/col";
import {useT} from 'web/lib/locale';
import {useLocale, useT} from 'web/lib/locale';
import {Event} from 'web/hooks/use-events'
@@ -22,6 +22,7 @@ export function CreateEventModal(props: {
const {open, setOpen, onClose, onSuccess, event} = props
const isEditing = !!event
const t = useT()
const {locale} = useLocale()
const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
@@ -76,8 +77,8 @@ export function CreateEventModal(props: {
title: formData.title,
description: formData.description || undefined,
locationType: formData.locationType,
locationAddress: formData.locationAddress || undefined,
locationUrl: formData.locationUrl || undefined,
locationAddress: formData.locationType === 'in_person' && formData.locationAddress || undefined,
locationUrl: formData.locationType === 'online' && formData.locationUrl || undefined,
eventStartTime: formData.eventStartTime!.toISOString(),
eventEndTime: formData.eventEndTime
? formData.eventEndTime.toISOString()
@@ -91,8 +92,8 @@ export function CreateEventModal(props: {
title: formData.title,
description: formData.description || undefined,
locationType: formData.locationType,
locationAddress: formData.locationAddress || undefined,
locationUrl: formData.locationUrl || undefined,
locationAddress: formData.locationType === 'in_person' && formData.locationAddress || undefined,
locationUrl: formData.locationType === 'online' && formData.locationUrl || undefined,
eventStartTime: formData.eventStartTime!.toISOString(),
eventEndTime: formData.eventEndTime
? formData.eventEndTime.toISOString()
@@ -136,6 +137,9 @@ export function CreateEventModal(props: {
setFormData((prev) => ({...prev, [name]: value}))
}
const dateFormat = locale === 'en' ? "MMM d, yyyy h:mm aa" : "dd MMM yyyy, HH:mm"
const timeFormat = "HH:mm"
return (
<Modal open={open} setOpen={setOpen} onClose={onClose} size="lg">
<Col className={clsx("", MODAL_CLASS)}>
@@ -231,10 +235,11 @@ export function CreateEventModal(props: {
<div className="grid grid-cols-1 xs:grid-cols-2 gap-4">
<div>
<label className="mb-1 block text-sm font-medium">
{t('events.start_time', 'Start Time')} *
{t('events.start_time', 'Start')} *
</label>
<DatePicker
selected={formData.eventStartTime}
locale={locale}
onChange={(date: Date | null) => {
if (!date) return
setFormData((prev) => {
@@ -245,21 +250,22 @@ export function CreateEventModal(props: {
})
}}
showTimeSelect
timeFormat="HH:mm"
timeFormat={timeFormat}
timeIntervals={15}
dateFormat="MMM d, yyyy h:mm aa"
dateFormat={dateFormat}
minDate={new Date()}
required
placeholderText="Select date and time"
placeholderText={t('events.select_start_datetime', 'Select date and time')}
className="bg-canvas-50 border-canvas-300 focus:border-primary-500 focus:ring-primary-500 w-full rounded-md border px-3 py-2"
/>
</div>
<div>
<label className="mb-1 block text-sm font-medium">
{t('events.end_time', 'End Time')}
{t('events.end_time', 'End')}
</label>
<DatePicker
selected={formData.eventEndTime}
locale={locale}
onChange={(date: Date | null) => {
setFormData((prev) => {
const startTime = prev.eventStartTime
@@ -271,11 +277,11 @@ export function CreateEventModal(props: {
})
}}
showTimeSelect
timeFormat="HH:mm"
timeFormat={timeFormat}
timeIntervals={15}
dateFormat="MMM d, yyyy h:mm aa"
dateFormat={dateFormat}
minDate={formData.eventStartTime || new Date()}
placeholderText="Select end time (optional)"
placeholderText={t('events.select_end_datetime', 'Select end time (optional)')}
className="bg-canvas-50 border-canvas-300 focus:border-primary-500 focus:ring-primary-500 w-full rounded-md border px-3 py-2"
/>
</div>

View File

@@ -6,6 +6,94 @@ import {getLocale} from "web/lib/locale-cookie";
dayjs.extend(relativeTime)
dayjs.extend(localizedFormat)
export const DATEPICKER_LOCALE_IMPORTS: Record<string, () => Promise<any>> = {
af: () => import('date-fns/locale/af'),
ar: () => import('date-fns/locale/ar'),
'ar-dz': () => import('date-fns/locale/ar-DZ'),
'ar-ma': () => import('date-fns/locale/ar-MA'),
'ar-sa': () => import('date-fns/locale/ar-SA'),
'ar-tn': () => import('date-fns/locale/ar-TN'),
az: () => import('date-fns/locale/az'),
be: () => import('date-fns/locale/be'),
bg: () => import('date-fns/locale/bg'),
bn: () => import('date-fns/locale/bn'),
bs: () => import('date-fns/locale/bs'),
ca: () => import('date-fns/locale/ca'),
cs: () => import('date-fns/locale/cs'),
cy: () => import('date-fns/locale/cy'),
da: () => import('date-fns/locale/da'),
de: () => import('date-fns/locale/de'),
'de-at': () => import('date-fns/locale/de-AT'),
el: () => import('date-fns/locale/el'),
en: () => import('date-fns/locale/en-US'),
'en-au': () => import('date-fns/locale/en-AU'),
'en-ca': () => import('date-fns/locale/en-CA'),
'en-gb': () => import('date-fns/locale/en-GB'),
'en-ie': () => import('date-fns/locale/en-IE'),
'en-in': () => import('date-fns/locale/en-IN'),
'en-nz': () => import('date-fns/locale/en-NZ'),
eo: () => import('date-fns/locale/eo'),
es: () => import('date-fns/locale/es'),
et: () => import('date-fns/locale/et'),
eu: () => import('date-fns/locale/eu'),
fi: () => import('date-fns/locale/fi'),
fr: () => import('date-fns/locale/fr'),
'fr-ca': () => import('date-fns/locale/fr-CA'),
'fr-ch': () => import('date-fns/locale/fr-CH'),
fy: () => import('date-fns/locale/fy'),
gd: () => import('date-fns/locale/gd'),
gl: () => import('date-fns/locale/gl'),
gu: () => import('date-fns/locale/gu'),
he: () => import('date-fns/locale/he'),
hi: () => import('date-fns/locale/hi'),
hr: () => import('date-fns/locale/hr'),
ht: () => import('date-fns/locale/ht'),
hu: () => import('date-fns/locale/hu'),
id: () => import('date-fns/locale/id'),
is: () => import('date-fns/locale/is'),
it: () => import('date-fns/locale/it'),
'it-ch': () => import('date-fns/locale/it-CH'),
ja: () => import('date-fns/locale/ja'),
ka: () => import('date-fns/locale/ka'),
kk: () => import('date-fns/locale/kk'),
km: () => import('date-fns/locale/km'),
kn: () => import('date-fns/locale/kn'),
ko: () => import('date-fns/locale/ko'),
lb: () => import('date-fns/locale/lb'),
lt: () => import('date-fns/locale/lt'),
lv: () => import('date-fns/locale/lv'),
mk: () => import('date-fns/locale/mk'),
mn: () => import('date-fns/locale/mn'),
ms: () => import('date-fns/locale/ms'),
mt: () => import('date-fns/locale/mt'),
nb: () => import('date-fns/locale/nb'),
nl: () => import('date-fns/locale/nl'),
'nl-be': () => import('date-fns/locale/nl-BE'),
nn: () => import('date-fns/locale/nn'),
pl: () => import('date-fns/locale/pl'),
pt: () => import('date-fns/locale/pt'),
'pt-br': () => import('date-fns/locale/pt-BR'),
ro: () => import('date-fns/locale/ro'),
ru: () => import('date-fns/locale/ru'),
sk: () => import('date-fns/locale/sk'),
sl: () => import('date-fns/locale/sl'),
sq: () => import('date-fns/locale/sq'),
sr: () => import('date-fns/locale/sr'),
sv: () => import('date-fns/locale/sv'),
ta: () => import('date-fns/locale/ta'),
te: () => import('date-fns/locale/te'),
th: () => import('date-fns/locale/th'),
tr: () => import('date-fns/locale/tr'),
uk: () => import('date-fns/locale/uk'),
uz: () => import('date-fns/locale/uz'),
vi: () => import('date-fns/locale/vi'),
zh: () => import('date-fns/locale/zh-CN'),
'zh-cn': () => import('date-fns/locale/zh-CN'),
'zh-hk': () => import('date-fns/locale/zh-HK'),
'zh-tw': () => import('date-fns/locale/zh-TW'),
zh_tw: () => import('date-fns/locale/zh-TW'),
}
export const DAYJS_LOCALE_IMPORTS: Record<string, () => Promise<unknown>> = {
af: () => import('dayjs/locale/af'),
am: () => import('dayjs/locale/am'),
@@ -154,5 +242,6 @@ export const DAYJS_LOCALE_IMPORTS: Record<string, () => Promise<unknown>> = {
}
DAYJS_LOCALE_IMPORTS[getLocale()]?.()
DATEPICKER_LOCALE_IMPORTS[getLocale()]?.()
export default dayjs

View File

@@ -556,6 +556,8 @@
"events.location_url": "Orts-URL",
"events.start_time": "Startzeit",
"events.end_time": "Endzeit",
"events.select_start_datetime": "Datum und Uhrzeit auswählen",
"events.select_end_datetime": "Endzeit auswählen (optional)",
"events.max_participants": "Max. Teilnehmer (optional)",
"events.leave_empty": "Leer lassen für unbegrenzt",
"events.in_person": "In Person",

View File

@@ -554,8 +554,10 @@
"events.location_type": "Type de lieu",
"events.location_address": "Adresse du lieu",
"events.location_url": "URL du lieu",
"events.start_time": "Heure de début",
"events.end_time": "Heure de fin",
"events.start_time": "Début",
"events.end_time": "Fin",
"events.select_start_datetime": "Sélectionner la date",
"events.select_end_datetime": "Sélectionner (optionnel)",
"events.max_participants": "Participants max (optionnel)",
"events.leave_empty": "Laisser vide pour illimité",
"events.in_person": "En personne",

View File

@@ -24,7 +24,7 @@ import {I18nContext} from "web/lib/locale"
import {HiddenProfilesProvider} from 'web/hooks/use-hidden-profiles'
import {updateStatusBar} from "web/hooks/use-theme"
import {useFontPreferenceManager} from "web/hooks/use-font-preference"
import {DAYJS_LOCALE_IMPORTS} from "web/lib/dayjs";
import {DATEPICKER_LOCALE_IMPORTS, DAYJS_LOCALE_IMPORTS} from "web/lib/dayjs";
import 'web/lib/dayjs'
if (Capacitor.isNativePlatform()) {
@@ -104,6 +104,7 @@ function MyApp(props: AppProps<PageProps>) {
setLocaleState(newLocale)
resetCachedLocale()
DAYJS_LOCALE_IMPORTS[newLocale]?.()
DATEPICKER_LOCALE_IMPORTS[newLocale]?.()
}
useEffect(() => {