mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2025-07-24 15:29:36 +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';
|
||||
let modal: HTMLDialogElement;
|
||||
import { t } from 'svelte-i18n';
|
||||
// @ts-ignore
|
||||
import { DateTime } from 'luxon';
|
||||
import {
|
||||
toLocalDatetime,
|
||||
toUTCDatetime,
|
||||
updateLocalDates,
|
||||
updateUTCDates,
|
||||
validateDateRange,
|
||||
formatUTCDate
|
||||
} from '$lib/dateUtils';
|
||||
|
||||
// Initialize with browser's timezone
|
||||
let selectedTimezone: string = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||
|
@ -30,21 +36,6 @@
|
|||
|
||||
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
|
||||
let transportation: Transportation = {
|
||||
id: transportationToEdit?.id || '',
|
||||
|
@ -88,24 +79,13 @@
|
|||
|
||||
// Update local display dates whenever timezone or UTC dates change
|
||||
$: {
|
||||
if (utcStartDate) {
|
||||
localStartDate = toLocalDatetime(utcStartDate, selectedTimezone);
|
||||
}
|
||||
if (utcEndDate) {
|
||||
localEndDate = toLocalDatetime(utcEndDate, selectedTimezone);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
const updatedDates = updateLocalDates({
|
||||
utcStartDate,
|
||||
utcEndDate,
|
||||
timezone: selectedTimezone
|
||||
});
|
||||
localStartDate = updatedDates.localStartDate;
|
||||
localEndDate = updatedDates.localEndDate;
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
|
@ -117,13 +97,20 @@
|
|||
// Initialize UTC dates from transportationToEdit if available
|
||||
if (transportationToEdit?.date) {
|
||||
utcStartDate = transportationToEdit.date;
|
||||
localStartDate = toLocalDatetime(utcStartDate);
|
||||
}
|
||||
|
||||
if (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() {
|
||||
|
@ -137,12 +124,18 @@
|
|||
}
|
||||
|
||||
// Update UTC dates when local dates change
|
||||
function updateUTCDates() {
|
||||
utcStartDate = localStartDate ? toUTCDatetime(localStartDate) : null;
|
||||
utcEndDate = localEndDate ? toUTCDatetime(localEndDate) : null;
|
||||
function handleLocalDateChange() {
|
||||
const updated = updateUTCDates({
|
||||
localStartDate,
|
||||
localEndDate,
|
||||
timezone: selectedTimezone
|
||||
});
|
||||
utcStartDate = updated.utcStartDate;
|
||||
utcEndDate = updated.utcEndDate;
|
||||
}
|
||||
|
||||
async function geocode(e: Event | null) {
|
||||
// Geocoding logic unchanged
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
@ -226,7 +219,7 @@
|
|||
Math.round(transportation.destination_longitude * 1e6) / 1e6;
|
||||
}
|
||||
|
||||
// Validate dates
|
||||
// Validate dates using utility function
|
||||
if (localEndDate && !localStartDate) {
|
||||
addToast('error', $t('adventures.start_date_required'));
|
||||
return;
|
||||
|
@ -238,11 +231,9 @@
|
|||
utcEndDate = utcStartDate;
|
||||
}
|
||||
|
||||
if (
|
||||
localStartDate &&
|
||||
localEndDate &&
|
||||
DateTime.fromISO(localStartDate).toMillis() > DateTime.fromISO(localEndDate).toMillis()
|
||||
) {
|
||||
// Validate date range
|
||||
const validation = validateDateRange(localStartDate, localEndDate);
|
||||
if (!validation.valid) {
|
||||
addToast('error', $t('adventures.start_before_end_error'));
|
||||
return;
|
||||
}
|
||||
|
@ -272,9 +263,15 @@
|
|||
// Update the UTC dates with the values from the server
|
||||
utcStartDate = data.date;
|
||||
utcEndDate = data.end_date;
|
||||
// Update displayed dates
|
||||
localStartDate = toLocalDatetime(utcStartDate);
|
||||
localEndDate = toLocalDatetime(utcEndDate);
|
||||
|
||||
// Update displayed dates using utility function
|
||||
const updatedDates = updateLocalDates({
|
||||
utcStartDate,
|
||||
utcEndDate,
|
||||
timezone: selectedTimezone
|
||||
});
|
||||
localStartDate = updatedDates.localStartDate;
|
||||
localEndDate = updatedDates.localEndDate;
|
||||
|
||||
addToast('success', $t('adventures.adventure_created'));
|
||||
dispatch('save', transportation);
|
||||
|
@ -296,9 +293,15 @@
|
|||
// Update the UTC dates with the values from the server
|
||||
utcStartDate = data.date;
|
||||
utcEndDate = data.end_date;
|
||||
// Update displayed dates
|
||||
localStartDate = toLocalDatetime(utcStartDate);
|
||||
localEndDate = toLocalDatetime(utcEndDate);
|
||||
|
||||
// Update displayed dates using utility function
|
||||
const updatedDates = updateLocalDates({
|
||||
utcStartDate,
|
||||
utcEndDate,
|
||||
timezone: selectedTimezone
|
||||
});
|
||||
localStartDate = updatedDates.localStartDate;
|
||||
localEndDate = updatedDates.localEndDate;
|
||||
|
||||
addToast('success', $t('adventures.adventure_updated'));
|
||||
dispatch('save', transportation);
|
||||
|
@ -481,7 +484,7 @@
|
|||
id="date"
|
||||
name="date"
|
||||
bind:value={localStartDate}
|
||||
on:change={updateUTCDates}
|
||||
on:change={handleLocalDateChange}
|
||||
min={constrainDates ? fullStartDate : ''}
|
||||
max={constrainDates ? fullEndDate : ''}
|
||||
class="input input-bordered w-full max-w-xs mt-1"
|
||||
|
@ -502,7 +505,7 @@
|
|||
min={constrainDates ? localStartDate : ''}
|
||||
max={constrainDates ? fullEndDate : ''}
|
||||
bind:value={localEndDate}
|
||||
on:change={updateUTCDates}
|
||||
on:change={handleLocalDateChange}
|
||||
class="input input-bordered w-full max-w-xs mt-1"
|
||||
/>
|
||||
</div>
|
||||
|
@ -529,15 +532,14 @@
|
|||
</div>
|
||||
{#if utcStartDate}
|
||||
<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}
|
||||
to {DateTime.fromISO(utcEndDate).toISO().slice(0, 16).replace('T', ' ')}
|
||||
to {formatUTCDate(utcEndDate)}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Flight Information -->
|
||||
|
||||
<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