mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2025-07-19 12:59:36 +02:00
feat: Add "all day" localization strings for multiple languages and enhance transportation date handling
This commit is contained in:
parent
9fd2a142cb
commit
1dc8e10758
16 changed files with 118 additions and 48 deletions
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
let fullStartDate: string = '';
|
let fullStartDate: string = '';
|
||||||
let fullEndDate: string = '';
|
let fullEndDate: string = '';
|
||||||
let allDay: boolean = false;
|
let allDay: boolean = true;
|
||||||
|
|
||||||
// Set full start and end dates from collection
|
// Set full start and end dates from collection
|
||||||
if (collection && collection.start_date && collection.end_date) {
|
if (collection && collection.start_date && collection.end_date) {
|
||||||
|
@ -698,13 +698,13 @@
|
||||||
on:change={() => (constrainDates = !constrainDates)}
|
on:change={() => (constrainDates = !constrainDates)}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
<span class="label-text">All Day</span>
|
<span class="label-text">{$t('adventures.all_day')}</span>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
class="toggle toggle-primary"
|
class="toggle toggle-primary"
|
||||||
id="constrain_dates"
|
id="constrain_dates"
|
||||||
name="constrain_dates"
|
name="constrain_dates"
|
||||||
on:change={() => (allDay = !allDay)}
|
bind:checked={allDay}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<div class="flex gap-2 mb-1">
|
<div class="flex gap-2 mb-1">
|
||||||
|
@ -782,30 +782,31 @@
|
||||||
}}
|
}}
|
||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
{#if !allDay}
|
||||||
<div role="alert" class="alert shadow-lg bg-neutral mt-2 mb-2">
|
<div role="alert" class="alert shadow-lg bg-neutral mt-2 mb-2">
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
fill="none"
|
fill="none"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
class="stroke-info h-6 w-6 shrink-0"
|
class="stroke-info h-6 w-6 shrink-0"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
stroke-width="2"
|
stroke-width="2"
|
||||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||||
></path>
|
></path>
|
||||||
</svg>
|
</svg>
|
||||||
<span>
|
<span>
|
||||||
{$t('lodging.current_timezone')}:
|
{$t('lodging.current_timezone')}:
|
||||||
{(() => {
|
{(() => {
|
||||||
const tz = new Intl.DateTimeFormat().resolvedOptions().timeZone;
|
const tz = new Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||||
const [continent, city] = tz.split('/');
|
const [continent, city] = tz.split('/');
|
||||||
return `${continent} (${city.replace('_', ' ')})`;
|
return `${continent} (${city.replace('_', ' ')})`;
|
||||||
})()}
|
})()}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<button type="button" class="btn btn-neutral" on:click={addNewVisit}
|
<button type="button" class="btn btn-neutral" on:click={addNewVisit}
|
||||||
|
|
|
@ -124,7 +124,7 @@
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="font-medium text-sm">{$t('adventures.dates')}:</span>
|
<span class="font-medium text-sm">{$t('adventures.dates')}:</span>
|
||||||
<p>
|
<p>
|
||||||
{new Date(lodging.check_in).toLocaleString('en-US', {
|
{new Date(lodging.check_in).toLocaleString(undefined, {
|
||||||
month: 'short',
|
month: 'short',
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
|
@ -132,7 +132,7 @@
|
||||||
minute: 'numeric'
|
minute: 'numeric'
|
||||||
})}
|
})}
|
||||||
-
|
-
|
||||||
{new Date(lodging.check_out).toLocaleString('en-US', {
|
{new Date(lodging.check_out).toLocaleString(undefined, {
|
||||||
month: 'short',
|
month: 'short',
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
|
|
|
@ -7,7 +7,15 @@
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import DeleteWarning from './DeleteWarning.svelte';
|
import DeleteWarning from './DeleteWarning.svelte';
|
||||||
// import ArrowDownThick from '~icons/mdi/arrow-down-thick';
|
// import ArrowDownThick from '~icons/mdi/arrow-down-thick';
|
||||||
|
import { TRANSPORTATION_TYPES_ICONS } from '$lib';
|
||||||
|
|
||||||
|
function getTransportationIcon(type: string) {
|
||||||
|
if (type in TRANSPORTATION_TYPES_ICONS) {
|
||||||
|
return TRANSPORTATION_TYPES_ICONS[type as keyof typeof TRANSPORTATION_TYPES_ICONS];
|
||||||
|
} else {
|
||||||
|
return '🚗';
|
||||||
|
}
|
||||||
|
}
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
export let transportation: Transportation;
|
export let transportation: Transportation;
|
||||||
|
@ -106,7 +114,9 @@
|
||||||
<h2 class="card-title text-lg font-semibold truncate">{transportation.name}</h2>
|
<h2 class="card-title text-lg font-semibold truncate">{transportation.name}</h2>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<div class="badge badge-secondary">
|
<div class="badge badge-secondary">
|
||||||
{$t(`transportation.modes.${transportation.type}`)}
|
{$t(`transportation.modes.${transportation.type}`) +
|
||||||
|
' ' +
|
||||||
|
getTransportationIcon(transportation.type)}
|
||||||
</div>
|
</div>
|
||||||
{#if transportation.type == 'plane' && transportation.flight_number}
|
{#if transportation.type == 'plane' && transportation.flight_number}
|
||||||
<div class="badge badge-neutral-200">{transportation.flight_number}</div>
|
<div class="badge badge-neutral-200">{transportation.flight_number}</div>
|
||||||
|
@ -128,7 +138,7 @@
|
||||||
{#if transportation.date}
|
{#if transportation.date}
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="font-medium text-sm">{$t('adventures.start')}:</span>
|
<span class="font-medium text-sm">{$t('adventures.start')}:</span>
|
||||||
<p>{new Date(transportation.date).toLocaleString(undefined, { timeZone: 'UTC' })}</p>
|
<p>{new Date(transportation.date).toLocaleString()}</p>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@ -146,7 +156,7 @@
|
||||||
{#if transportation.end_date}
|
{#if transportation.end_date}
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="font-medium text-sm">{$t('adventures.end')}:</span>
|
<span class="font-medium text-sm">{$t('adventures.end')}:</span>
|
||||||
<p>{new Date(transportation.end_date).toLocaleString(undefined, { timeZone: 'UTC' })}</p>
|
<p>{new Date(transportation.end_date).toLocaleString()}</p>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -16,10 +16,15 @@
|
||||||
|
|
||||||
let constrainDates: boolean = false;
|
let constrainDates: boolean = false;
|
||||||
|
|
||||||
|
// Format date as local datetime
|
||||||
|
// Convert an ISO date to a datetime-local value in local time.
|
||||||
function toLocalDatetime(value: string | null): string {
|
function toLocalDatetime(value: string | null): string {
|
||||||
if (!value) return '';
|
if (!value) return '';
|
||||||
const date = new Date(value);
|
const date = new Date(value);
|
||||||
return date.toISOString().slice(0, 16); // Format: YYYY-MM-DDTHH:mm
|
// Adjust the time by subtracting the timezone offset.
|
||||||
|
date.setMinutes(date.getMinutes() - date.getTimezoneOffset());
|
||||||
|
// Return format YYYY-MM-DDTHH:mm
|
||||||
|
return date.toISOString().slice(0, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
let transportation: Transportation = {
|
let transportation: Transportation = {
|
||||||
|
@ -185,6 +190,14 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert local dates to UTC
|
||||||
|
if (transportation.date && !transportation.date.includes('Z')) {
|
||||||
|
transportation.date = new Date(transportation.date).toISOString();
|
||||||
|
}
|
||||||
|
if (transportation.end_date && !transportation.end_date.includes('Z')) {
|
||||||
|
transportation.end_date = new Date(transportation.end_date).toISOString();
|
||||||
|
}
|
||||||
|
|
||||||
if (transportation.type != 'plane') {
|
if (transportation.type != 'plane') {
|
||||||
transportation.flight_number = '';
|
transportation.flight_number = '';
|
||||||
}
|
}
|
||||||
|
@ -422,6 +435,29 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
<div role="alert" class="alert shadow-lg bg-neutral mt-4">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
class="stroke-info h-6 w-6 shrink-0"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
<span>
|
||||||
|
{$t('lodging.current_timezone')}:
|
||||||
|
{(() => {
|
||||||
|
const tz = new Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||||
|
const [continent, city] = tz.split('/');
|
||||||
|
return `${continent} (${city.replace('_', ' ')})`;
|
||||||
|
})()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -338,6 +338,17 @@ export let LODGING_TYPES_ICONS = {
|
||||||
other: '❓'
|
other: '❓'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export let TRANSPORTATION_TYPES_ICONS = {
|
||||||
|
car: '🚗',
|
||||||
|
plane: '✈️',
|
||||||
|
train: '🚆',
|
||||||
|
bus: '🚌',
|
||||||
|
boat: '⛵',
|
||||||
|
bike: '🚲',
|
||||||
|
walking: '🚶',
|
||||||
|
other: '❓'
|
||||||
|
};
|
||||||
|
|
||||||
// Helper to check if a given date string represents midnight (all-day)
|
// Helper to check if a given date string represents midnight (all-day)
|
||||||
export function isAllDay(dateStr: string | string[]) {
|
export function isAllDay(dateStr: string | string[]) {
|
||||||
// Checks for the pattern "T00:00:00.000Z"
|
// Checks for the pattern "T00:00:00.000Z"
|
||||||
|
|
|
@ -247,7 +247,8 @@
|
||||||
"price": "Preis",
|
"price": "Preis",
|
||||||
"reservation_number": "Reservierungsnummer",
|
"reservation_number": "Reservierungsnummer",
|
||||||
"welcome_map_info": "Frei zugängliche Abenteuer auf diesem Server",
|
"welcome_map_info": "Frei zugängliche Abenteuer auf diesem Server",
|
||||||
"open_in_maps": "In Karten öffnen"
|
"open_in_maps": "In Karten öffnen",
|
||||||
|
"all_day": "Den ganzen Tag"
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"desc_1": "Entdecken, planen und erkunden Sie mühelos",
|
"desc_1": "Entdecken, planen und erkunden Sie mühelos",
|
||||||
|
|
|
@ -250,6 +250,7 @@
|
||||||
"show_map": "Show Map",
|
"show_map": "Show Map",
|
||||||
"emoji_picker": "Emoji Picker",
|
"emoji_picker": "Emoji Picker",
|
||||||
"download_calendar": "Download Calendar",
|
"download_calendar": "Download Calendar",
|
||||||
|
"all_day": "All Day",
|
||||||
"date_information": "Date Information",
|
"date_information": "Date Information",
|
||||||
"flight_information": "Flight Information",
|
"flight_information": "Flight Information",
|
||||||
"out_of_range": "Not in itinerary date range",
|
"out_of_range": "Not in itinerary date range",
|
||||||
|
|
|
@ -295,7 +295,8 @@
|
||||||
"region": "Región",
|
"region": "Región",
|
||||||
"reservation_number": "Número de reserva",
|
"reservation_number": "Número de reserva",
|
||||||
"welcome_map_info": "Aventuras públicas en este servidor",
|
"welcome_map_info": "Aventuras públicas en este servidor",
|
||||||
"open_in_maps": "Abrir en mapas"
|
"open_in_maps": "Abrir en mapas",
|
||||||
|
"all_day": "Todo el día"
|
||||||
},
|
},
|
||||||
"worldtravel": {
|
"worldtravel": {
|
||||||
"all": "Todo",
|
"all": "Todo",
|
||||||
|
|
|
@ -247,7 +247,8 @@
|
||||||
"region": "Région",
|
"region": "Région",
|
||||||
"reservation_number": "Numéro de réservation",
|
"reservation_number": "Numéro de réservation",
|
||||||
"welcome_map_info": "Aventures publiques sur ce serveur",
|
"welcome_map_info": "Aventures publiques sur ce serveur",
|
||||||
"open_in_maps": "Ouvert dans les cartes"
|
"open_in_maps": "Ouvert dans les cartes",
|
||||||
|
"all_day": "Toute la journée"
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"desc_1": "Découvrez, planifiez et explorez en toute simplicité",
|
"desc_1": "Découvrez, planifiez et explorez en toute simplicité",
|
||||||
|
|
|
@ -247,7 +247,8 @@
|
||||||
"region": "Regione",
|
"region": "Regione",
|
||||||
"welcome_map_info": "Avventure pubbliche su questo server",
|
"welcome_map_info": "Avventure pubbliche su questo server",
|
||||||
"reservation_number": "Numero di prenotazione",
|
"reservation_number": "Numero di prenotazione",
|
||||||
"open_in_maps": "Aperto in mappe"
|
"open_in_maps": "Aperto in mappe",
|
||||||
|
"all_day": "Tutto il giorno"
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"desc_1": "Scopri, pianifica ed esplora con facilità",
|
"desc_1": "Scopri, pianifica ed esplora con facilità",
|
||||||
|
|
|
@ -247,7 +247,8 @@
|
||||||
"region": "지역",
|
"region": "지역",
|
||||||
"reservation_number": "예약 번호",
|
"reservation_number": "예약 번호",
|
||||||
"welcome_map_info": "이 서버의 공개 모험",
|
"welcome_map_info": "이 서버의 공개 모험",
|
||||||
"open_in_maps": "지도에서 열립니다"
|
"open_in_maps": "지도에서 열립니다",
|
||||||
|
"all_day": "하루 종일"
|
||||||
},
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"both_passwords_required": "두 암호 모두 필요합니다",
|
"both_passwords_required": "두 암호 모두 필요합니다",
|
||||||
|
|
|
@ -247,7 +247,8 @@
|
||||||
"lodging_information": "Informatie overliggen",
|
"lodging_information": "Informatie overliggen",
|
||||||
"price": "Prijs",
|
"price": "Prijs",
|
||||||
"region": "Regio",
|
"region": "Regio",
|
||||||
"open_in_maps": "Open in kaarten"
|
"open_in_maps": "Open in kaarten",
|
||||||
|
"all_day": "De hele dag"
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"desc_1": "Ontdek, plan en verken met gemak",
|
"desc_1": "Ontdek, plan en verken met gemak",
|
||||||
|
|
|
@ -295,7 +295,8 @@
|
||||||
"region": "Region",
|
"region": "Region",
|
||||||
"reservation_number": "Numer rezerwacji",
|
"reservation_number": "Numer rezerwacji",
|
||||||
"welcome_map_info": "Publiczne przygody na tym serwerze",
|
"welcome_map_info": "Publiczne przygody na tym serwerze",
|
||||||
"open_in_maps": "Otwarte w mapach"
|
"open_in_maps": "Otwarte w mapach",
|
||||||
|
"all_day": "Cały dzień"
|
||||||
},
|
},
|
||||||
"worldtravel": {
|
"worldtravel": {
|
||||||
"country_list": "Lista krajów",
|
"country_list": "Lista krajów",
|
||||||
|
|
|
@ -247,7 +247,8 @@
|
||||||
"price": "Pris",
|
"price": "Pris",
|
||||||
"region": "Område",
|
"region": "Område",
|
||||||
"reservation_number": "Bokningsnummer",
|
"reservation_number": "Bokningsnummer",
|
||||||
"open_in_maps": "Kappas in"
|
"open_in_maps": "Kappas in",
|
||||||
|
"all_day": "Hela dagen"
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"desc_1": "Upptäck, planera och utforska med lätthet",
|
"desc_1": "Upptäck, planera och utforska med lätthet",
|
||||||
|
|
|
@ -295,7 +295,8 @@
|
||||||
"lodging_information": "住宿信息",
|
"lodging_information": "住宿信息",
|
||||||
"price": "价格",
|
"price": "价格",
|
||||||
"reservation_number": "预订号",
|
"reservation_number": "预订号",
|
||||||
"open_in_maps": "在地图上打开"
|
"open_in_maps": "在地图上打开",
|
||||||
|
"all_day": "整天"
|
||||||
},
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"forgot_password": "忘记密码?",
|
"forgot_password": "忘记密码?",
|
||||||
|
|
|
@ -456,12 +456,14 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<a
|
{#if adventure.longitude && adventure.latitude}
|
||||||
class="btn btn-neutral btn-sm max-w-32"
|
<a
|
||||||
href={`https://maps.apple.com/?q=${adventure.latitude},${adventure.longitude}`}
|
class="btn btn-neutral btn-sm max-w-32"
|
||||||
target="_blank"
|
href={`https://maps.apple.com/?q=${adventure.latitude},${adventure.longitude}`}
|
||||||
rel="noopener noreferrer">{$t('adventures.open_in_maps')}</a
|
target="_blank"
|
||||||
>
|
rel="noopener noreferrer">{$t('adventures.open_in_maps')}</a
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
<MapLibre
|
<MapLibre
|
||||||
style="https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json"
|
style="https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json"
|
||||||
class="flex items-center self-center justify-center aspect-[9/16] max-h-[70vh] sm:aspect-video sm:max-h-full w-10/12 rounded-lg"
|
class="flex items-center self-center justify-center aspect-[9/16] max-h-[70vh] sm:aspect-video sm:max-h-full w-10/12 rounded-lg"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue