Date Fixes, Translations, Misc Bugs (#840)

* Translated using Weblate (Spanish)

Currently translated at 100.0% (956 of 956 strings)

Translation: AdventureLog/Web App
Translate-URL: https://hosted.weblate.org/projects/adventurelog/web-app/es/

* Added translation using Weblate (English (United States))

* Translated using Weblate (Norwegian Bokmål)

Currently translated at 100.0% (956 of 956 strings)

Translation: AdventureLog/Web App
Translate-URL: https://hosted.weblate.org/projects/adventurelog/web-app/nb_NO/

* Remove empty English (United States) locale file

* Translated using Weblate (Spanish)

Currently translated at 100.0% (956 of 956 strings)

Translation: AdventureLog/Web App
Translate-URL: https://hosted.weblate.org/projects/adventurelog/web-app/es/

* [BUG]Ordered Itinerary includes visits that are outside itinerary date range
Fixes #746

* [BUG] Server Error (500) when trying to access the API docs
Fixes #712

* [BUG] Single day Collections will think location visits are out of date range
Fixes #827

* Fixes #654

* Translated using Weblate (Spanish)

Currently translated at 100.0% (956 of 956 strings)

Translation: AdventureLog/Web App
Translate-URL: https://hosted.weblate.org/projects/adventurelog/web-app/es/

* Added Slovak translations (#815)

* Created sk.json

* Update Navbar.svelte

* Update +layout.svelte

---------

Co-authored-by: Sean Morley <98704938+seanmorley15@users.noreply.github.com>

* Implement code changes to enhance functionality and improve performance

---------

Co-authored-by: Nikolai Eidsheim <nikolai.eidsheim@gmail.com>
Co-authored-by: Sergio <garcia.sergio@me.com>
Co-authored-by: fantastron27 <fantastron27@gmail.com>
This commit is contained in:
Sean Morley
2025-09-06 21:45:40 -04:00
committed by GitHub
parent 4a53b1fdfd
commit 96dfda1cfb
31 changed files with 5343 additions and 4242 deletions

View File

@@ -36,4 +36,8 @@ EMAIL_BACKEND='console'
# PGDATABASE='adventurelog'
# PGUSER='admin'
# PGPASSWORD='admin'
# For Sean's use:
# re-sync the development branch with main after doing squash merges
# git fetch origin && git checkout development && git reset --hard origin/main && git push origin development --force
# ------------------- #

View File

@@ -26,4 +26,5 @@ geopy==2.4.1
psutil==6.1.1
geojson==3.2.0
gpxpy==1.6.2
pymemcache==4.0.0
pymemcache==4.0.0
legacy-cgi==2.6.3

View File

@@ -9,29 +9,18 @@
import TrashCan from '~icons/mdi/trash-can';
import Calendar from '~icons/mdi/calendar';
import DeleteWarning from './DeleteWarning.svelte';
import { isEntityOutsideCollectionDateRange } from '$lib/dateUtils';
export let checklist: Checklist;
export let user: User | null = null;
export let collection: Collection | null = null;
export let collection: Collection;
let isWarningModalOpen: boolean = false;
let unlinked: boolean = false;
let outsideCollectionRange: boolean = false;
$: {
if (collection?.start_date && collection.end_date) {
const startOutsideRange =
checklist.date &&
collection.start_date < checklist.date &&
collection.end_date < checklist.date;
const endOutsideRange =
checklist.date &&
collection.start_date > checklist.date &&
collection.end_date > checklist.date;
unlinked = !!(startOutsideRange || endOutsideRange || !checklist.date);
}
outsideCollectionRange = isEntityOutsideCollectionDateRange(checklist, collection);
}
function editChecklist() {
@@ -71,7 +60,7 @@
<h2 class="text-xl font-bold break-words">{checklist.name}</h2>
<div class="flex flex-wrap gap-2">
<div class="badge badge-primary">{$t('adventures.checklist')}</div>
{#if unlinked}
{#if outsideCollectionRange}
<div class="badge badge-error">{$t('adventures.out_of_range')}</div>
{/if}
</div>

View File

@@ -14,7 +14,7 @@
let items: ChecklistItem[] = [];
let constrainDates: boolean = false;
let constrainDates: boolean = true;
items = checklist?.items || [];

View File

@@ -22,6 +22,7 @@
import StarOutline from '~icons/mdi/star-outline';
import Eye from '~icons/mdi/eye';
import EyeOff from '~icons/mdi/eye-off';
import { isEntityOutsideCollectionDateRange } from '$lib/dateUtils';
export let type: string | null = null;
export let user: User | null;
@@ -48,17 +49,13 @@
}
}
let unlinked: boolean = false;
let outsideCollectionRange: boolean = false;
// Reactive block to update `unlinked` when dependencies change
$: {
if (collection && collection?.start_date && collection.end_date) {
unlinked = adventure.visits.every((visit) => {
if (!visit.start_date || !visit.end_date) return true;
const isBeforeVisit = collection.end_date && collection.end_date < visit.start_date;
const isAfterVisit = collection.start_date && collection.start_date > visit.end_date;
return isBeforeVisit || isAfterVisit;
});
if (collection) {
outsideCollectionRange = adventure.visits.every((visit) =>
isEntityOutsideCollectionDateRange(visit, collection)
);
}
}
@@ -199,7 +196,7 @@
>
{adventure.is_visited ? $t('adventures.visited') : $t('adventures.planned')}
</div>
{#if unlinked}
{#if outsideCollectionRange}
<div class="badge badge-sm badge-error shadow-lg">{$t('adventures.out_of_range')}</div>
{/if}
</div>

View File

@@ -7,7 +7,7 @@
import { t } from 'svelte-i18n';
import DeleteWarning from './DeleteWarning.svelte';
import { LODGING_TYPES_ICONS } from '$lib';
import { formatDateInTimezone } from '$lib/dateUtils';
import { formatDateInTimezone, isEntityOutsideCollectionDateRange } from '$lib/dateUtils';
import { formatAllDayDate } from '$lib/dateUtils';
import { isAllDay } from '$lib';
import CardCarousel from './CardCarousel.svelte';
@@ -31,38 +31,11 @@
dispatch('edit', lodging);
}
let unlinked: boolean = false;
let outsideCollectionRange: boolean = false;
$: {
if (collection?.start_date && collection.end_date) {
// Parse transportation dates
let transportationStartDate = lodging.check_in
? new Date(lodging.check_in.split('T')[0]) // Ensure proper date parsing
: null;
let transportationEndDate = lodging.check_out
? new Date(lodging.check_out.split('T')[0])
: null;
// Parse collection dates
let collectionStartDate = new Date(collection.start_date);
let collectionEndDate = new Date(collection.end_date);
// Check if the collection range is outside the transportation range
const startOutsideRange =
transportationStartDate &&
collectionStartDate < transportationStartDate &&
collectionEndDate < transportationStartDate;
const endOutsideRange =
transportationEndDate &&
collectionStartDate > transportationEndDate &&
collectionEndDate > transportationEndDate;
unlinked = !!(
startOutsideRange ||
endOutsideRange ||
(!transportationStartDate && !transportationEndDate)
);
if (collection) {
outsideCollectionRange = isEntityOutsideCollectionDateRange(lodging, collection);
}
}
@@ -120,7 +93,7 @@
{$t(`lodging.${lodging.type}`)}
{getLodgingIcon(lodging.type)}
</div>
{#if unlinked}
{#if outsideCollectionRange}
<div class="badge badge-error">{$t('adventures.out_of_range')}</div>
{/if}
</div>

View File

@@ -63,7 +63,8 @@
ru: 'Русский',
ja: '日本語',
ar: 'العربية',
'pt-br': 'Português (Brasil)'
'pt-br': 'Português (Brasil)',
'sk': 'Slovenský'
};
const submitLocaleChange = (event: Event) => {

View File

@@ -15,23 +15,18 @@
import TrashCan from '~icons/mdi/trash-can';
import Calendar from '~icons/mdi/calendar';
import DeleteWarning from './DeleteWarning.svelte';
import { isEntityOutsideCollectionDateRange } from '$lib/dateUtils';
export let note: Note;
export let user: User | null = null;
export let collection: Collection | null = null;
let isWarningModalOpen: boolean = false;
let unlinked: boolean = false;
let outsideCollectionRange: boolean = false;
$: {
if (collection?.start_date && collection.end_date) {
const startOutsideRange =
note.date && collection.start_date < note.date && collection.end_date < note.date;
const endOutsideRange =
note.date && collection.start_date > note.date && collection.end_date > note.date;
unlinked = !!(startOutsideRange || endOutsideRange || !note.date);
if (collection) {
outsideCollectionRange = isEntityOutsideCollectionDateRange(note, collection);
}
}
@@ -73,7 +68,7 @@
<h2 class="text-xl font-bold break-words">{note.name}</h2>
<div class="flex flex-wrap gap-2">
<div class="badge badge-primary">{$t('adventures.note')}</div>
{#if unlinked}
{#if outsideCollectionRange}
<div class="badge badge-error">{$t('adventures.out_of_range')}</div>
{/if}
</div>

View File

@@ -17,7 +17,7 @@
export let collection: Collection;
export let user: User | null = null;
let constrainDates: boolean = false;
let constrainDates: boolean = true;
let isReadOnly =
!(note && user?.uuid == note?.user) &&

View File

@@ -8,7 +8,11 @@
import DeleteWarning from './DeleteWarning.svelte';
// import ArrowDownThick from '~icons/mdi/arrow-down-thick';
import { TRANSPORTATION_TYPES_ICONS } from '$lib';
import { formatAllDayDate, formatDateInTimezone } from '$lib/dateUtils';
import {
formatAllDayDate,
formatDateInTimezone,
isEntityOutsideCollectionDateRange
} from '$lib/dateUtils';
import { isAllDay } from '$lib';
import CardCarousel from './CardCarousel.svelte';
@@ -36,52 +40,11 @@
dispatch('edit', transportation);
}
let unlinked: boolean = false;
let outsideCollectionRange: boolean = false;
$: {
if (collection?.start_date && collection.end_date) {
// Parse transportation dates
let transportationStartDate = transportation.date
? new Date(transportation.date.split('T')[0]) // Ensure proper date parsing
: null;
let transportationEndDate = transportation.end_date
? new Date(transportation.end_date.split('T')[0])
: null;
// Parse collection dates
let collectionStartDate = new Date(collection.start_date);
let collectionEndDate = new Date(collection.end_date);
// // Debugging outputs
// console.log(
// 'Transportation Start Date:',
// transportationStartDate,
// 'Transportation End Date:',
// transportationEndDate
// );
// console.log(
// 'Collection Start Date:',
// collectionStartDate,
// 'Collection End Date:',
// collectionEndDate
// );
// Check if the collection range is outside the transportation range
const startOutsideRange =
transportationStartDate &&
collectionStartDate < transportationStartDate &&
collectionEndDate < transportationStartDate;
const endOutsideRange =
transportationEndDate &&
collectionStartDate > transportationEndDate &&
collectionEndDate > transportationEndDate;
unlinked = !!(
startOutsideRange ||
endOutsideRange ||
(!transportationStartDate && !transportationEndDate)
);
if (collection) {
outsideCollectionRange = isEntityOutsideCollectionDateRange(transportation, collection);
}
}
@@ -165,7 +128,7 @@
{#if transportation.type === 'plane' && transportation.flight_number}
<div class="badge badge-neutral">{transportation.flight_number}</div>
{/if}
{#if unlinked}
{#if outsideCollectionRange}
<div class="badge badge-error">{$t('adventures.out_of_range')}</div>
{/if}
</div>

View File

@@ -173,6 +173,10 @@
Math.round(transportation.destination_longitude * 1e6) / 1e6;
}
if (transportation.date && !transportation.end_date) {
transportation.end_date = transportation.date;
}
if (!transportation.type) {
transportation.type = 'other';
}

View File

@@ -28,6 +28,7 @@
import ArrowLeftIcon from '~icons/mdi/arrow-left';
import RunFastIcon from '~icons/mdi/run-fast';
import LoadingIcon from '~icons/mdi/loading';
import InfoIcon from '~icons/mdi/information';
import UploadIcon from '~icons/mdi/upload';
import FileIcon from '~icons/mdi/file';
import CloseIcon from '~icons/mdi/close';
@@ -1535,6 +1536,17 @@
</div>
</div>
<!-- if localStartDate and localEndDate are set, show a callout saying its not saved yet -->
{#if localStartDate || localEndDate}
<div class="alert alert-neutral">
<InfoIcon class="w-5 h-5" />
<div>
<div class="font-medium text-sm">{$t('adventures.dates_not_saved')}</div>
<div class="text-xs opacity-75">{$t('adventures.dates_not_saved_description')}</div>
</div>
</div>
{/if}
<div class="flex gap-3 justify-end pt-4">
<button class="btn btn-neutral-200 gap-2" on:click={handleBack}>
<ArrowLeftIcon class="w-5 h-5" />

View File

@@ -1,34 +1,27 @@
// @ts-ignore
import { DateTime } from 'luxon';
import type { Checklist, Collection, Lodging, Note, Transportation, Visit } from './types';
import { isAllDay } from '$lib';
/**
* 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 '';
const dt = DateTime.fromISO(utcDate, { zone: 'UTC' });
if (!dt.isValid) return '';
const isoString = dt.setZone(timezone).toISO({
suppressSeconds: true,
includeOffset: false
});
return isoString ? isoString.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,
@@ -36,15 +29,12 @@ export function toUTCDatetime(
allDay: boolean = false
): string | null {
if (!localDate) return null;
if (allDay) {
// Treat input as date-only, set UTC midnight manually
// Treat as date only, set UTC midnight
return DateTime.fromISO(localDate, { zone: 'UTC' })
.startOf('day')
.toISO({ suppressMilliseconds: true });
}
// Normal timezone conversion for datetime-local input
return DateTime.fromISO(localDate, { zone: timezone })
.toUTC()
.toISO({ suppressMilliseconds: true });
@@ -52,8 +42,6 @@ export function toUTCDatetime(
/**
* Updates local datetime values based on UTC date and timezone
* @param params Object containing UTC date and timezone
* @returns Object with updated local datetime string
*/
export function updateLocalDate({
utcDate,
@@ -62,15 +50,11 @@ export function updateLocalDate({
utcDate: string | null;
timezone: string;
}) {
return {
localDate: toLocalDatetime(utcDate, timezone)
};
return { localDate: toLocalDatetime(utcDate, timezone) };
}
/**
* Updates UTC datetime values based on local datetime and timezone
* @param params Object containing local date and timezone
* @returns Object with updated UTC datetime string
*/
export function updateUTCDate({
localDate,
@@ -81,40 +65,27 @@ export function updateUTCDate({
timezone: string;
allDay?: boolean;
}) {
return {
utcDate: toUTCDatetime(localDate, timezone, allDay)
};
return { utcDate: toUTCDatetime(localDate, timezone, allDay) };
}
/**
* Validate date ranges using UTC comparison
* @param startDate - Start date string in UTC (ISO format)
* @param endDate - End date string in UTC (ISO format)
* @returns Object with validation result and optional 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'
};
return { valid: false, error: 'Start date is required when end date is provided' };
}
if (
startDate &&
endDate &&
DateTime.fromISO(startDate, { zone: 'utc' }).toMillis() >
DateTime.fromISO(endDate, { zone: 'utc' }).toMillis()
) {
return {
valid: false,
error: 'Start date must be before end date (based on UTC)'
};
return { valid: false, error: 'Start date must be before end date (based on UTC)' };
}
return { valid: true };
}
@@ -135,11 +106,6 @@ export function formatDateInTimezone(utcDate: string, timezone: string | null):
}
}
/**
* 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 '';
const dateTime = DateTime.fromISO(utcDate);
@@ -147,18 +113,10 @@ export function formatUTCDate(utcDate: string | null): string {
return dateTime.toISO()?.slice(0, 16).replace('T', ' ') || '';
}
/**
* Format all-day date for display without timezone conversion
* @param dateString - Date string in ISO format (YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS)
* @returns Formatted date string (e.g., "Jun 1, 2025")
*/
export function formatAllDayDate(dateString: string): string {
if (!dateString) return '';
// Extract just the date part and add midday time to avoid timezone issues
const datePart = dateString.split('T')[0];
const dateWithMidday = `${datePart}T12:00:00`;
return new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'short',
@@ -166,6 +124,150 @@ export function formatAllDayDate(dateString: string): string {
}).format(new Date(dateWithMidday));
}
// ==== FIXED TIMEZONE-AWARE DATE RANGE LOGIC ====
/**
* Extracts start and end dates from various entity types (Luxon DateTime)
* Returns also isAllDay flag for correct comparison logic
*/
function getEntityDateRange(entity: Visit | Transportation | Lodging | Note | Checklist): {
start: DateTime | null;
end: DateTime | null;
isAllDay: boolean;
} {
let start: DateTime | null = null;
let end: DateTime | null = null;
let isAllDayEvent = false;
try {
let timezone = (entity as Visit).timezone || Intl.DateTimeFormat().resolvedOptions().timeZone;
if ('start_date' in entity && 'end_date' in entity) {
// Check if all-day (no time portion)
isAllDayEvent = isAllDay(entity.start_date) && isAllDay(entity.end_date);
console;
if (isAllDayEvent) {
start = entity.start_date
? DateTime.fromISO(entity.start_date.split('T')[0], { zone: 'UTC' }).startOf('day')
: null;
end = entity.end_date
? DateTime.fromISO(entity.end_date.split('T')[0], { zone: 'UTC' }).endOf('day')
: null;
} else {
start = DateTime.fromISO(entity.start_date, { zone: 'UTC' }).setZone(timezone);
end = DateTime.fromISO(entity.end_date, { zone: 'UTC' }).setZone(timezone);
}
} else if ('date' in entity && 'end_date' in entity) {
isAllDayEvent = !!(entity.date && entity.date.length === 10);
if (isAllDayEvent) {
start = DateTime.fromISO(entity.date, { zone: 'UTC' }).startOf('day');
end = DateTime.fromISO(entity.end_date, { zone: 'UTC' }).endOf('day');
} else {
start = DateTime.fromISO(entity.date, { zone: 'UTC' }).setZone(timezone);
end = DateTime.fromISO(entity.end_date, { zone: 'UTC' }).setZone(timezone);
}
} else if ('check_in' in entity && 'check_out' in entity) {
isAllDayEvent = !!(entity.check_in && entity.check_in.length === 10);
if (isAllDayEvent) {
start = DateTime.fromISO(entity.check_in, { zone: 'UTC' }).startOf('day');
end = DateTime.fromISO(entity.check_out, { zone: 'UTC' }).endOf('day');
} else {
start = DateTime.fromISO(entity.check_in, { zone: 'UTC' }).setZone(timezone);
end = DateTime.fromISO(entity.check_out, { zone: 'UTC' }).setZone(timezone);
}
} else if ('date' in entity) {
isAllDayEvent = !!(entity.date && entity.date.length === 10);
if (isAllDayEvent) {
start = DateTime.fromISO(entity.date, { zone: 'UTC' }).startOf('day');
end = start;
} else {
start = DateTime.fromISO(entity.date, { zone: 'UTC' }).setZone(timezone);
end = start;
}
}
} catch (error) {
console.error('Error processing entity dates:', error);
}
return { start, end, isAllDay: isAllDayEvent };
}
/**
* Extract collection start/end as Luxon DateTime w/allDay logic
*/
function getCollectionDateRange(collection: Collection): {
start: DateTime | null;
end: DateTime | null;
isAllDay: boolean;
} {
if (!collection.start_date || !collection.end_date) {
return { start: null, end: null, isAllDay: false };
}
// Assume collection always uses full datetimes in ISO string
const isAllDay = collection.start_date.length === 10 && collection.end_date.length === 10;
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const start = isAllDay
? DateTime.fromISO(collection.start_date, { zone: 'UTC' }).startOf('day')
: DateTime.fromISO(collection.start_date, { zone: 'UTC' }).setZone(timezone);
const end = isAllDay
? DateTime.fromISO(collection.end_date, { zone: 'UTC' }).endOf('day')
: DateTime.fromISO(collection.end_date, { zone: 'UTC' }).setZone(timezone);
return { start, end, isAllDay };
}
/**
* Checks if an entity falls within a collection's date range (timezone-safe, all-day-aware)
*/
export function isEntityInCollectionDateRange(
entity: Visit | Transportation | Lodging | Note | Checklist,
collection: Collection
): boolean {
if (!collection?.start_date || !collection.end_date) {
return false;
}
const { start: entityStart, end: entityEnd, isAllDay: entityAllDay } = getEntityDateRange(entity);
const {
start: collStart,
end: collEnd,
isAllDay: collAllDay
} = getCollectionDateRange(collection);
// If any dates are missing, don't match
if (!entityStart || !collStart) return false;
// If either side is all-day, use date comparison
if (entityAllDay || collAllDay) {
// Compare only date components
const entStartDate = entityStart.startOf('day');
const entEndDate = (entityEnd || entityStart).endOf('day');
const colStartDate = collStart.startOf('day');
const colEndDate = collEnd.endOf('day');
return entStartDate <= colEndDate && entEndDate >= colStartDate;
} else {
// Compare actual DateTimes
const entEnd = entityEnd || entityStart;
return entityStart <= collEnd && entEnd >= collStart;
}
}
export function isEntityOutsideCollectionDateRange(
entity: Visit | Transportation | Lodging | Note | Checklist,
collection: Collection
): boolean {
return !isEntityInCollectionDateRange(entity, collection);
}
export function getEntitiesInDateRange<
T extends Visit | Transportation | Lodging | Note | Checklist
>(entities: T[], collection: Collection): T[] {
return entities.filter((entity) => isEntityInCollectionDateRange(entity, collection));
}
export function getEntitiesOutsideDateRange<
T extends Visit | Transportation | Lodging | Note | Checklist
>(entities: T[], collection: Collection): T[] {
return entities.filter((entity) => isEntityOutsideCollectionDateRange(entity, collection));
}
export const VALID_TIMEZONES = [
'Africa/Abidjan',
'Africa/Accra',

View File

@@ -422,7 +422,9 @@
"recorded_sessions": "جلسات مسجلة",
"total_activities": "الأنشطة الكلية",
"total_climbed": "مجموع تسلق",
"total_distance": "إجمالي المسافة"
"total_distance": "إجمالي المسافة",
"dates_not_saved": "زيارة لم تتم إضافتها بعد",
"dates_not_saved_description": "انقر فوق إضافة زيارة إلى حفظ"
},
"auth": {
"confirm_password": "تأكيد كلمة المرور",

View File

@@ -423,7 +423,9 @@
"recorded_sessions": "Aufgenommene Sitzungen",
"total_activities": "Gesamtaktivitäten",
"total_climbed": "Total bestiegen",
"total_distance": "Gesamtabstand"
"total_distance": "Gesamtabstand",
"dates_not_saved": "Besuchen Sie noch nicht hinzugefügt",
"dates_not_saved_description": "Klicken Sie auf Besuchen Sie, um sie zu speichern"
},
"home": {
"desc_1": "Entdecken, planen und erkunden Sie mühelos",

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

@@ -423,7 +423,9 @@
"recorded_sessions": "Sessions enregistrées",
"total_activities": "Activités totales",
"total_climbed": "Total grimpé",
"total_distance": "Distance totale"
"total_distance": "Distance totale",
"dates_not_saved": "Visitez non encore ajouté",
"dates_not_saved_description": "Cliquez sur Ajouter une visite pour enregistrer"
},
"home": {
"desc_1": "Découvrez, planifiez et explorez en toute simplicité",

View File

@@ -423,7 +423,9 @@
"recorded_sessions": "Sessioni registrate",
"total_activities": "Attività totali",
"total_climbed": "Totale scalato",
"total_distance": "Distanza totale"
"total_distance": "Distanza totale",
"dates_not_saved": "Visitare non ancora aggiunto",
"dates_not_saved_description": "Fai clic su Aggiungi visita per salvare"
},
"home": {
"desc_1": "Scopri, pianifica ed esplora con facilità",

View File

@@ -422,7 +422,9 @@
"recorded_sessions": "録音されたセッション",
"total_activities": "総アクティビティ",
"total_climbed": "総登り",
"total_distance": "総距離"
"total_distance": "総距離",
"dates_not_saved": "まだ追加されていません",
"dates_not_saved_description": "[訪問の追加]をクリックして保存します"
},
"auth": {
"confirm_password": "パスワードを認証する",

View File

@@ -423,7 +423,9 @@
"recorded_sessions": "기록 된 세션",
"total_activities": "총 활동",
"total_climbed": "총계가 올라 갔다",
"total_distance": "총 거리"
"total_distance": "총 거리",
"dates_not_saved": "아직 추가되지 않았습니다",
"dates_not_saved_description": "저장을 위해 방문 추가를 클릭하십시오"
},
"auth": {
"confirm_password": "비밀번호 확인",

View File

@@ -423,7 +423,9 @@
"recorded_sessions": "Opgenomen sessies",
"total_activities": "Totale activiteiten",
"total_climbed": "Totaal geklommen",
"total_distance": "Totale afstand"
"total_distance": "Totale afstand",
"dates_not_saved": "Bezoek nog niet toegevoegd",
"dates_not_saved_description": "Klik op Bezoek toevoegen om op te slaan"
},
"home": {
"desc_1": "Ontdek, plan en verken met gemak",

View File

File diff suppressed because it is too large Load Diff

View File

@@ -476,7 +476,9 @@
"recorded_sessions": "Nagrane sesje",
"total_activities": "Całkowite działania",
"total_climbed": "Całkowita wspinana",
"total_distance": "Całkowita odległość"
"total_distance": "Całkowita odległość",
"dates_not_saved": "Wizyta jeszcze nie dodana",
"dates_not_saved_description": "Kliknij Dodaj wizytę, aby zapisać"
},
"worldtravel": {
"country_list": "Lista krajów",

View File

@@ -422,7 +422,9 @@
"recorded_sessions": "Sessões gravadas",
"total_activities": "Atividades totais",
"total_climbed": "Total escalou",
"total_distance": "Distância total"
"total_distance": "Distância total",
"dates_not_saved": "Visite ainda não adicionado",
"dates_not_saved_description": "Clique em Adicionar visita para salvar"
},
"auth": {
"confirm_password": "Confirme sua senha",

View File

File diff suppressed because it is too large Load Diff

1016
frontend/src/locales/sk.json Normal file
View File

File diff suppressed because it is too large Load Diff

View File

@@ -423,7 +423,9 @@
"recorded_sessions": "Inspelade sessioner",
"total_activities": "Totala aktiviteter",
"total_climbed": "Total stigning",
"total_distance": "Totalt avstånd"
"total_distance": "Totalt avstånd",
"dates_not_saved": "Besök ännu inte tillagd",
"dates_not_saved_description": "Klicka på Lägg till Besök för att spara"
},
"home": {
"desc_1": "Upptäck, planera och utforska med lätthet",

View File

@@ -476,7 +476,9 @@
"recorded_sessions": "记录的会议",
"total_activities": "总活动",
"total_climbed": "总攀登",
"total_distance": "总距离"
"total_distance": "总距离",
"dates_not_saved": "访问尚未添加",
"dates_not_saved_description": "点击添加访问以保存"
},
"auth": {
"forgot_password": "忘记密码?",

View File

@@ -20,6 +20,7 @@
register('ja', () => import('../locales/ja.json'));
register('ar', () => import('../locales/ar.json'));
register('pt-br', () => import('../locales/pt-br.json'));
register('sk', () => import('../locales/sk.json'));
let locales = [
'en',
@@ -36,7 +37,8 @@
'ru',
'ja',
'ar',
'pt-br'
'pt-br',
'sk'
];
if (browser) {

View File

@@ -389,6 +389,21 @@
});
}
$: filteredOrderedItems = orderedItems.filter((item) => {
if (!collection?.start_date || !collection?.end_date) {
return true; // If no date range is set, show all items
}
const collectionStart = new Date(collection.start_date);
const collectionEnd = new Date(collection.end_date);
const itemStart = new Date(item.start);
const itemEnd = new Date(item.end);
// Check if item overlaps with collection date range
// Item is included if it starts before collection ends AND ends after collection starts
return itemStart <= collectionEnd && itemEnd >= collectionStart;
});
$: {
numAdventures = adventures.length;
numVisited = adventures.filter((adventure) => adventure.is_visited).length;
@@ -1176,11 +1191,11 @@
<div class="flex flex-col items-center">
<div class="w-full max-w-4xl relative">
<!-- Vertical timeline line that spans the entire height -->
{#if orderedItems.length > 0}
{#if filteredOrderedItems.length > 0}
<div class="absolute left-8 top-0 bottom-0 w-1 bg-primary"></div>
{/if}
<ul class="relative">
{#each orderedItems as orderedItem, index}
{#each filteredOrderedItems as orderedItem, index}
<li class="relative pl-20 mb-8">
<!-- Timeline Icon -->
<div
@@ -1281,7 +1296,7 @@
</li>
{/each}
</ul>
{#if orderedItems.length === 0}
{#if filteredOrderedItems.length === 0}
<div class="alert alert-info">
<p class="text-center text-lg">{$t('adventures.no_ordered_items')}</p>
</div>
@@ -1360,7 +1375,7 @@
</Marker>
{/if}
{/each}
{#if lineData}
{#if lineData && collection.start_date && collection.end_date}
<GeoJSON data={lineData}>
<LineLayer
layout={{ 'line-cap': 'round', 'line-join': 'round' }}