mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2025-07-25 07:49:37 +02:00
feat: Refactor date handling in TransportationModal and add utility functions for date conversion and validation
This commit is contained in:
parent
6942f5e1bb
commit
c12f94855d
2 changed files with 173 additions and 57 deletions
|
@ -6,8 +6,14 @@
|
||||||
import { addToast } from '$lib/toasts';
|
import { addToast } from '$lib/toasts';
|
||||||
let modal: HTMLDialogElement;
|
let modal: HTMLDialogElement;
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
// @ts-ignore
|
import {
|
||||||
import { DateTime } from 'luxon';
|
toLocalDatetime,
|
||||||
|
toUTCDatetime,
|
||||||
|
updateLocalDates,
|
||||||
|
updateUTCDates,
|
||||||
|
validateDateRange,
|
||||||
|
formatUTCDate
|
||||||
|
} from '$lib/dateUtils';
|
||||||
|
|
||||||
// Initialize with browser's timezone
|
// Initialize with browser's timezone
|
||||||
let selectedTimezone: string = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
let selectedTimezone: string = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||||
|
@ -30,21 +36,6 @@
|
||||||
|
|
||||||
let constrainDates: boolean = false;
|
let constrainDates: boolean = false;
|
||||||
|
|
||||||
// Convert a UTC ISO date to a datetime-local value in the specified timezone
|
|
||||||
function toLocalDatetime(utcDate: string | null, timezone: string = selectedTimezone): string {
|
|
||||||
if (!utcDate) return '';
|
|
||||||
return DateTime.fromISO(utcDate, { zone: 'UTC' })
|
|
||||||
.setZone(timezone)
|
|
||||||
.toISO({ suppressSeconds: true, includeOffset: false })
|
|
||||||
.slice(0, 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert a local datetime to UTC
|
|
||||||
function toUTCDatetime(localDate: string, timezone: string = selectedTimezone): string | null {
|
|
||||||
if (!localDate) return null;
|
|
||||||
return DateTime.fromISO(localDate, { zone: timezone }).toUTC().toISO();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize transportation object
|
// Initialize transportation object
|
||||||
let transportation: Transportation = {
|
let transportation: Transportation = {
|
||||||
id: transportationToEdit?.id || '',
|
id: transportationToEdit?.id || '',
|
||||||
|
@ -88,24 +79,13 @@
|
||||||
|
|
||||||
// Update local display dates whenever timezone or UTC dates change
|
// Update local display dates whenever timezone or UTC dates change
|
||||||
$: {
|
$: {
|
||||||
if (utcStartDate) {
|
const updatedDates = updateLocalDates({
|
||||||
localStartDate = toLocalDatetime(utcStartDate, selectedTimezone);
|
utcStartDate,
|
||||||
}
|
utcEndDate,
|
||||||
if (utcEndDate) {
|
timezone: selectedTimezone
|
||||||
localEndDate = toLocalDatetime(utcEndDate, selectedTimezone);
|
});
|
||||||
}
|
localStartDate = updatedDates.localStartDate;
|
||||||
}
|
localEndDate = updatedDates.localEndDate;
|
||||||
|
|
||||||
// Explicitly watch for timezone changes to update displayed dates
|
|
||||||
$: {
|
|
||||||
// This will trigger whenever selectedTimezone changes
|
|
||||||
selectedTimezone;
|
|
||||||
if (utcStartDate) {
|
|
||||||
localStartDate = toLocalDatetime(utcStartDate);
|
|
||||||
}
|
|
||||||
if (utcEndDate) {
|
|
||||||
localEndDate = toLocalDatetime(utcEndDate);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
|
@ -117,13 +97,20 @@
|
||||||
// Initialize UTC dates from transportationToEdit if available
|
// Initialize UTC dates from transportationToEdit if available
|
||||||
if (transportationToEdit?.date) {
|
if (transportationToEdit?.date) {
|
||||||
utcStartDate = transportationToEdit.date;
|
utcStartDate = transportationToEdit.date;
|
||||||
localStartDate = toLocalDatetime(utcStartDate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transportationToEdit?.end_date) {
|
if (transportationToEdit?.end_date) {
|
||||||
utcEndDate = transportationToEdit.end_date;
|
utcEndDate = transportationToEdit.end_date;
|
||||||
localEndDate = toLocalDatetime(utcEndDate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update local dates based on UTC dates
|
||||||
|
const updatedDates = updateLocalDates({
|
||||||
|
utcStartDate,
|
||||||
|
utcEndDate,
|
||||||
|
timezone: selectedTimezone
|
||||||
|
});
|
||||||
|
localStartDate = updatedDates.localStartDate;
|
||||||
|
localEndDate = updatedDates.localEndDate;
|
||||||
});
|
});
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
|
@ -137,12 +124,18 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update UTC dates when local dates change
|
// Update UTC dates when local dates change
|
||||||
function updateUTCDates() {
|
function handleLocalDateChange() {
|
||||||
utcStartDate = localStartDate ? toUTCDatetime(localStartDate) : null;
|
const updated = updateUTCDates({
|
||||||
utcEndDate = localEndDate ? toUTCDatetime(localEndDate) : null;
|
localStartDate,
|
||||||
|
localEndDate,
|
||||||
|
timezone: selectedTimezone
|
||||||
|
});
|
||||||
|
utcStartDate = updated.utcStartDate;
|
||||||
|
utcEndDate = updated.utcEndDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function geocode(e: Event | null) {
|
async function geocode(e: Event | null) {
|
||||||
|
// Geocoding logic unchanged
|
||||||
if (e) {
|
if (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
|
@ -226,7 +219,7 @@
|
||||||
Math.round(transportation.destination_longitude * 1e6) / 1e6;
|
Math.round(transportation.destination_longitude * 1e6) / 1e6;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate dates
|
// Validate dates using utility function
|
||||||
if (localEndDate && !localStartDate) {
|
if (localEndDate && !localStartDate) {
|
||||||
addToast('error', $t('adventures.start_date_required'));
|
addToast('error', $t('adventures.start_date_required'));
|
||||||
return;
|
return;
|
||||||
|
@ -238,11 +231,9 @@
|
||||||
utcEndDate = utcStartDate;
|
utcEndDate = utcStartDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
// Validate date range
|
||||||
localStartDate &&
|
const validation = validateDateRange(localStartDate, localEndDate);
|
||||||
localEndDate &&
|
if (!validation.valid) {
|
||||||
DateTime.fromISO(localStartDate).toMillis() > DateTime.fromISO(localEndDate).toMillis()
|
|
||||||
) {
|
|
||||||
addToast('error', $t('adventures.start_before_end_error'));
|
addToast('error', $t('adventures.start_before_end_error'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -272,9 +263,15 @@
|
||||||
// Update the UTC dates with the values from the server
|
// Update the UTC dates with the values from the server
|
||||||
utcStartDate = data.date;
|
utcStartDate = data.date;
|
||||||
utcEndDate = data.end_date;
|
utcEndDate = data.end_date;
|
||||||
// Update displayed dates
|
|
||||||
localStartDate = toLocalDatetime(utcStartDate);
|
// Update displayed dates using utility function
|
||||||
localEndDate = toLocalDatetime(utcEndDate);
|
const updatedDates = updateLocalDates({
|
||||||
|
utcStartDate,
|
||||||
|
utcEndDate,
|
||||||
|
timezone: selectedTimezone
|
||||||
|
});
|
||||||
|
localStartDate = updatedDates.localStartDate;
|
||||||
|
localEndDate = updatedDates.localEndDate;
|
||||||
|
|
||||||
addToast('success', $t('adventures.adventure_created'));
|
addToast('success', $t('adventures.adventure_created'));
|
||||||
dispatch('save', transportation);
|
dispatch('save', transportation);
|
||||||
|
@ -296,9 +293,15 @@
|
||||||
// Update the UTC dates with the values from the server
|
// Update the UTC dates with the values from the server
|
||||||
utcStartDate = data.date;
|
utcStartDate = data.date;
|
||||||
utcEndDate = data.end_date;
|
utcEndDate = data.end_date;
|
||||||
// Update displayed dates
|
|
||||||
localStartDate = toLocalDatetime(utcStartDate);
|
// Update displayed dates using utility function
|
||||||
localEndDate = toLocalDatetime(utcEndDate);
|
const updatedDates = updateLocalDates({
|
||||||
|
utcStartDate,
|
||||||
|
utcEndDate,
|
||||||
|
timezone: selectedTimezone
|
||||||
|
});
|
||||||
|
localStartDate = updatedDates.localStartDate;
|
||||||
|
localEndDate = updatedDates.localEndDate;
|
||||||
|
|
||||||
addToast('success', $t('adventures.adventure_updated'));
|
addToast('success', $t('adventures.adventure_updated'));
|
||||||
dispatch('save', transportation);
|
dispatch('save', transportation);
|
||||||
|
@ -481,7 +484,7 @@
|
||||||
id="date"
|
id="date"
|
||||||
name="date"
|
name="date"
|
||||||
bind:value={localStartDate}
|
bind:value={localStartDate}
|
||||||
on:change={updateUTCDates}
|
on:change={handleLocalDateChange}
|
||||||
min={constrainDates ? fullStartDate : ''}
|
min={constrainDates ? fullStartDate : ''}
|
||||||
max={constrainDates ? fullEndDate : ''}
|
max={constrainDates ? fullEndDate : ''}
|
||||||
class="input input-bordered w-full max-w-xs mt-1"
|
class="input input-bordered w-full max-w-xs mt-1"
|
||||||
|
@ -502,7 +505,7 @@
|
||||||
min={constrainDates ? localStartDate : ''}
|
min={constrainDates ? localStartDate : ''}
|
||||||
max={constrainDates ? fullEndDate : ''}
|
max={constrainDates ? fullEndDate : ''}
|
||||||
bind:value={localEndDate}
|
bind:value={localEndDate}
|
||||||
on:change={updateUTCDates}
|
on:change={handleLocalDateChange}
|
||||||
class="input input-bordered w-full max-w-xs mt-1"
|
class="input input-bordered w-full max-w-xs mt-1"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -529,15 +532,14 @@
|
||||||
</div>
|
</div>
|
||||||
{#if utcStartDate}
|
{#if utcStartDate}
|
||||||
<div class="text-sm mt-2">
|
<div class="text-sm mt-2">
|
||||||
UTC Time: {DateTime.fromISO(utcStartDate).toISO().slice(0, 16).replace('T', ' ')}
|
UTC Time: {formatUTCDate(utcStartDate)}
|
||||||
{#if utcEndDate && utcEndDate !== utcStartDate}
|
{#if utcEndDate && utcEndDate !== utcStartDate}
|
||||||
to {DateTime.fromISO(utcEndDate).toISO().slice(0, 16).replace('T', ' ')}
|
to {formatUTCDate(utcEndDate)}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Flight Information -->
|
<!-- Flight Information -->
|
||||||
|
|
||||||
<div class="collapse collapse-plus bg-base-200 mb-4">
|
<div class="collapse collapse-plus bg-base-200 mb-4">
|
||||||
|
|
114
frontend/src/lib/dateUtils.ts
Normal file
114
frontend/src/lib/dateUtils.ts
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
// @ts-ignore
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a UTC ISO date to a datetime-local value in the specified timezone
|
||||||
|
* @param utcDate - UTC date in ISO format or null
|
||||||
|
* @param timezone - Target timezone (defaults to browser timezone)
|
||||||
|
* @returns Formatted local datetime string for input fields (YYYY-MM-DDTHH:MM)
|
||||||
|
*/
|
||||||
|
export function toLocalDatetime(
|
||||||
|
utcDate: string | null,
|
||||||
|
timezone: string = Intl.DateTimeFormat().resolvedOptions().timeZone
|
||||||
|
): string {
|
||||||
|
if (!utcDate) return '';
|
||||||
|
return DateTime.fromISO(utcDate, { zone: 'UTC' })
|
||||||
|
.setZone(timezone)
|
||||||
|
.toISO({ suppressSeconds: true, includeOffset: false })
|
||||||
|
.slice(0, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a local datetime to UTC
|
||||||
|
* @param localDate - Local datetime string in ISO format
|
||||||
|
* @param timezone - Source timezone (defaults to browser timezone)
|
||||||
|
* @returns UTC datetime in ISO format or null
|
||||||
|
*/
|
||||||
|
export function toUTCDatetime(
|
||||||
|
localDate: string,
|
||||||
|
timezone: string = Intl.DateTimeFormat().resolvedOptions().timeZone
|
||||||
|
): string | null {
|
||||||
|
if (!localDate) return null;
|
||||||
|
return DateTime.fromISO(localDate, { zone: timezone }).toUTC().toISO();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates local datetime display values based on UTC values and timezone
|
||||||
|
* @param params Object containing UTC dates and timezone
|
||||||
|
* @returns Object with updated local datetime strings
|
||||||
|
*/
|
||||||
|
export function updateLocalDates({
|
||||||
|
utcStartDate,
|
||||||
|
utcEndDate,
|
||||||
|
timezone
|
||||||
|
}: {
|
||||||
|
utcStartDate: string | null;
|
||||||
|
utcEndDate: string | null;
|
||||||
|
timezone: string;
|
||||||
|
}) {
|
||||||
|
return {
|
||||||
|
localStartDate: toLocalDatetime(utcStartDate, timezone),
|
||||||
|
localEndDate: toLocalDatetime(utcEndDate, timezone)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates UTC datetime values based on local values and timezone
|
||||||
|
* @param params Object containing local dates and timezone
|
||||||
|
* @returns Object with updated UTC datetime strings
|
||||||
|
*/
|
||||||
|
export function updateUTCDates({
|
||||||
|
localStartDate,
|
||||||
|
localEndDate,
|
||||||
|
timezone
|
||||||
|
}: {
|
||||||
|
localStartDate: string;
|
||||||
|
localEndDate: string;
|
||||||
|
timezone: string;
|
||||||
|
}) {
|
||||||
|
return {
|
||||||
|
utcStartDate: toUTCDatetime(localStartDate, timezone),
|
||||||
|
utcEndDate: toUTCDatetime(localEndDate, timezone)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate date ranges
|
||||||
|
* @param startDate - Start date string
|
||||||
|
* @param endDate - End date string
|
||||||
|
* @returns Object with validation result and error message
|
||||||
|
*/
|
||||||
|
export function validateDateRange(
|
||||||
|
startDate: string,
|
||||||
|
endDate: string
|
||||||
|
): { valid: boolean; error?: string } {
|
||||||
|
if (endDate && !startDate) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
error: 'Start date is required when end date is provided'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
startDate &&
|
||||||
|
endDate &&
|
||||||
|
DateTime.fromISO(startDate).toMillis() > DateTime.fromISO(endDate).toMillis()
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
error: 'Start date must be before end date'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return { valid: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format UTC date for display
|
||||||
|
* @param utcDate - UTC date in ISO format
|
||||||
|
* @returns Formatted date string without seconds (YYYY-MM-DD HH:MM)
|
||||||
|
*/
|
||||||
|
export function formatUTCDate(utcDate: string | null): string {
|
||||||
|
if (!utcDate) return '';
|
||||||
|
return DateTime.fromISO(utcDate).toISO().slice(0, 16).replace('T', ' ');
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue