{/if}
diff --git a/frontend/src/lib/index.ts b/frontend/src/lib/index.ts
index 70aef59..af68de2 100644
--- a/frontend/src/lib/index.ts
+++ b/frontend/src/lib/index.ts
@@ -70,23 +70,23 @@ export function groupAdventuresByDate(
// Initialize all days in the range
for (let i = 0; i < numberOfDays; i++) {
const currentDate = new Date(startDate);
- currentDate.setUTCDate(startDate.getUTCDate() + i);
- const dateString = currentDate.toISOString().split('T')[0];
+ currentDate.setDate(startDate.getDate() + i);
+ const dateString = getLocalDateString(currentDate);
groupedAdventures[dateString] = [];
}
adventures.forEach((adventure) => {
adventure.visits.forEach((visit) => {
if (visit.start_date) {
- const adventureDate = new Date(visit.start_date).toISOString().split('T')[0];
+ const adventureDate = getLocalDateString(new Date(visit.start_date));
if (visit.end_date) {
const endDate = new Date(visit.end_date).toISOString().split('T')[0];
// Loop through all days and include adventure if it falls within the range
for (let i = 0; i < numberOfDays; i++) {
const currentDate = new Date(startDate);
- currentDate.setUTCDate(startDate.getUTCDate() + i);
- const dateString = currentDate.toISOString().split('T')[0];
+ currentDate.setDate(startDate.getDate() + i);
+ const dateString = getLocalDateString(currentDate);
// Include the current day if it falls within the adventure date range
if (dateString >= adventureDate && dateString <= endDate) {
@@ -116,22 +116,22 @@ export function groupTransportationsByDate(
// Initialize all days in the range
for (let i = 0; i < numberOfDays; i++) {
const currentDate = new Date(startDate);
- currentDate.setUTCDate(startDate.getUTCDate() + i);
- const dateString = currentDate.toISOString().split('T')[0];
+ currentDate.setDate(startDate.getDate() + i);
+ const dateString = getLocalDateString(currentDate);
groupedTransportations[dateString] = [];
}
transportations.forEach((transportation) => {
if (transportation.date) {
- const transportationDate = new Date(transportation.date).toISOString().split('T')[0];
+ const transportationDate = getLocalDateString(new Date(transportation.date));
if (transportation.end_date) {
const endDate = new Date(transportation.end_date).toISOString().split('T')[0];
// Loop through all days and include transportation if it falls within the range
for (let i = 0; i < numberOfDays; i++) {
const currentDate = new Date(startDate);
- currentDate.setUTCDate(startDate.getUTCDate() + i);
- const dateString = currentDate.toISOString().split('T')[0];
+ currentDate.setDate(startDate.getDate() + i);
+ const dateString = getLocalDateString(currentDate);
// Include the current day if it falls within the transportation date range
if (dateString >= transportationDate && dateString <= endDate) {
@@ -150,6 +150,13 @@ export function groupTransportationsByDate(
return groupedTransportations;
}
+function getLocalDateString(date: Date): string {
+ const year = date.getFullYear();
+ const month = String(date.getMonth() + 1).padStart(2, '0'); // Months are 0-indexed
+ const day = String(date.getDate()).padStart(2, '0');
+ return `${year}-${month}-${day}`;
+}
+
export function groupLodgingByDate(
transportations: Lodging[],
startDate: Date,
@@ -157,35 +164,32 @@ export function groupLodgingByDate(
): Record {
const groupedTransportations: Record = {};
- // Initialize all days in the range
+ // Initialize all days in the range using local dates
for (let i = 0; i < numberOfDays; i++) {
const currentDate = new Date(startDate);
- currentDate.setUTCDate(startDate.getUTCDate() + i);
- const dateString = currentDate.toISOString().split('T')[0];
+ currentDate.setDate(startDate.getDate() + i);
+ const dateString = getLocalDateString(currentDate);
groupedTransportations[dateString] = [];
}
transportations.forEach((transportation) => {
if (transportation.check_in) {
- const transportationDate = new Date(transportation.check_in).toISOString().split('T')[0];
+ // Use local date string conversion
+ const transportationDate = getLocalDateString(new Date(transportation.check_in));
if (transportation.check_out) {
- const endDate = new Date(transportation.check_out).toISOString().split('T')[0];
+ const endDate = getLocalDateString(new Date(transportation.check_out));
- // Loop through all days and include transportation if it falls within the range
+ // Loop through all days and include transportation if it falls within the transportation date range
for (let i = 0; i < numberOfDays; i++) {
const currentDate = new Date(startDate);
- currentDate.setUTCDate(startDate.getUTCDate() + i);
- const dateString = currentDate.toISOString().split('T')[0];
+ currentDate.setDate(startDate.getDate() + i);
+ const dateString = getLocalDateString(currentDate);
- // Include the current day if it falls within the transportation date range
if (dateString >= transportationDate && dateString <= endDate) {
- if (groupedTransportations[dateString]) {
- groupedTransportations[dateString].push(transportation);
- }
+ groupedTransportations[dateString].push(transportation);
}
}
} else if (groupedTransportations[transportationDate]) {
- // If there's no end date, add transportation to the start date only
groupedTransportations[transportationDate].push(transportation);
}
}
@@ -201,19 +205,18 @@ export function groupNotesByDate(
): Record {
const groupedNotes: Record = {};
- // Initialize all days in the range
+ // Initialize all days in the range using local dates
for (let i = 0; i < numberOfDays; i++) {
const currentDate = new Date(startDate);
- currentDate.setUTCDate(startDate.getUTCDate() + i);
- const dateString = currentDate.toISOString().split('T')[0];
+ currentDate.setDate(startDate.getDate() + i);
+ const dateString = getLocalDateString(currentDate);
groupedNotes[dateString] = [];
}
notes.forEach((note) => {
if (note.date) {
- const noteDate = new Date(note.date).toISOString().split('T')[0];
-
- // Add note to the appropriate date group if it exists
+ // Use the date string as is since it's already in "YYYY-MM-DD" format.
+ const noteDate = note.date;
if (groupedNotes[noteDate]) {
groupedNotes[noteDate].push(note);
}
@@ -230,19 +233,18 @@ export function groupChecklistsByDate(
): Record {
const groupedChecklists: Record = {};
- // Initialize all days in the range
+ // Initialize all days in the range using local dates
for (let i = 0; i < numberOfDays; i++) {
const currentDate = new Date(startDate);
- currentDate.setUTCDate(startDate.getUTCDate() + i);
- const dateString = currentDate.toISOString().split('T')[0];
+ currentDate.setDate(startDate.getDate() + i);
+ const dateString = getLocalDateString(currentDate);
groupedChecklists[dateString] = [];
}
checklists.forEach((checklist) => {
if (checklist.date) {
- const checklistDate = new Date(checklist.date).toISOString().split('T')[0];
-
- // Add checklist to the appropriate date group if it exists
+ // Use the date string as is since it's already in "YYYY-MM-DD" format.
+ const checklistDate = checklist.date;
if (groupedChecklists[checklistDate]) {
groupedChecklists[checklistDate].push(checklist);
}
diff --git a/frontend/src/routes/collections/[id]/+page.svelte b/frontend/src/routes/collections/[id]/+page.svelte
index b6ffac3..1a69fdd 100644
--- a/frontend/src/routes/collections/[id]/+page.svelte
+++ b/frontend/src/routes/collections/[id]/+page.svelte
@@ -935,24 +935,25 @@
{@const dateString = adjustedDate.toISOString().split('T')[0]}
{@const dayAdventures =
- groupAdventuresByDate(adventures, new Date(collection.start_date), numberOfDays)[
+ groupAdventuresByDate(adventures, new Date(collection.start_date), numberOfDays + 1)[
dateString
] || []}
{@const dayTransportations =
groupTransportationsByDate(
transportations,
new Date(collection.start_date),
- numberOfDays
+ numberOfDays + 1
)[dateString] || []}
{@const dayLodging =
- groupLodgingByDate(lodging, new Date(collection.start_date), numberOfDays)[
+ groupLodgingByDate(lodging, new Date(collection.start_date), numberOfDays + 1)[
dateString
] || []}
{@const dayNotes =
- groupNotesByDate(notes, new Date(collection.start_date), numberOfDays)[dateString] ||
- []}
+ groupNotesByDate(notes, new Date(collection.start_date), numberOfDays + 1)[
+ dateString
+ ] || []}
{@const dayChecklists =
- groupChecklistsByDate(checklists, new Date(collection.start_date), numberOfDays)[
+ groupChecklistsByDate(checklists, new Date(collection.start_date), numberOfDays + 1)[
dateString
] || []}
From 771579ef3d83d158f6acaf53b2b6ccc7b8a5e996 Mon Sep 17 00:00:00 2001
From: Sean Morley
Date: Thu, 20 Mar 2025 10:26:41 -0400
Subject: [PATCH 06/79] feat: Improve adventure date grouping to handle all-day
events and enhance date formatting
---
frontend/src/lib/index.ts | 80 +++++++++++++++-------
frontend/src/routes/signup/+page.server.ts | 2 -
2 files changed, 56 insertions(+), 26 deletions(-)
diff --git a/frontend/src/lib/index.ts b/frontend/src/lib/index.ts
index af68de2..d66abec 100644
--- a/frontend/src/lib/index.ts
+++ b/frontend/src/lib/index.ts
@@ -78,26 +78,57 @@ export function groupAdventuresByDate(
adventures.forEach((adventure) => {
adventure.visits.forEach((visit) => {
if (visit.start_date) {
- const adventureDate = getLocalDateString(new Date(visit.start_date));
- if (visit.end_date) {
- const endDate = new Date(visit.end_date).toISOString().split('T')[0];
+ // Check if this is an all-day event (both start and end at midnight)
+ const isAllDayEvent =
+ isAllDay(visit.start_date) && (visit.end_date ? isAllDay(visit.end_date) : false);
- // Loop through all days and include adventure if it falls within the range
+ // For all-day events, we need to handle dates differently
+ if (isAllDayEvent && visit.end_date) {
+ // Extract just the date parts without time
+ const startDateStr = visit.start_date.split('T')[0];
+ const endDateStr = visit.end_date.split('T')[0];
+
+ // Loop through all days in the range
for (let i = 0; i < numberOfDays; i++) {
const currentDate = new Date(startDate);
currentDate.setDate(startDate.getDate() + i);
- const dateString = getLocalDateString(currentDate);
+ const currentDateStr = getLocalDateString(currentDate);
// Include the current day if it falls within the adventure date range
- if (dateString >= adventureDate && dateString <= endDate) {
- if (groupedAdventures[dateString]) {
- groupedAdventures[dateString].push(adventure);
+ if (currentDateStr >= startDateStr && currentDateStr <= endDateStr) {
+ if (groupedAdventures[currentDateStr]) {
+ groupedAdventures[currentDateStr].push(adventure);
}
}
}
- } else if (groupedAdventures[adventureDate]) {
- // If there's no end date, add adventure to the start date only
- groupedAdventures[adventureDate].push(adventure);
+ } else {
+ // Handle regular events with time components
+ const adventureStartDate = new Date(visit.start_date);
+ const adventureDateStr = getLocalDateString(adventureStartDate);
+
+ if (visit.end_date) {
+ const adventureEndDate = new Date(visit.end_date);
+ const endDateStr = getLocalDateString(adventureEndDate);
+
+ // Loop through all days and include adventure if it falls within the range
+ for (let i = 0; i < numberOfDays; i++) {
+ const currentDate = new Date(startDate);
+ currentDate.setDate(startDate.getDate() + i);
+ const dateString = getLocalDateString(currentDate);
+
+ // Include the current day if it falls within the adventure date range
+ if (dateString >= adventureDateStr && dateString <= endDateStr) {
+ if (groupedAdventures[dateString]) {
+ groupedAdventures[dateString].push(adventure);
+ }
+ }
+ }
+ } else {
+ // If there's no end date, add adventure to the start date only
+ if (groupedAdventures[adventureDateStr]) {
+ groupedAdventures[adventureDateStr].push(adventure);
+ }
+ }
}
}
});
@@ -106,6 +137,20 @@ export function groupAdventuresByDate(
return groupedAdventures;
}
+function getLocalDateString(date: Date): string {
+ const year = date.getFullYear();
+ const month = String(date.getMonth() + 1).padStart(2, '0'); // Months are 0-indexed
+ const day = String(date.getDate()).padStart(2, '0');
+ return `${year}-${month}-${day}`;
+}
+
+// Helper to check if a given date string represents midnight (all-day)
+// Improved isAllDay function to handle different ISO date formats
+export function isAllDay(dateStr: string): boolean {
+ // Check for various midnight formats in UTC
+ return dateStr.endsWith('T00:00:00Z') || dateStr.endsWith('T00:00:00.000Z');
+}
+
export function groupTransportationsByDate(
transportations: Transportation[],
startDate: Date,
@@ -150,13 +195,6 @@ export function groupTransportationsByDate(
return groupedTransportations;
}
-function getLocalDateString(date: Date): string {
- const year = date.getFullYear();
- const month = String(date.getMonth() + 1).padStart(2, '0'); // Months are 0-indexed
- const day = String(date.getDate()).padStart(2, '0');
- return `${year}-${month}-${day}`;
-}
-
export function groupLodgingByDate(
transportations: Lodging[],
startDate: Date,
@@ -351,12 +389,6 @@ export let TRANSPORTATION_TYPES_ICONS = {
other: '❓'
};
-// Helper to check if a given date string represents midnight (all-day)
-export function isAllDay(dateStr: string | string[]) {
- // Checks for the pattern "T00:00:00.000Z"
- return dateStr.includes('T00:00:00Z') || dateStr.includes('T00:00:00.000Z');
-}
-
export function getAdventureTypeLabel(type: string) {
// return the emoji ADVENTURE_TYPE_ICONS label for the given type if not found return ? emoji
if (type in ADVENTURE_TYPE_ICONS) {
diff --git a/frontend/src/routes/signup/+page.server.ts b/frontend/src/routes/signup/+page.server.ts
index 1e39414..61f0852 100644
--- a/frontend/src/routes/signup/+page.server.ts
+++ b/frontend/src/routes/signup/+page.server.ts
@@ -74,8 +74,6 @@ export const actions: Actions = {
} else {
const setCookieHeader = loginFetch.headers.get('Set-Cookie');
- console.log('setCookieHeader:', setCookieHeader);
-
if (setCookieHeader) {
// Regular expression to match sessionid cookie and its expiry
const sessionIdRegex = /sessionid=([^;]+).*?expires=([^;]+)/;
From 1042a3edcc93ff184d11b4b9addff7c54f9b3756 Mon Sep 17 00:00:00 2001
From: Sean Morley
Date: Thu, 20 Mar 2025 22:08:22 -0400
Subject: [PATCH 07/79] refactor: Remove debug print statement from
NoPasswordAuthBackend authentication method
---
backend/server/users/backends.py | 1 -
.../src/routes/collections/[id]/+page.svelte | 376 +++++++++++-------
2 files changed, 224 insertions(+), 153 deletions(-)
diff --git a/backend/server/users/backends.py b/backend/server/users/backends.py
index a099f11..f9291f0 100644
--- a/backend/server/users/backends.py
+++ b/backend/server/users/backends.py
@@ -3,7 +3,6 @@ from allauth.socialaccount.models import SocialAccount
class NoPasswordAuthBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
- print("NoPasswordAuthBackend")
# First, attempt normal authentication
user = super().authenticate(request, username=username, password=password, **kwargs)
if user is None:
diff --git a/frontend/src/routes/collections/[id]/+page.svelte b/frontend/src/routes/collections/[id]/+page.svelte
index 1a69fdd..df8ad0a 100644
--- a/frontend/src/routes/collections/[id]/+page.svelte
+++ b/frontend/src/routes/collections/[id]/+page.svelte
@@ -133,6 +133,7 @@
}
let currentView: string = 'itinerary';
+ let currentItineraryView: string = 'date';
let adventures: Adventure[] = [];
@@ -303,6 +304,7 @@
} else {
notFound = true;
}
+
if (collection.start_date && collection.end_date) {
numberOfDays =
Math.floor(
@@ -923,129 +925,233 @@
})}
+
- {#if orderedItem.type === 'adventure'}
- {#if orderedItem.item && 'images' in orderedItem.item}
-
- {/if}
- {/if}
- {#if orderedItem.type === 'transportation' && orderedItem.item && 'origin_latitude' in orderedItem.item}
- {
- transportations = transportations.filter((t) => t.id != event.detail);
- }}
- on:edit={editTransportation}
- {collection}
- />
- {/if}
- {#if orderedItem.type === 'lodging' && orderedItem.item && 'reservation_number' in orderedItem.item}
- {
- lodging = lodging.filter((t) => t.id != event.detail);
- }}
- on:edit={editLodging}
- {collection}
- />
- {/if}
-{/each}
-
{data.props.adventure && data.props.adventure.name
From f79b06f6b3f35a4e8ceb1d77edcdae536be2e44c Mon Sep 17 00:00:00 2001
From: Sean Morley
Date: Thu, 20 Mar 2025 22:28:23 -0400
Subject: [PATCH 08/79] feat: Add troubleshooting guide for unresponsive login
and registration, enhance collection modal alerts, and improve localization
for itinerary features
---
documentation/.vitepress/config.mts | 4 +++
.../troubleshooting/login_unresponsive.md | 19 ++++++++++++++
.../src/lib/components/CollectionModal.svelte | 25 +++++++++++++++++--
frontend/src/locales/en.json | 4 +++
.../src/routes/collections/[id]/+page.svelte | 10 +++++---
frontend/src/routes/worldtravel/+page.svelte | 8 ++++++
6 files changed, 64 insertions(+), 6 deletions(-)
create mode 100644 documentation/docs/troubleshooting/login_unresponsive.md
diff --git a/documentation/.vitepress/config.mts b/documentation/.vitepress/config.mts
index dbd9299..4263aca 100644
--- a/documentation/.vitepress/config.mts
+++ b/documentation/.vitepress/config.mts
@@ -134,6 +134,10 @@ export default defineConfig({
text: "No Images Displaying",
link: "/docs/troubleshooting/no_images",
},
+ {
+ text: "Login and Registration Unresponsive",
+ link: "/docs/troubleshooting/login_unresponsive",
+ },
{
text: "Failed to Start Nginx",
link: "/docs/troubleshooting/nginx_failed",
diff --git a/documentation/docs/troubleshooting/login_unresponsive.md b/documentation/docs/troubleshooting/login_unresponsive.md
new file mode 100644
index 0000000..aecf431
--- /dev/null
+++ b/documentation/docs/troubleshooting/login_unresponsive.md
@@ -0,0 +1,19 @@
+# Troubleshooting: Login and Registration Unresponsive
+
+When you encounter issues with the login and registration pages being unresponsive in AdventureLog, it can be due to various reasons. This guide will help you troubleshoot and resolve the unresponsive login and registration pages in AdventureLog.
+
+1. Check to make sure the backend container is running and accessible.
+
+ - Check the backend container logs to see if there are any errors or issues blocking the contianer from running.
+
+2. Check the connection between the frontend and backend containers.
+
+ - Attempt login with the browser console network tab open to see if there are any errors or issues with the connection between the frontend and backend containers. If there is a connection issue, the code will show an error like `Failed to load resource: net::ERR_CONNECTION_REFUSED`. If this is the case, check the `PUBLIC_SERVER_URL` in the frontend container and refer to the installation docs to ensure the correct URL is set.
+ - If the error is `403`, continue to the next step.
+
+3. The error most likely is due to a CSRF security config issue in either the backend or frontend.
+
+ - Check that the `ORIGIN` variable in the frontend is set to the URL where the frontend is access and you are accessing the app from currently.
+ - Check that the `CSRF_TRUSTED_ORIGINS` variable in the backend is set to a comma separated list of the origins where you use your backend server and frontend. One of these values should match the `ORIGIN` variable in the frontend.
+
+4. If you are still experiencing issues, please refer to the [AdventureLog Discord Server](https://discord.gg/wRbQ9Egr8C) for further assistance, providing as much detail as possible about the issue you are experiencing!
diff --git a/frontend/src/lib/components/CollectionModal.svelte b/frontend/src/lib/components/CollectionModal.svelte
index e5cb294..93ec776 100644
--- a/frontend/src/lib/components/CollectionModal.svelte
+++ b/frontend/src/lib/components/CollectionModal.svelte
@@ -189,10 +189,31 @@
{/if}
From 4ccfa6e42ce39389cdff5528b9212f747899b710 Mon Sep 17 00:00:00 2001
From: ClumsyAdmin <59402569+ClumsyAdmin@users.noreply.github.com>
Date: Fri, 21 Mar 2025 12:02:23 -0400
Subject: [PATCH 09/79] refactor docker startup to use supervisord
---
backend/Dockerfile | 9 ++++++---
backend/entrypoint.sh | 7 +++++--
backend/supervisord.conf | 10 ++++++++++
frontend/startup.sh | 2 +-
4 files changed, 22 insertions(+), 6 deletions(-)
create mode 100644 backend/supervisord.conf
diff --git a/backend/Dockerfile b/backend/Dockerfile
index aa0f9f4..fae48fb 100644
--- a/backend/Dockerfile
+++ b/backend/Dockerfile
@@ -12,7 +12,7 @@ WORKDIR /code
# Install system dependencies (Nginx included)
RUN apt-get update \
- && apt-get install -y git postgresql-client gdal-bin libgdal-dev nginx \
+ && apt-get install -y git postgresql-client gdal-bin libgdal-dev nginx supervisor \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
@@ -31,6 +31,9 @@ COPY ./server /code/
# Copy Nginx configuration
COPY ./nginx.conf /etc/nginx/nginx.conf
+# Copy Supervisor configuration
+COPY ./supervisord.conf /etc/supervisor/conf.d/supervisord.conf
+
# Collect static files
RUN python3 manage.py collectstatic --noinput --verbosity 2
@@ -41,5 +44,5 @@ RUN chmod +x /code/entrypoint.sh
# Expose ports for NGINX and Gunicorn
EXPOSE 80 8000
-# Command to start Nginx and Gunicorn
-CMD ["bash", "-c", "service nginx start && /code/entrypoint.sh"]
\ No newline at end of file
+# Command to start Supervisor (which starts Nginx and Gunicorn)
+CMD ["supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
diff --git a/backend/entrypoint.sh b/backend/entrypoint.sh
index 9ca926c..780a182 100644
--- a/backend/entrypoint.sh
+++ b/backend/entrypoint.sh
@@ -62,5 +62,8 @@ fi
cat /code/adventurelog.txt
-# Start gunicorn
-gunicorn main.wsgi:application --bind [::]:8000 --timeout 120 --workers 2
\ No newline at end of file
+# Start Gunicorn in foreground
+exec gunicorn main.wsgi:application \
+ --bind 0.0.0.0:8000 \
+ --workers 4 \
+ --timeout 120
diff --git a/backend/supervisord.conf b/backend/supervisord.conf
new file mode 100644
index 0000000..7657f7f
--- /dev/null
+++ b/backend/supervisord.conf
@@ -0,0 +1,10 @@
+[supervisord]
+nodaemon=true
+
+[program:nginx]
+command=/usr/sbin/nginx -g "daemon off;"
+autorestart=true
+
+[program:gunicorn]
+command=/code/entrypoint.sh
+autorestart=true
diff --git a/frontend/startup.sh b/frontend/startup.sh
index 1b611e3..67dbb1e 100644
--- a/frontend/startup.sh
+++ b/frontend/startup.sh
@@ -2,4 +2,4 @@
echo "The origin to be set is: $ORIGIN"
# Start the application
-ORIGIN=$ORIGIN node build
\ No newline at end of file
+ORIGIN=$ORIGIN exec node build
From db63b6e7d8b65f22b89ac021b6ad708eb6373d5c Mon Sep 17 00:00:00 2001
From: Sean Morley
Date: Fri, 21 Mar 2025 13:35:29 -0400
Subject: [PATCH 10/79] feat: Add usage guide for AdventureLog and enhance
overview with personal message from the maintainer
---
documentation/.vitepress/config.mts | 10 +++++++
.../docs/intro/adventurelog_overview.md | 4 ++-
documentation/docs/usage/usage.md | 29 +++++++++++++++++++
3 files changed, 42 insertions(+), 1 deletion(-)
create mode 100644 documentation/docs/usage/usage.md
diff --git a/documentation/.vitepress/config.mts b/documentation/.vitepress/config.mts
index 4263aca..4877f62 100644
--- a/documentation/.vitepress/config.mts
+++ b/documentation/.vitepress/config.mts
@@ -84,6 +84,16 @@ export default defineConfig({
},
],
},
+ {
+ text: "Usage",
+ collapsed: false,
+ items: [
+ {
+ text: "How to use AdventureLog",
+ link: "/docs/usage/usage",
+ },
+ ],
+ },
{
text: "Configuration",
collapsed: false,
diff --git a/documentation/docs/intro/adventurelog_overview.md b/documentation/docs/intro/adventurelog_overview.md
index 310237f..cfb30f4 100644
--- a/documentation/docs/intro/adventurelog_overview.md
+++ b/documentation/docs/intro/adventurelog_overview.md
@@ -27,4 +27,6 @@ AdventureLog is open-source software, licensed under the GPL-3.0 license. This m
## About the Maintainer
-AdventureLog is created and maintained by [Sean Morley](https://seanmorley.com), a Computer Science student at the University of Connecticut. Sean is passionate about open-source software and building modern tools that help people solve real-world problems.
+Hi, I'm [Sean Morley](https://seanmorley.com), the creator of AdventureLog. I'm a Computer Science student at the University of Connecticut, and I'm passionate about open-source software and building modern tools that help people solve real-world problems. I created AdventureLog to solve a problem: the lack of a modern, open-source, user-friendly travel companion. Many existing travel apps are either too complex, too expensive, or too closed-off to be useful for the average traveler. AdventureLog aims to be the opposite: simple, beautiful, and open to everyone.
+
+I hope you enjoy using AdventureLog as much as I enjoy creating it! If you have any questions, feedback, or suggestions, feel free to reach out to me via the email address listed on my website. I'm always happy to hear from users and help in any way I can. Thank you for using AdventureLog, and happy travels! 🌍
diff --git a/documentation/docs/usage/usage.md b/documentation/docs/usage/usage.md
new file mode 100644
index 0000000..8cf0129
--- /dev/null
+++ b/documentation/docs/usage/usage.md
@@ -0,0 +1,29 @@
+# How to use AdventureLog
+
+Welcome to AdventureLog! This guide will help you get started with AdventureLog and provide you with an overview of the features available to you.
+
+## Key Terms
+
+#### Adventures
+
+- **Adventure**: think of an adventure as a point on a map, a location you want to visit, or a place you want to explore. An adventure can be anything you want it to be, from a local park to a famous landmark.
+- **Visit**: a visit is added to an adventure. It contains a date and notes about when the adventure was visited. If an adventure is visited multiple times, multiple visits can be added. If there are no visits on an adventure or the date of all visits is in the future, the adventure is considered planned. If the date of the visit is in the past, the adventure is considered completed.
+- **Category**: a category is a way to group adventures together. For example, you could have a category for parks, a category for museums, and a category for restaurants.
+- **Tag**: a tag is a way to add additional information to an adventure. For example, you could have a tag for the type of cuisine at a restaurant or the type of art at a museum. Multiple tags can be added to an adventure.
+- **Image**: an image is a photo that is added to an adventure. Images can be added to an adventure to provide a visual representation of the location or to capture a memory of the visit. These can be uploded from your device or with a service like [Immich](/docs/configuration/immich_integration) if the integration is enabled.
+- **Attachment**: an attachment is a file that is added to an adventure. Attachments can be added to an adventure to provide additional information, such as a map of the location or a brochure from the visit.
+
+#### Collections
+
+- **Collection**: a collection is a way to group adventures together. Collections are flexible and can be used in many ways. When no start or end date is added to a collection, it acts like a folder to group adventures together. When a start and end date is added to a collection, it acts like a trip to group adventures together that were visited during that time period. With start and end dates, the collection is transformed into a full itinerary with a map showing the route taken between adventures.
+- **Transportation**: a transportation is a collection exclusive feature that allows you to add transportation information to your trip. This can be used to show the route taken between locations and the mode of transportation used. It can also be used to track flight information, such as flight number and departure time.
+- **Lodging**: a lodging is a collection exclusive feature that allows you to add lodging information to your trip. This can be used to plan where you will stay during your trip and add notes about the lodging location. It can also be used to track reservation information, such as reservation number and check-in time.
+- **Note**: a note is a collection exclusive feature that allows you to add notes to your trip. This can be used to add additional information about your trip, such as a summary of the trip or a list of things to do. Notes can be assigned to a specific day of the trip to help organize the information.
+- **Checklist**: a checklist is a collection exclusive feature that allows you to add a checklist to your trip. This can be used to create a list of things to do during your trip or for planning purposes like packing lists. Checklists can be assigned to a specific day of the trip to help organize the information.
+
+#### World Travel
+
+- **World Travel**: the world travel feature of AdventureLog allows you to track the countries, regions, and cities you have visited during your lifetime. You can add visits to countries, regions, and cities, and view statistics about your travels. The world travel feature is a fun way to visualize where you have been and where you want to go next.
+ - **Country**: a country is a geographical area that is recognized as an independent nation. You can add visits to countries to track where you have been.
+ - **Region**: a region is a geographical area that is part of a country. You can add visits to regions to track where you have been within a country.
+ - **City**: a city is a geographical area that is a populated urban center. You can add visits to cities to track where you have been within a region.
From 794df82ec62f7b99ab0262e880bdfeb1a1cd80a8 Mon Sep 17 00:00:00 2001
From: Sean Morley
Date: Fri, 21 Mar 2025 16:30:03 -0400
Subject: [PATCH 11/79] feat: Enhance AdventureModal date handling for all-day
events and improve localization in collections page
---
.../src/lib/components/AdventureModal.svelte | 59 +++++++++++++++++--
.../src/routes/collections/[id]/+page.svelte | 32 +++++++++-
2 files changed, 86 insertions(+), 5 deletions(-)
diff --git a/frontend/src/lib/components/AdventureModal.svelte b/frontend/src/lib/components/AdventureModal.svelte
index 3054687..85db0a4 100644
--- a/frontend/src/lib/components/AdventureModal.svelte
+++ b/frontend/src/lib/components/AdventureModal.svelte
@@ -8,12 +8,16 @@
let fullStartDate: string = '';
let fullEndDate: string = '';
+ let fullStartDateOnly: string = '';
+ let fullEndDateOnly: string = '';
let allDay: boolean = true;
// Set full start and end dates from collection
if (collection && collection.start_date && collection.end_date) {
fullStartDate = `${collection.start_date}T00:00`;
fullEndDate = `${collection.end_date}T23:59`;
+ fullStartDateOnly = collection.start_date;
+ fullEndDateOnly = collection.end_date;
}
const dispatch = createEventDispatcher();
@@ -742,8 +746,8 @@
type="date"
class="input input-bordered w-full"
placeholder={$t('adventures.start_date')}
- min={constrainDates ? fullStartDate : ''}
- max={constrainDates ? fullEndDate : ''}
+ min={constrainDates ? fullStartDateOnly : ''}
+ max={constrainDates ? fullEndDateOnly : ''}
bind:value={new_start_date}
on:keydown={(e) => {
if (e.key === 'Enter') {
@@ -757,8 +761,8 @@
class="input input-bordered w-full"
placeholder={$t('adventures.end_date')}
bind:value={new_end_date}
- min={constrainDates ? fullStartDate : ''}
- max={constrainDates ? fullEndDate : ''}
+ min={constrainDates ? fullStartDateOnly : ''}
+ max={constrainDates ? fullEndDateOnly : ''}
on:keydown={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
@@ -848,6 +852,53 @@
{/if}
+ {
+ // Determine if this is an all-day event
+ const isAllDayEvent = isAllDay(visit.start_date);
+ allDay = isAllDayEvent;
+
+ if (isAllDayEvent) {
+ // For all-day events, use date only
+ new_start_date = visit.start_date.split('T')[0];
+ new_end_date = visit.end_date.split('T')[0];
+ } else {
+ // For timed events, format properly for datetime-local input
+ const startDate = new Date(visit.start_date);
+ const endDate = new Date(visit.end_date);
+
+ // Format as yyyy-MM-ddThh:mm
+ new_start_date =
+ startDate.getFullYear() +
+ '-' +
+ String(startDate.getMonth() + 1).padStart(2, '0') +
+ '-' +
+ String(startDate.getDate()).padStart(2, '0') +
+ 'T' +
+ String(startDate.getHours()).padStart(2, '0') +
+ ':' +
+ String(startDate.getMinutes()).padStart(2, '0');
+
+ new_end_date =
+ endDate.getFullYear() +
+ '-' +
+ String(endDate.getMonth() + 1).padStart(2, '0') +
+ '-' +
+ String(endDate.getDate()).padStart(2, '0') +
+ 'T' +
+ String(endDate.getHours()).padStart(2, '0') +
+ ':' +
+ String(endDate.getMinutes()).padStart(2, '0');
+ }
+
+ new_notes = visit.notes;
+ adventure.visits = adventure.visits.filter((v) => v !== visit);
+ }}
+ >
+ {$t('lodging.edit')}
+
From fe25f8e2c8574fed89a009d631b8d5b2d0b9b932 Mon Sep 17 00:00:00 2001
From: Sean Morley
Date: Fri, 21 Mar 2025 17:31:33 -0400
Subject: [PATCH 12/79] feat: Add start_date to collection ordering and enhance
localization for itinerary features
---
.../adventures/views/collection_view.py | 8 +++-
frontend/src/locales/de.json | 6 ++-
frontend/src/locales/es.json | 6 ++-
frontend/src/locales/fr.json | 6 ++-
frontend/src/locales/it.json | 6 ++-
frontend/src/locales/ko.json | 6 ++-
frontend/src/locales/nl.json | 6 ++-
frontend/src/locales/pl.json | 6 ++-
frontend/src/locales/sv.json | 6 ++-
frontend/src/locales/zh.json | 6 ++-
.../src/routes/collections/+page.server.ts | 4 +-
frontend/src/routes/collections/+page.svelte | 39 +++++++++++++------
12 files changed, 82 insertions(+), 23 deletions(-)
diff --git a/backend/server/adventures/views/collection_view.py b/backend/server/adventures/views/collection_view.py
index f0529ee..2c46dc5 100644
--- a/backend/server/adventures/views/collection_view.py
+++ b/backend/server/adventures/views/collection_view.py
@@ -22,7 +22,7 @@ class CollectionViewSet(viewsets.ModelViewSet):
order_by = self.request.query_params.get('order_by', 'name')
order_direction = self.request.query_params.get('order_direction', 'asc')
- valid_order_by = ['name', 'upated_at']
+ valid_order_by = ['name', 'upated_at', 'start_date']
if order_by not in valid_order_by:
order_by = 'updated_at'
@@ -35,6 +35,12 @@ class CollectionViewSet(viewsets.ModelViewSet):
ordering = 'lower_name'
if order_direction == 'desc':
ordering = f'-{ordering}'
+ elif order_by == 'start_date':
+ ordering = 'start_date'
+ if order_direction == 'asc':
+ ordering = 'start_date'
+ else:
+ ordering = '-start_date'
else:
order_by == 'updated_at'
ordering = 'updated_at'
diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json
index c0d939e..13a02ad 100644
--- a/frontend/src/locales/de.json
+++ b/frontend/src/locales/de.json
@@ -248,7 +248,11 @@
"reservation_number": "Reservierungsnummer",
"welcome_map_info": "Frei zugängliche Abenteuer auf diesem Server",
"open_in_maps": "In Karten öffnen",
- "all_day": "Den ganzen Tag"
+ "all_day": "Den ganzen Tag",
+ "collection_no_start_end_date": "Durch das Hinzufügen eines Start- und Enddatums zur Sammlung werden Reiseroutenplanungsfunktionen auf der Sammlungsseite freigegeben.",
+ "date_itinerary": "Datumstrecke",
+ "no_ordered_items": "Fügen Sie der Sammlung Elemente mit Daten hinzu, um sie hier zu sehen.",
+ "ordered_itinerary": "Reiseroute bestellt"
},
"home": {
"desc_1": "Entdecken, planen und erkunden Sie mühelos",
diff --git a/frontend/src/locales/es.json b/frontend/src/locales/es.json
index bd9b6a4..c82df3f 100644
--- a/frontend/src/locales/es.json
+++ b/frontend/src/locales/es.json
@@ -296,7 +296,11 @@
"reservation_number": "Número de reserva",
"welcome_map_info": "Aventuras públicas en este servidor",
"open_in_maps": "Abrir en mapas",
- "all_day": "Todo el día"
+ "all_day": "Todo el día",
+ "collection_no_start_end_date": "Agregar una fecha de inicio y finalización a la colección desbloqueará las funciones de planificación del itinerario en la página de colección.",
+ "date_itinerary": "Itinerario de fecha",
+ "no_ordered_items": "Agregue elementos con fechas a la colección para verlos aquí.",
+ "ordered_itinerary": "Itinerario ordenado"
},
"worldtravel": {
"all": "Todo",
diff --git a/frontend/src/locales/fr.json b/frontend/src/locales/fr.json
index 2523fe5..249243d 100644
--- a/frontend/src/locales/fr.json
+++ b/frontend/src/locales/fr.json
@@ -248,7 +248,11 @@
"reservation_number": "Numéro de réservation",
"welcome_map_info": "Aventures publiques sur ce serveur",
"open_in_maps": "Ouvert dans les cartes",
- "all_day": "Toute la journée"
+ "all_day": "Toute la journée",
+ "collection_no_start_end_date": "L'ajout d'une date de début et de fin à la collection débloquera les fonctionnalités de planification de l'itinéraire dans la page de collection.",
+ "date_itinerary": "Itinéraire de date",
+ "no_ordered_items": "Ajoutez des articles avec des dates à la collection pour les voir ici.",
+ "ordered_itinerary": "Itinéraire ordonné"
},
"home": {
"desc_1": "Découvrez, planifiez et explorez en toute simplicité",
diff --git a/frontend/src/locales/it.json b/frontend/src/locales/it.json
index 216d121..87c963f 100644
--- a/frontend/src/locales/it.json
+++ b/frontend/src/locales/it.json
@@ -248,7 +248,11 @@
"welcome_map_info": "Avventure pubbliche su questo server",
"reservation_number": "Numero di prenotazione",
"open_in_maps": "Aperto in mappe",
- "all_day": "Tutto il giorno"
+ "all_day": "Tutto il giorno",
+ "collection_no_start_end_date": "L'aggiunta di una data di inizio e fine alla raccolta sbloccherà le funzionalità di pianificazione dell'itinerario nella pagina di raccolta.",
+ "date_itinerary": "Itinerario della data",
+ "no_ordered_items": "Aggiungi articoli con date alla collezione per vederli qui.",
+ "ordered_itinerary": "Itinerario ordinato"
},
"home": {
"desc_1": "Scopri, pianifica ed esplora con facilità",
diff --git a/frontend/src/locales/ko.json b/frontend/src/locales/ko.json
index 2099c0c..dda6962 100644
--- a/frontend/src/locales/ko.json
+++ b/frontend/src/locales/ko.json
@@ -248,7 +248,11 @@
"reservation_number": "예약 번호",
"welcome_map_info": "이 서버의 공개 모험",
"open_in_maps": "지도에서 열립니다",
- "all_day": "하루 종일"
+ "all_day": "하루 종일",
+ "collection_no_start_end_date": "컬렉션에 시작 및 종료 날짜를 추가하면 컬렉션 페이지에서 여정 계획 기능이 잠금 해제됩니다.",
+ "date_itinerary": "날짜 일정",
+ "no_ordered_items": "컬렉션에 날짜가있는 항목을 추가하여 여기에서 확인하십시오.",
+ "ordered_itinerary": "주문한 여정"
},
"auth": {
"both_passwords_required": "두 암호 모두 필요합니다",
diff --git a/frontend/src/locales/nl.json b/frontend/src/locales/nl.json
index 6dbf2d7..7cb51dc 100644
--- a/frontend/src/locales/nl.json
+++ b/frontend/src/locales/nl.json
@@ -248,7 +248,11 @@
"price": "Prijs",
"region": "Regio",
"open_in_maps": "Open in kaarten",
- "all_day": "De hele dag"
+ "all_day": "De hele dag",
+ "collection_no_start_end_date": "Als u een start- en einddatum aan de collectie toevoegt, ontgrendelt u de functies van de planning van de route ontgrendelen in de verzamelpagina.",
+ "date_itinerary": "Datumroute",
+ "no_ordered_items": "Voeg items toe met datums aan de collectie om ze hier te zien.",
+ "ordered_itinerary": "Besteld reisschema"
},
"home": {
"desc_1": "Ontdek, plan en verken met gemak",
diff --git a/frontend/src/locales/pl.json b/frontend/src/locales/pl.json
index 3eb73ac..c4cd914 100644
--- a/frontend/src/locales/pl.json
+++ b/frontend/src/locales/pl.json
@@ -296,7 +296,11 @@
"reservation_number": "Numer rezerwacji",
"welcome_map_info": "Publiczne przygody na tym serwerze",
"open_in_maps": "Otwarte w mapach",
- "all_day": "Cały dzień"
+ "all_day": "Cały dzień",
+ "collection_no_start_end_date": "Dodanie daty rozpoczęcia i końca do kolekcji odblokuje funkcje planowania planu podróży na stronie kolekcji.",
+ "date_itinerary": "Trasa daty",
+ "no_ordered_items": "Dodaj przedmioty z datami do kolekcji, aby je zobaczyć tutaj.",
+ "ordered_itinerary": "Zamówiono trasę"
},
"worldtravel": {
"country_list": "Lista krajów",
diff --git a/frontend/src/locales/sv.json b/frontend/src/locales/sv.json
index 0baff34..5efbf63 100644
--- a/frontend/src/locales/sv.json
+++ b/frontend/src/locales/sv.json
@@ -248,7 +248,11 @@
"region": "Område",
"reservation_number": "Bokningsnummer",
"open_in_maps": "Kappas in",
- "all_day": "Hela dagen"
+ "all_day": "Hela dagen",
+ "collection_no_start_end_date": "Att lägga till ett start- och slutdatum till samlingen kommer att låsa upp planeringsfunktioner för resplan på insamlingssidan.",
+ "date_itinerary": "Datum resplan",
+ "no_ordered_items": "Lägg till objekt med datum i samlingen för att se dem här.",
+ "ordered_itinerary": "Beställd resplan"
},
"home": {
"desc_1": "Upptäck, planera och utforska med lätthet",
diff --git a/frontend/src/locales/zh.json b/frontend/src/locales/zh.json
index dfd5e8e..84caea8 100644
--- a/frontend/src/locales/zh.json
+++ b/frontend/src/locales/zh.json
@@ -296,7 +296,11 @@
"price": "价格",
"reservation_number": "预订号",
"open_in_maps": "在地图上打开",
- "all_day": "整天"
+ "all_day": "整天",
+ "collection_no_start_end_date": "在集合页面中添加开始日期和结束日期将在“收集”页面中解锁行程计划功能。",
+ "date_itinerary": "日期行程",
+ "no_ordered_items": "将带有日期的项目添加到集合中,以便在此处查看它们。",
+ "ordered_itinerary": "订购了行程"
},
"auth": {
"forgot_password": "忘记密码?",
diff --git a/frontend/src/routes/collections/+page.server.ts b/frontend/src/routes/collections/+page.server.ts
index 20e2c40..0c9a24b 100644
--- a/frontend/src/routes/collections/+page.server.ts
+++ b/frontend/src/routes/collections/+page.server.ts
@@ -208,7 +208,7 @@ export const actions: Actions = {
const order_direction = formData.get('order_direction') as string;
const order_by = formData.get('order_by') as string;
- console.log(order_direction, order_by);
+ // console.log(order_direction, order_by);
let adventures: Adventure[] = [];
@@ -242,7 +242,7 @@ export const actions: Actions = {
previous = res.previous;
count = res.count;
adventures = [...adventures, ...visited];
- console.log(next, previous, count);
+ // console.log(next, previous, count);
}
return {
diff --git a/frontend/src/routes/collections/+page.svelte b/frontend/src/routes/collections/+page.svelte
index 171a5d4..6eddc70 100644
--- a/frontend/src/routes/collections/+page.svelte
+++ b/frontend/src/routes/collections/+page.svelte
@@ -15,8 +15,6 @@
let collections: Collection[] = data.props.adventures || [];
- let currentSort = { attribute: 'name', order: 'asc' };
-
let newType: string = '';
let resultsPerPage: number = 25;
@@ -235,17 +233,36 @@
aria-label={$t(`adventures.descending`)}
/>
+
{$t('adventures.order_by')}
+
+
+
+
+
-
{$t(`adventures.sort`)}
From a3cd94006534b965b14d2043392e96ba6e74db73 Mon Sep 17 00:00:00 2001
From: Sean Morley
Date: Fri, 21 Mar 2025 17:35:58 -0400
Subject: [PATCH 13/79] feat: Pass collection data to adventure,
transportation, lodging, and checklist components
---
frontend/src/routes/collections/[id]/+page.svelte | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/frontend/src/routes/collections/[id]/+page.svelte b/frontend/src/routes/collections/[id]/+page.svelte
index 2578384..351aabd 100644
--- a/frontend/src/routes/collections/[id]/+page.svelte
+++ b/frontend/src/routes/collections/[id]/+page.svelte
@@ -998,6 +998,7 @@
on:edit={editAdventure}
on:delete={deleteAdventure}
{adventure}
+ {collection}
/>
{/each}
{/if}
@@ -1013,6 +1014,7 @@
transportationToEdit = event.detail;
isShowingTransportationModal = true;
}}
+ {collection}
/>
{/each}
{/if}
@@ -1028,6 +1030,7 @@
on:delete={(event) => {
notes = notes.filter((n) => n.id != event.detail);
}}
+ {collection}
/>
{/each}
{/if}
@@ -1040,6 +1043,7 @@
lodging = lodging.filter((t) => t.id != event.detail);
}}
on:edit={editLodging}
+ {collection}
/>
{/each}
{/if}
@@ -1055,6 +1059,7 @@
checklistToEdit = event.detail;
isShowingChecklistModal = true;
}}
+ {collection}
/>
{/each}
{/if}
From b71109fd090193aa7e339e161778205a9b406849 Mon Sep 17 00:00:00 2001
From: ClumsyAdmin <59402569+ClumsyAdmin@users.noreply.github.com>
Date: Fri, 21 Mar 2025 20:25:25 -0400
Subject: [PATCH 14/79] potential fix: set Supervisor priorities to ensure
Gunicorn starts before nginx to prevent 502 errors
---
backend/supervisord.conf | 3 +++
1 file changed, 3 insertions(+)
diff --git a/backend/supervisord.conf b/backend/supervisord.conf
index 7657f7f..4436a9b 100644
--- a/backend/supervisord.conf
+++ b/backend/supervisord.conf
@@ -4,7 +4,10 @@ nodaemon=true
[program:nginx]
command=/usr/sbin/nginx -g "daemon off;"
autorestart=true
+priority=20
[program:gunicorn]
command=/code/entrypoint.sh
autorestart=true
+priority=10
+startsecs=5
From 13d3b24ec2ba5eb662dd0f4730cae4b6a5701aa2 Mon Sep 17 00:00:00 2001
From: Sean Morley
Date: Fri, 21 Mar 2025 20:27:46 -0400
Subject: [PATCH 15/79] chore: Reduce Gunicorn worker count from 4 to 2 for
optimized resource usage
---
backend/entrypoint.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/backend/entrypoint.sh b/backend/entrypoint.sh
index 780a182..19444fb 100644
--- a/backend/entrypoint.sh
+++ b/backend/entrypoint.sh
@@ -65,5 +65,5 @@ cat /code/adventurelog.txt
# Start Gunicorn in foreground
exec gunicorn main.wsgi:application \
--bind 0.0.0.0:8000 \
- --workers 4 \
+ --workers 2 \
--timeout 120
From ca4ef7983729a8881bfe8f46128d054c3a90e4cb Mon Sep 17 00:00:00 2001
From: ClumsyAdmin <59402569+ClumsyAdmin@users.noreply.github.com>
Date: Fri, 21 Mar 2025 20:51:58 -0400
Subject: [PATCH 16/79] fix supervisor logging
---
backend/supervisord.conf | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/backend/supervisord.conf b/backend/supervisord.conf
index 4436a9b..e7adec7 100644
--- a/backend/supervisord.conf
+++ b/backend/supervisord.conf
@@ -4,10 +4,13 @@ nodaemon=true
[program:nginx]
command=/usr/sbin/nginx -g "daemon off;"
autorestart=true
-priority=20
+stdout_logfile=/dev/stdout
+stderr_logfile=/dev/stderr
[program:gunicorn]
command=/code/entrypoint.sh
autorestart=true
-priority=10
-startsecs=5
+stdout_logfile=/dev/stdout
+stderr_logfile=/dev/stderr
+stdout_logfile_maxbytes = 0
+stderr_logfile_maxbytes = 0
From 16a77720038e25f3bc57cf417e6b1440fe6b0295 Mon Sep 17 00:00:00 2001
From: Sean Morley
Date: Sat, 22 Mar 2025 12:25:53 -0400
Subject: [PATCH 17/79] feat: Add additional adventure type and endpoint for
sunrise/sunset information
---
.../server/adventures/views/adventure_view.py | 71 ++++----
frontend/src/lib/types.ts | 9 ++
frontend/src/locales/en.json | 2 +
.../routes/adventures/[id]/+page.server.ts | 6 +-
.../src/routes/adventures/[id]/+page.svelte | 153 ++++++++++++------
5 files changed, 146 insertions(+), 95 deletions(-)
diff --git a/backend/server/adventures/views/adventure_view.py b/backend/server/adventures/views/adventure_view.py
index 2f7e1f1..55beac3 100644
--- a/backend/server/adventures/views/adventure_view.py
+++ b/backend/server/adventures/views/adventure_view.py
@@ -10,6 +10,7 @@ from adventures.models import Adventure, Category, Transportation, Lodging
from adventures.permissions import IsOwnerOrSharedWithFullAccess
from adventures.serializers import AdventureSerializer, TransportationSerializer, LodgingSerializer
from adventures.utils import pagination
+import requests
class AdventureViewSet(viewsets.ModelViewSet):
serializer_class = AdventureSerializer
@@ -170,48 +171,38 @@ class AdventureViewSet(viewsets.ModelViewSet):
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
- # @action(detail=True, methods=['post'])
- # def convert(self, request, pk=None):
- # """
- # Convert an Adventure instance into a Transportation or Lodging instance.
- # Expects a JSON body with "target_type": "transportation" or "lodging".
- # """
- # adventure = self.get_object()
- # target_type = request.data.get("target_type", "").lower()
+ @action(detail=True, methods=['get'], url_path='additional-info')
+ def additional_info(self, request, pk=None):
+ adventure = self.get_object()
- # if target_type not in ["transportation", "lodging"]:
- # return Response(
- # {"error": "Invalid target type. Must be 'transportation' or 'lodging'."},
- # status=400
- # )
- # if not adventure.collection:
- # return Response(
- # {"error": "Adventure must be part of a collection to be converted."},
- # status=400
- # )
+ # Permission check: owner or shared collection member
+ if adventure.user_id != request.user:
+ if not (adventure.collection and adventure.collection.shared_with.filter(id=request.user.id).exists()):
+ return Response({"error": "User does not have permission to access this adventure"},
+ status=status.HTTP_403_FORBIDDEN)
- # # Define the overlapping fields that both the Adventure and target models share.
- # overlapping_fields = ["name", "description", "is_public", 'collection']
+ serializer = self.get_serializer(adventure)
+ response_data = serializer.data
- # # Gather the overlapping data from the adventure instance.
- # conversion_data = {}
- # for field in overlapping_fields:
- # if hasattr(adventure, field):
- # conversion_data[field] = getattr(adventure, field)
+ visits = response_data.get('visits', [])
+ sun_times = []
- # # Make sure to include the user reference
- # conversion_data["user_id"] = adventure.user_id
+ for visit in visits:
+ date = visit.get('start_date')
+ if date and adventure.longitude and adventure.latitude:
+ api_url = f'https://api.sunrisesunset.io/json?lat={adventure.latitude}&lng={adventure.longitude}&date={date}'
+ res = requests.get(api_url)
+ if res.status_code == 200:
+ data = res.json()
+ results = data.get('results', {})
+ if results.get('sunrise') and results.get('sunset'):
+ sun_times.append({
+ "date": date,
+ "visit_id": visit.get('id'),
+ "sunrise": results.get('sunrise'),
+ "sunset": results.get('sunset')
+ })
+
- # # Convert the adventure instance within an atomic transaction.
- # with transaction.atomic():
- # if target_type == "transportation":
- # new_instance = Transportation.objects.create(**conversion_data)
- # serializer = TransportationSerializer(new_instance)
- # else: # target_type == "lodging"
- # new_instance = Lodging.objects.create(**conversion_data)
- # serializer = LodgingSerializer(new_instance)
-
- # # Optionally, delete the original adventure to avoid duplicates.
- # adventure.delete()
-
- # return Response(serializer.data)
+ response_data['sun_times'] = sun_times
+ return Response(response_data)
\ No newline at end of file
diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts
index fd0eadb..69735a3 100644
--- a/frontend/src/lib/types.ts
+++ b/frontend/src/lib/types.ts
@@ -44,6 +44,15 @@ export type Adventure = {
user?: User | null;
};
+export type AdditionalAdventure = Adventure & {
+ sun_times: {
+ date: string;
+ visit_id: string;
+ sunrise: string;
+ sunset: string;
+ }[];
+};
+
export type Country = {
id: number;
name: string;
diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json
index 72d1883..65c7288 100644
--- a/frontend/src/locales/en.json
+++ b/frontend/src/locales/en.json
@@ -90,6 +90,8 @@
"visits": "Visits",
"create_new": "Create New...",
"adventure": "Adventure",
+ "additional_info": "Additional Information",
+ "sunrise_sunset": "Sunrise & Sunset",
"count_txt": "results matching your search",
"sort": "Sort",
"order_by": "Order By",
diff --git a/frontend/src/routes/adventures/[id]/+page.server.ts b/frontend/src/routes/adventures/[id]/+page.server.ts
index eed47ba..140ca46 100644
--- a/frontend/src/routes/adventures/[id]/+page.server.ts
+++ b/frontend/src/routes/adventures/[id]/+page.server.ts
@@ -1,11 +1,11 @@
import type { PageServerLoad } from './$types';
const PUBLIC_SERVER_URL = process.env['PUBLIC_SERVER_URL'];
-import type { Adventure, Collection } from '$lib/types';
+import type { AdditionalAdventure, Adventure, Collection } from '$lib/types';
const endpoint = PUBLIC_SERVER_URL || 'http://localhost:8000';
export const load = (async (event) => {
const id = event.params as { id: string };
- let request = await fetch(`${endpoint}/api/adventures/${id.id}/`, {
+ let request = await fetch(`${endpoint}/api/adventures/${id.id}/additional-info/`, {
headers: {
Cookie: `sessionid=${event.cookies.get('sessionid')}`
},
@@ -19,7 +19,7 @@ export const load = (async (event) => {
}
};
} else {
- let adventure = (await request.json()) as Adventure;
+ let adventure = (await request.json()) as AdditionalAdventure;
let collection: Collection | null = null;
if (adventure.collection) {
diff --git a/frontend/src/routes/adventures/[id]/+page.svelte b/frontend/src/routes/adventures/[id]/+page.svelte
index 6d88e0e..f11f450 100644
--- a/frontend/src/routes/adventures/[id]/+page.svelte
+++ b/frontend/src/routes/adventures/[id]/+page.svelte
@@ -1,5 +1,5 @@
{#if $page.status === 404}
@@ -24,3 +25,52 @@
{/if}
+
+{#if $page.status === 500}
+
+
+
+
+ {$page.status}: {$page.error?.message}
+
+
+ Oops, looks like something went wrong.
+
+
+
+ AdventureLog server encountered an error while processing your request.
+
+ Please check the server logs for more information.
+
+
+
+
+ Administrators: Please check your setup using the
+ documentation.
+
From 3a8776c000d8145c833846bc22a8352fbb474581 Mon Sep 17 00:00:00 2001
From: Lars Kiesow
Date: Fri, 25 Apr 2025 15:54:02 +0200
Subject: [PATCH 49/79] Show AdventureLog icon instead of text in mobile mode
This patch makes AdventureLog hide the text but show the app icon in
the navigation bar when in mobile mode.
---
frontend/src/lib/components/Navbar.svelte | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/frontend/src/lib/components/Navbar.svelte b/frontend/src/lib/components/Navbar.svelte
index 61e65c1..efa0fa6 100644
--- a/frontend/src/lib/components/Navbar.svelte
+++ b/frontend/src/lib/components/Navbar.svelte
@@ -180,8 +180,8 @@
From cd494fefeefe0f1ee2a5529c57b61993e5de7ab1 Mon Sep 17 00:00:00 2001
From: Lars Kiesow
Date: Sun, 27 Apr 2025 15:30:12 +0200
Subject: [PATCH 52/79] Unify collection card width
The width of collection cards can vary quite a bit. Additionally, the
cards look different than the cards within a collection. It would be
nice if the design would be more or leyy the same for all of them.
This patch adjusts the collection card design by adapting the classes
from the cards within the collection (I literally just copied the
classes from ChecklistCard). This is another step in making the user
interface more homogeneous.
---
frontend/src/lib/components/CollectionCard.svelte | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frontend/src/lib/components/CollectionCard.svelte b/frontend/src/lib/components/CollectionCard.svelte
index 78b511f..44691f3 100644
--- a/frontend/src/lib/components/CollectionCard.svelte
+++ b/frontend/src/lib/components/CollectionCard.svelte
@@ -87,7 +87,7 @@
{/if}
From 932036bc8b807ed6e771ec9f72de3180481bc93f Mon Sep 17 00:00:00 2001
From: Lars Kiesow
Date: Sun, 27 Apr 2025 15:41:43 +0200
Subject: [PATCH 53/79] Move hiding AdventureLog to first Tailwind breakpoint
This is a slight improvement to pull request #576. I noticed that on a
tablet, the AdventureLog in the navigation bar would not render, even
though there would be enpugh free space. This patch moves the breakpoint
for hiding the text one step further towards smaller devices.
---
frontend/src/lib/components/Navbar.svelte | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frontend/src/lib/components/Navbar.svelte b/frontend/src/lib/components/Navbar.svelte
index efa0fa6..72a01cf 100644
--- a/frontend/src/lib/components/Navbar.svelte
+++ b/frontend/src/lib/components/Navbar.svelte
@@ -180,7 +180,7 @@
From 911ce67d9fcbe99c3024202052f18459f7d17b92 Mon Sep 17 00:00:00 2001
From: Lars Kiesow
Date: Sun, 27 Apr 2025 16:03:16 +0200
Subject: [PATCH 54/79] Remove Invisible MapMarker
This patch removes the map marker from the adventures list item of the
main menu dropdown. It is not being rendered and given that all other
elements do not have an icon, it is probably a remnant of old code and
left by accident.
---
frontend/src/lib/components/Navbar.svelte | 1 -
1 file changed, 1 deletion(-)
diff --git a/frontend/src/lib/components/Navbar.svelte b/frontend/src/lib/components/Navbar.svelte
index efa0fa6..66b0dd3 100644
--- a/frontend/src/lib/components/Navbar.svelte
+++ b/frontend/src/lib/components/Navbar.svelte
@@ -121,7 +121,6 @@
>
{#if data.user}
- goto('/adventures')}>{$t('navbar.adventures')}
From 5f7bf5275860448151e1828560dccd1710f989b6 Mon Sep 17 00:00:00 2001
From: Lars Kiesow
Date: Sun, 27 Apr 2025 16:08:03 +0200
Subject: [PATCH 55/79] Adjust main menu font size
This patch slightly adjusts the font size of list items in the main
menu. I probably wouldn't mind making it even a bit bigger, but that's
probably worth a separate discussion.
Reasons for this adjustment:
1. Don't use different font sizes in the same lement.
While the buttons rendered at 14px, the search text rendered at 16px.
They should have the same font-size.
2. The buttons were below the base text size controlled by Tailwind CSS.
This means, it also puts the font size below the recommended
font-size for mobile devices (Google/Apple guidelines). This patch
puts them at the base level.
---
frontend/src/lib/components/Navbar.svelte | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frontend/src/lib/components/Navbar.svelte b/frontend/src/lib/components/Navbar.svelte
index 0e5e4f2..1d776df 100644
--- a/frontend/src/lib/components/Navbar.svelte
+++ b/frontend/src/lib/components/Navbar.svelte
@@ -116,7 +116,7 @@
{#if data.user}
From f6097a2d60fb1220ab0ffe0a46d576f4ad1f050b Mon Sep 17 00:00:00 2001
From: Lars Kiesow
Date: Sun, 27 Apr 2025 17:42:51 +0200
Subject: [PATCH 56/79] Open in Apple Maps or Google Maps
This patch provides the additional option to open a location in Google
maps as an alternative to Apple Maps.
---
.../src/routes/adventures/[id]/+page.svelte | 21 +++++++++++++------
1 file changed, 15 insertions(+), 6 deletions(-)
diff --git a/frontend/src/routes/adventures/[id]/+page.svelte b/frontend/src/routes/adventures/[id]/+page.svelte
index f11f450..b32a19e 100644
--- a/frontend/src/routes/adventures/[id]/+page.svelte
+++ b/frontend/src/routes/adventures/[id]/+page.svelte
@@ -458,12 +458,21 @@
+ {$t('adventures.open_in_maps')}:
+ Apple
+ Google
+
{/if}
Date: Sun, 27 Apr 2025 17:49:31 +0200
Subject: [PATCH 57/79] Full width map on mobile
This patch lets the map in the adventure details use the full screen
width on mobile instead of having one sixth of the screen empty.
---
frontend/src/routes/adventures/[id]/+page.svelte | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frontend/src/routes/adventures/[id]/+page.svelte b/frontend/src/routes/adventures/[id]/+page.svelte
index f11f450..c76d905 100644
--- a/frontend/src/routes/adventures/[id]/+page.svelte
+++ b/frontend/src/routes/adventures/[id]/+page.svelte
@@ -467,7 +467,7 @@
{/if}
Date: Sun, 27 Apr 2025 18:05:27 +0200
Subject: [PATCH 58/79] Update fr.json
---
frontend/src/locales/fr.json | 318 +++++++++++++++++------------------
1 file changed, 159 insertions(+), 159 deletions(-)
diff --git a/frontend/src/locales/fr.json b/frontend/src/locales/fr.json
index 249243d..86703fc 100644
--- a/frontend/src/locales/fr.json
+++ b/frontend/src/locales/fr.json
@@ -50,19 +50,19 @@
"adventure_delete_success": "Aventure supprimée avec succès !",
"adventure_details": "Détails de l'aventure",
"adventure_type": "Type d'aventure",
- "archive": "Archive",
- "archived": "Archivé",
+ "archive": "Archiver",
+ "archived": "Archivée",
"archived_collection_message": "Collection archivée avec succès !",
"archived_collections": "Collections archivées",
"ascending": "Ascendant",
"cancel": "Annuler",
- "category_filter": "Filtre de catégorie",
- "clear": "Clair",
+ "category_filter": "Filtres de catégorie",
+ "clear": "Réinitialiser",
"close_filters": "Fermer les filtres",
"collection": "Collection",
- "collection_adventures": "Inclure les aventures de collection",
+ "collection_adventures": "Inclure les aventures liées à une collection",
"count_txt": "résultats correspondant à votre recherche",
- "create_new": "Créer un nouveau...",
+ "create_new": "Créer une nouvelle aventure...",
"date": "Date",
"dates": "Dates",
"delete_adventure": "Supprimer l'aventure",
@@ -72,7 +72,7 @@
"descending": "Descendant",
"duration": "Durée",
"edit_collection": "Modifier la collection",
- "filter": "Filtre",
+ "filter": "Filtrer",
"homepage": "Page d'accueil",
"image_removed_error": "Erreur lors de la suppression de l'image",
"image_removed_success": "Image supprimée avec succès !",
@@ -84,12 +84,12 @@
"name": "Nom",
"no_image_url": "Aucune image trouvée à cette URL.",
"not_found": "Aventure introuvable",
- "not_found_desc": "L'aventure que vous cherchiez est introuvable. \nVeuillez essayer une autre aventure ou revenez plus tard.",
+ "not_found_desc": "L'aventure que vous cherchez est introuvable. \nVeuillez essayer une autre aventure ou revenez plus tard.",
"open_filters": "Ouvrir les filtres",
- "order_by": "Commander par",
- "order_direction": "Direction de la commande",
- "planned": "Prévu",
- "private": "Privé",
+ "order_by": "Trier par",
+ "order_direction": "Direction du tri",
+ "planned": "Prévue",
+ "private": "Privée",
"public": "Publique",
"rating": "Notation",
"share": "Partager",
@@ -98,12 +98,12 @@
"start_before_end_error": "La date de début doit être antérieure à la date de fin",
"unarchive": "Désarchiver",
"unarchived_collection_message": "Collection désarchivée avec succès !",
- "updated": "Mis à jour",
+ "updated": "Mise à jour",
"visit": "Visite",
- "visited": "Visité",
+ "visited": "Visitée",
"visits": "Visites",
"wiki_image_error": "Erreur lors de la récupération de l'image depuis Wikipédia",
- "actions": "Actes",
+ "actions": "Actions",
"activity": "Activité",
"activity_types": "Types d'activités",
"add": "Ajouter",
@@ -117,7 +117,7 @@
"category": "Catégorie",
"clear_map": "Effacer la carte",
"copy_link": "Copier le lien",
- "date_constrain": "Contraindre aux dates de collecte",
+ "date_constrain": "Limiter aux dates de la collection",
"description": "Description",
"end_date": "Date de fin",
"fetch_image": "Récupérer une image",
@@ -125,7 +125,7 @@
"image": "Image",
"image_fetch_failed": "Échec de la récupération de l'image",
"link": "Lien",
- "location": "Emplacement",
+ "location": "Lieu",
"location_information": "Informations de localisation",
"my_images": "Mes images",
"my_visits": "Mes visites",
@@ -138,7 +138,7 @@
"public_adventure": "Aventure publique",
"remove": "Retirer",
"save_next": "Sauvegarder",
- "search_for_location": "Rechercher un emplacement",
+ "search_for_location": "Rechercher un lieu",
"search_results": "Résultats de la recherche",
"see_adventures": "Voir les aventures",
"select_adventure_category": "Sélectionnez la catégorie d'aventure",
@@ -148,73 +148,73 @@
"upload_images_here": "Téléchargez des images ici",
"url": "URL",
"warning": "Avertissement",
- "wiki_desc": "Extrait un extrait de l'article Wikipédia correspondant au nom de l'aventure.",
+ "wiki_desc": "Obtient un extrait de l'article Wikipédia correspondant au nom de l'aventure.",
"wikipedia": "Wikipédia",
- "adventure_not_found": "Il n'y a aucune aventure à afficher. \nAjoutez-en en utilisant le bouton plus en bas à droite ou essayez de changer les filtres !",
+ "adventure_not_found": "Il n'y a aucune aventure à afficher. \nAjoutez-en en utilisant le bouton '+' en bas à droite ou essayez de changer les filtres !",
"all": "Tous",
"error_updating_regions": "Erreur lors de la mise à jour des régions",
"mark_region_as_visited": "Marquer la région {region}, {country} comme visitée ?",
- "mark_visited": "Mark a visité",
+ "mark_visited": "Marquer comme visité",
"my_adventures": "Mes aventures",
"no_adventures_found": "Aucune aventure trouvée",
"no_collections_found": "Aucune collection trouvée pour ajouter cette aventure.",
"no_linkable_adventures": "Aucune aventure trouvée pouvant être liée à cette collection.",
- "not_visited": "Non visité",
+ "not_visited": "Non visitée",
"regions_updated": "régions mises à jour",
"update_visited_regions": "Mettre à jour les régions visitées",
"update_visited_regions_disclaimer": "Cela peut prendre un certain temps en fonction du nombre d'aventures que vous avez visitées.",
"visited_region_check": "Vérification de la région visitée",
- "visited_region_check_desc": "En sélectionnant cette option, le serveur vérifiera toutes vos aventures visitées et marquera les régions dans lesquelles elles se trouvent comme visitées lors des voyages dans le monde.",
+ "visited_region_check_desc": "En sélectionnant cette option, le serveur vérifiera toutes vos aventures visitées et marquera les régions correspondantes comme visitées dans la section 'Voyage dans le monde'.",
"add_new": "Ajouter un nouveau...",
"checklists": "Listes de contrôle",
"collection_archived": "Cette collection a été archivée.",
"collection_completed": "Vous avez terminé cette collection !",
- "collection_stats": "Statistiques de collecte",
+ "collection_stats": "Statistiques de la collection",
"days": "jours",
- "itineary_by_date": "Itinéraire par date",
+ "itineary_by_date": "Itinéraire trié par date",
"keep_exploring": "Continuez à explorer !",
- "link_new": "Lien Nouveau...",
+ "link_new": "Ajouter un lien vers...",
"linked_adventures": "Aventures liées",
- "links": "Links",
+ "links": "Liens",
"no_end_date": "Veuillez saisir une date de fin",
"note": "Note",
- "notes": "Remarques",
+ "notes": "Notes",
"nothing_planned": "Rien de prévu pour cette journée. \nBon voyage !",
- "transportation": "Transport",
- "transportations": "Transports",
- "visit_link": "Visitez le lien",
+ "transportation": "Déplacement",
+ "transportations": "Déplacements",
+ "visit_link": "Visiter le lien",
"checklist": "Liste de contrôle",
"day": "Jour",
"add_a_tag": "Ajouter une balise",
"tags": "Balises",
- "set_to_pin": "Définir sur Épingler",
+ "set_to_pin": "Épingler",
"category_fetch_error": "Erreur lors de la récupération des catégories",
"copied_to_clipboard": "Copié dans le presse-papier !",
"copy_failed": "Échec de la copie",
- "adventure_calendar": "Calendrier d'aventure",
+ "adventure_calendar": "Calendrier des aventures",
"emoji_picker": "Sélecteur d'émoticônes",
"hide": "Cacher",
"show": "Montrer",
"download_calendar": "Télécharger le calendrier",
- "md_instructions": "Écrivez votre démarque ici...",
+ "md_instructions": "Écrivez ici au format Markdown...",
"preview": "Aperçu",
"checklist_delete_confirm": "Êtes-vous sûr de vouloir supprimer cette liste de contrôle ? \nCette action ne peut pas être annulée.",
- "clear_location": "Effacer l'emplacement",
- "date_information": "Informations sur les dates",
+ "clear_location": "Effacer le lieu",
+ "date_information": "Dates",
"delete_checklist": "Supprimer la liste de contrôle",
"delete_note": "Supprimer la note",
- "delete_transportation": "Supprimer le transport",
- "end": "Fin",
- "ending_airport": "Aéroport de fin",
+ "delete_transportation": "Supprimer le déplacement",
+ "end": "Arrivée",
+ "ending_airport": "Aéroport d'arrivée",
"flight_information": "Informations sur le vol",
"from": "Depuis",
- "no_location_found": "Aucun emplacement trouvé",
+ "no_location_found": "Aucun lieu trouvé",
"note_delete_confirm": "Êtes-vous sûr de vouloir supprimer cette note ? \nCette action ne peut pas être annulée.",
"out_of_range": "Pas dans la plage de dates de l'itinéraire",
"show_region_labels": "Afficher les étiquettes de région",
- "start": "Commencer",
+ "start": "Départ",
"starting_airport": "Aéroport de départ",
- "to": "À",
+ "to": "Vers",
"transportation_delete_confirm": "Etes-vous sûr de vouloir supprimer ce transport ? \nCette action ne peut pas être annulée.",
"show_map": "Afficher la carte",
"will_be_marked": "sera marqué comme visité une fois l’aventure sauvegardée.",
@@ -232,27 +232,27 @@
"attachments": "Pièces jointes",
"gpx_tip": "Téléchargez des fichiers GPX en pièces jointes pour les afficher sur la carte !",
"images": "Images",
- "primary": "Primaire",
+ "primary": "Principale",
"upload": "Télécharger",
"view_attachment": "Voir la pièce jointe",
"of": "de",
"city": "Ville",
"delete_lodging": "Supprimer l'hébergement",
"display_name": "Nom d'affichage",
- "location_details": "Détails de l'emplacement",
+ "location_details": "Détails du lieu",
"lodging": "Hébergement",
- "lodging_delete_confirm": "Êtes-vous sûr de vouloir supprimer cet emplacement d'hébergement? \nCette action ne peut pas être annulée.",
+ "lodging_delete_confirm": "Êtes-vous sûr de vouloir supprimer cet hébergement? \nCette action ne peut pas être annulée.",
"lodging_information": "Informations sur l'hébergement",
"price": "Prix",
"region": "Région",
"reservation_number": "Numéro de réservation",
"welcome_map_info": "Aventures publiques sur ce serveur",
"open_in_maps": "Ouvert dans les cartes",
- "all_day": "Toute la journée",
+ "all_day": "Journée complète",
"collection_no_start_end_date": "L'ajout d'une date de début et de fin à la collection débloquera les fonctionnalités de planification de l'itinéraire dans la page de collection.",
- "date_itinerary": "Itinéraire de date",
- "no_ordered_items": "Ajoutez des articles avec des dates à la collection pour les voir ici.",
- "ordered_itinerary": "Itinéraire ordonné"
+ "date_itinerary": "Itinéraire trié par date",
+ "no_ordered_items": "Ajoutez des éléments avec des dates de visite à la collection pour les voir ici.",
+ "ordered_itinerary": "Itinéraire trié par activité"
},
"home": {
"desc_1": "Découvrez, planifiez et explorez en toute simplicité",
@@ -272,7 +272,7 @@
"about": "À propos de AdventureLog",
"adventures": "Aventures",
"collections": "Collections",
- "discord": "Discorde",
+ "discord": "Discord",
"documentation": "Documentation",
"greeting": "Salut",
"logout": "Déconnexion",
@@ -285,12 +285,12 @@
"theme_selection": "Sélection de thèmes",
"themes": {
"forest": "Forêt",
- "light": "Lumière",
+ "light": "Clair",
"night": "Nuit",
"aqua": "Aqua",
"dark": "Sombre",
- "aestheticDark": "Esthétique sombre",
- "aestheticLight": "Lumière esthétique",
+ "aestheticDark": "Esthétique (sombre)",
+ "aestheticLight": "Esthétique (clair)",
"northernLights": "Aurores boréales"
},
"users": "Utilisateurs",
@@ -303,7 +303,7 @@
"admin_panel": "Panneau d'administration"
},
"auth": {
- "confirm_password": "Confirmez le mot de passe",
+ "confirm_password": "Confirmer le mot de passe",
"email": "E-mail",
"first_name": "Prénom",
"forgot_password": "Mot de passe oublié ?",
@@ -317,18 +317,18 @@
"profile_picture": "Photo de profil",
"public_profile": "Profil public",
"public_tooltip": "Avec un profil public, les utilisateurs peuvent partager des collections avec vous et afficher votre profil sur la page des utilisateurs.",
- "email_required": "L'e-mail est requis",
+ "email_required": "Le courriel est requis",
"both_passwords_required": "Les deux mots de passe sont requis",
"new_password": "Nouveau mot de passe",
"reset_failed": "Échec de la réinitialisation du mot de passe",
"or_3rd_party": "Ou connectez-vous avec un service tiers",
"no_public_adventures": "Aucune aventure publique trouvée",
"no_public_collections": "Aucune collection publique trouvée",
- "user_adventures": "Aventures utilisateur",
- "user_collections": "Collections d'utilisateurs"
+ "user_adventures": "Aventures de l'utilisateur",
+ "user_collections": "Collections de l'utilisateur"
},
"users": {
- "no_users_found": "Aucun utilisateur trouvé avec des profils publics."
+ "no_users_found": "Aucun utilisateur trouvé avec un profil public."
},
"worldtravel": {
"all": "Tous",
@@ -357,27 +357,27 @@
"settings": {
"account_settings": "Paramètres du compte utilisateur",
"confirm_new_password": "Confirmer le nouveau mot de passe",
- "current_email": "Courriel actuel",
- "email_change": "Changer l'e-mail",
- "new_email": "Nouvel e-mail",
+ "current_email": "Adresse de courriel actuel",
+ "email_change": "Changer l'adresse de courriel",
+ "new_email": "Nouvelle adresse de courriel",
"new_password": "Nouveau mot de passe",
- "no_email_set": "Aucune adresse e-mail définie",
+ "no_email_set": "Aucune adresse de courriel définie",
"password_change": "Changer le mot de passe",
- "settings_page": "Page Paramètres",
+ "settings_page": "Page de paramétrage",
"update": "Mise à jour",
"update_error": "Erreur lors de la mise à jour des paramètres",
"update_success": "Paramètres mis à jour avec succès !",
"change_password": "Changer le mot de passe",
"invalid_token": "Le jeton n'est pas valide ou a expiré",
"login_redir": "Vous serez alors redirigé vers la page de connexion.",
- "missing_email": "Veuillez entrer une adresse e-mail",
+ "missing_email": "Veuillez entrer une adresse de courriel",
"password_does_not_match": "Les mots de passe ne correspondent pas",
"password_is_required": "Le mot de passe est requis",
- "possible_reset": "Si l'adresse e-mail que vous avez fournie est associée à un compte, vous recevrez un e-mail avec des instructions pour réinitialiser votre mot de passe !",
+ "possible_reset": "Si l'adresse de courriel que vous avez fournie est associée à un compte, vous recevrez un courriel avec des instructions pour réinitialiser votre mot de passe !",
"reset_password": "Réinitialiser le mot de passe",
- "submit": "Soumettre",
- "token_required": "Le jeton et l'UID sont requis pour la réinitialisation du mot de passe.",
- "about_this_background": "À propos de ce contexte",
+ "submit": "Valider",
+ "token_required": "Le jeton et l'identifiant utilisateur sont requis pour la réinitialisation du mot de passe.",
+ "about_this_background": "À propos de cette photo",
"join_discord": "Rejoignez le Discord",
"join_discord_desc": "pour partager vos propres photos. \nPostez-les dans le",
"photo_by": "Photo par",
@@ -385,77 +385,77 @@
"current_password": "Mot de passe actuel",
"password_change_lopout_warning": "Vous serez déconnecté après avoir modifié votre mot de passe.",
"authenticator_code": "Code d'authentification",
- "copy": "Copie",
- "disable_mfa": "Désactiver MFA",
- "email_added": "E-mail ajouté avec succès !",
- "email_added_error": "Erreur lors de l'ajout de l'e-mail",
- "email_removed": "E-mail supprimé avec succès !",
- "email_removed_error": "Erreur lors de la suppression de l'e-mail",
- "email_set_primary": "E-mail défini comme principal avec succès !",
- "email_set_primary_error": "Erreur lors de la définition de l'adresse e-mail comme adresse principale",
- "email_verified": "E-mail vérifié avec succès !",
- "email_verified_erorr_desc": "Votre email n'a pas pu être vérifié. \nVeuillez réessayer.",
- "email_verified_error": "Erreur lors de la vérification de l'e-mail",
- "email_verified_success": "Votre email a été vérifié. \nVous pouvez maintenant vous connecter.",
- "enable_mfa": "Activer l'authentification multifacteur",
+ "copy": "Copier",
+ "disable_mfa": "Désactiver l'authentification multi-facteurs",
+ "email_added": "Adresse de courriel ajoutée avec succès !",
+ "email_added_error": "Erreur lors de l'ajout de l'adresse de courriel",
+ "email_removed": "Adresse de courriel supprimée avec succès !",
+ "email_removed_error": "Erreur lors de la suppression de l'adresse de courriel",
+ "email_set_primary": "Adresse de courriel principale définie avec succès !",
+ "email_set_primary_error": "Erreur lors de la définition de l'adresse de courriel principale",
+ "email_verified": "Adresse de courriel vérifiée avec succès !",
+ "email_verified_erorr_desc": "Votre adresse de courriel n'a pas pu être vérifiée. \nVeuillez réessayer.",
+ "email_verified_error": "Erreur lors de la vérification de l'adresse de courriel",
+ "email_verified_success": "Votre adresse de courriel a été vérifiée. \nVous pouvez maintenant vous connecter.",
+ "enable_mfa": "Activer l'authentification multi-facteurs",
"error_change_password": "Erreur lors du changement de mot de passe. \nVeuillez vérifier votre mot de passe actuel et réessayer.",
"generic_error": "Une erreur s'est produite lors du traitement de votre demande.",
- "invalid_code": "Code MFA invalide",
+ "invalid_code": "Code d'authentification multi-facteurs invalide",
"invalid_credentials": "Nom d'utilisateur ou mot de passe invalide",
- "make_primary": "Rendre primaire",
- "mfa_disabled": "Authentification multifacteur désactivée avec succès !",
- "mfa_enabled": "Authentification multifacteur activée avec succès !",
- "mfa_not_enabled": "MFA n'est pas activé",
- "mfa_page_title": "Authentification multifacteur",
- "mfa_required": "Une authentification multifacteur est requise",
- "no_emai_set": "Aucune adresse e-mail définie",
- "not_verified": "Non vérifié",
- "primary": "Primaire",
+ "make_primary": "Définir comme adresse de courriel principale",
+ "mfa_disabled": "Authentification multi-facteurs désactivée avec succès !",
+ "mfa_enabled": "Authentification multi-facteurs activée avec succès !",
+ "mfa_not_enabled": "L'authentification multi-facteurs n'est pas activée",
+ "mfa_page_title": "Authentification multi-facteurs",
+ "mfa_required": "Une authentification multi-facteurs est requise",
+ "no_emai_set": "Aucune adresse de courriel définie",
+ "not_verified": "Non vérifiée",
+ "primary": "Principale",
"recovery_codes": "Codes de récupération",
- "recovery_codes_desc": "Ce sont vos codes de récupération. \nGardez-les en sécurité. \nVous ne pourrez plus les revoir.",
+ "recovery_codes_desc": "Ce sont vos codes de récupération. \nGardez-les en sécurité. \nIls ne pourront plus vous être affichés.",
"reset_session_error": "Veuillez vous déconnecter, puis vous reconnecter pour actualiser votre session et réessayer.",
- "verified": "Vérifié",
+ "verified": "Vérifiée",
"verify": "Vérifier",
- "verify_email_error": "Erreur lors de la vérification de l'e-mail. \nRéessayez dans quelques minutes.",
- "verify_email_success": "Vérification par e-mail envoyée avec succès !",
- "add_email_blocked": "Vous ne pouvez pas ajouter une adresse e-mail à un compte protégé par une authentification à deux facteurs.",
+ "verify_email_error": "Erreur lors de la vérification de l'adresse de courriel. \nRéessayez dans quelques minutes.",
+ "verify_email_success": "Vérification par courriel envoyée avec succès !",
+ "add_email_blocked": "Vous ne pouvez pas ajouter une adresse de courriel à un compte protégé par une authentification à deux facteurs.",
"required": "Ce champ est obligatoire",
"csrf_failed": "Échec de la récupération du jeton CSRF",
- "duplicate_email": "Cette adresse e-mail est déjà utilisée.",
- "email_taken": "Cette adresse e-mail est déjà utilisée.",
+ "duplicate_email": "Cette adresse de courriel est déjà utilisée.",
+ "email_taken": "Cette adresse de courriel est déjà utilisée.",
"username_taken": "Ce nom d'utilisateur est déjà utilisé.",
"administration_settings": "Paramètres d'administration",
"documentation_link": "Lien vers la documentation",
"launch_account_connections": "Lancer les connexions au compte",
"launch_administration_panel": "Lancer le panneau d'administration",
- "no_verified_email_warning": "Vous devez disposer d'une adresse e-mail vérifiée pour activer l'authentification à deux facteurs.",
- "social_auth_desc": "Activez ou désactivez les fournisseurs d'authentification sociale et OIDC pour votre compte. \nCes connexions vous permettent de vous connecter avec des fournisseurs d'identité d'authentification auto-hébergés comme Authentik ou des fournisseurs tiers comme GitHub.",
+ "no_verified_email_warning": "Vous devez disposer d'une adresse de courriel vérifiée pour activer l'authentification multi-facteurs.",
+ "social_auth_desc": "Activez ou désactivez les fournisseurs d'authentification sociale et OIDC pour votre compte. \nCes connexions vous permettent de vous connecter avec des fournisseurs d'identité auto-hébergés comme Authentik ou des fournisseurs tiers comme GitHub.",
"social_auth_desc_2": "Ces paramètres sont gérés sur le serveur AdventureLog et doivent être activés manuellement par l'administrateur.",
"social_oidc_auth": "Authentification sociale et OIDC",
- "add_email": "Ajouter un e-mail",
+ "add_email": "Ajouter une adresse de courriel",
"password_too_short": "Le mot de passe doit contenir au moins 6 caractères",
"disable_password": "Désactiver le mot de passe",
- "password_disable": "Désactiver l'authentification du mot de passe",
- "password_disable_desc": "La désactivation de l'authentification du mot de passe vous empêchera de vous connecter avec un mot de passe. \nVous devrez utiliser un fournisseur social ou OIDC pour vous connecter. Si votre fournisseur social est non lié, l'authentification du mot de passe sera automatiquement réactivé même si ce paramètre est désactivé.",
- "password_disable_warning": "Actuellement, l'authentification du mot de passe est désactivée. \nLa connexion via un fournisseur social ou OIDC est requise.",
- "password_disabled": "Authentification du mot de passe désactivé",
- "password_disabled_error": "Erreur de désactivation de l'authentification du mot de passe. \nAssurez-vous qu'un fournisseur social ou OIDC est lié à votre compte.",
- "password_enabled": "Authentification du mot de passe activé",
- "password_enabled_error": "Erreur permettant l'authentification du mot de passe."
+ "password_disable": "Désactiver l'authentification par mot de passe",
+ "password_disable_desc": "La désactivation de l'authentification par mot de passe vous empêchera de vous connecter avec un mot de passe. \nVous devrez utiliser un fournisseur social ou OIDC pour vous connecter. Si votre fournisseur social est non lié, l'authentification par mot de passe sera automatiquement réactivée même si ce paramètre est désactivé.",
+ "password_disable_warning": "Actuellement, l'authentification par mot de passe est désactivée. \nLa connexion via un fournisseur social ou OIDC est requise.",
+ "password_disabled": "Authentification par mot de passe désactivée",
+ "password_disabled_error": "Erreur de désactivation de l'authentification par mot de passe. \nAssurez-vous qu'un fournisseur social ou OIDC est lié à votre compte.",
+ "password_enabled": "Authentification par mot de passe activée",
+ "password_enabled_error": "Erreur permettant l'authentification par mot de passe."
},
"checklist": {
- "add_item": "Ajouter un article",
+ "add_item": "Ajouter un élément",
"checklist_delete_error": "Erreur lors de la suppression de la liste de contrôle",
"checklist_deleted": "Liste de contrôle supprimée avec succès !",
"checklist_editor": "Éditeur de liste de contrôle",
"checklist_public": "Cette liste de contrôle est publique car elle fait partie d’une collection publique.",
- "editing_checklist": "Liste de contrôle d'édition",
+ "editing_checklist": "Édition de la liste de contrôle",
"failed_to_save": "Échec de l'enregistrement de la liste de contrôle",
- "item": "Article",
- "item_already_exists": "L'article existe déjà",
+ "item": "Élément",
+ "item_already_exists": "L'élément existe déjà",
"item_cannot_be_empty": "L'élément ne peut pas être vide",
- "items": "Articles",
- "new_item": "Nouvel article",
+ "items": "Éléments",
+ "new_item": "Nouvel élément",
"save": "Sauvegarder",
"checklist_viewer": "Visionneuse de liste de contrôle",
"new_checklist": "Nouvelle liste de contrôle"
@@ -473,7 +473,7 @@
"notes": {
"add_a_link": "Ajouter un lien",
"content": "Contenu",
- "editing_note": "Note d'édition",
+ "editing_note": "Modification de la note",
"failed_to_save": "Échec de l'enregistrement de la note",
"note_delete_error": "Erreur lors de la suppression de la note",
"note_deleted": "Note supprimée avec succès !",
@@ -485,13 +485,13 @@
"note_viewer": "Visionneuse de notes"
},
"transportation": {
- "date_time": "Date de début",
+ "date_time": "Date de départ",
"edit": "Modifier",
- "edit_transportation": "Modifier le transport",
- "end_date_time": "Date de fin",
- "error_editing_transportation": "Erreur lors de la modification du transport",
+ "edit_transportation": "Modifier le déplacement",
+ "end_date_time": "Date d'arrivée",
+ "error_editing_transportation": "Erreur lors de la modification du déplacement",
"flight_number": "Numéro du vol",
- "from_location": "De l'emplacement",
+ "from_location": "Du lieu",
"modes": {
"bike": "Vélo",
"boat": "Bateau",
@@ -499,33 +499,33 @@
"car": "Voiture",
"other": "Autre",
"plane": "Avion",
- "train": "Former",
+ "train": "Train",
"walking": "Marche"
},
- "new_transportation": "Nouveau transport",
- "provide_start_date": "Veuillez fournir une date de début",
+ "new_transportation": "Nouveau déplacement",
+ "provide_start_date": "Veuillez fournir une date de départ",
"start": "Commencer",
- "to_location": "Vers l'emplacement",
+ "to_location": "Vers le lieu",
"transport_type": "Type de transport",
- "type": "Taper",
- "date_and_time": "Date",
- "transportation_added": "Transport ajouté avec succès !",
- "transportation_delete_error": "Erreur lors de la suppression du transport",
- "transportation_deleted": "Transport supprimé avec succès !",
- "transportation_edit_success": "Transport modifié avec succès !",
- "ending_airport_desc": "Entrez la fin du code aéroportuaire (par exemple, laxiste)",
- "fetch_location_information": "Récupérer les informations de localisation",
- "starting_airport_desc": "Entrez le code aéroport de démarrage (par exemple, JFK)"
+ "type": "Type",
+ "date_and_time": "Date et heure",
+ "transportation_added": "Déplacement ajouté avec succès !",
+ "transportation_delete_error": "Erreur lors de la suppression du déplacement",
+ "transportation_deleted": "Déplacement supprimé avec succès !",
+ "transportation_edit_success": "Déplacement modifié avec succès !",
+ "ending_airport_desc": "Entrez le code de l'aéroport de départ (par exemple, CDG)",
+ "fetch_location_information": "Récupérer les informations sur les lieux",
+ "starting_airport_desc": "Entrez le code de l'aéroport d'arrivée (par exemple, ORY)"
},
"search": {
- "adventurelog_results": "Résultats du journal d'aventure",
+ "adventurelog_results": "Résultats dans AdventureLog",
"online_results": "Résultats en ligne",
"public_adventures": "Aventures publiques"
},
"map": {
"add_adventure": "Ajouter une nouvelle aventure",
"add_adventure_at_marker": "Ajouter une nouvelle aventure au marqueur",
- "adventure_map": "Carte d'aventure",
+ "adventure_map": "Carte des aventures",
"clear_marker": "Effacer le marqueur",
"map_options": "Options de la carte",
"show_visited_regions": "Afficher les régions visitées",
@@ -533,20 +533,20 @@
},
"languages": {},
"share": {
- "no_users_shared": "Aucun utilisateur partagé avec",
- "not_shared_with": "Non partagé avec",
- "share_desc": "Partagez cette collection avec d'autres utilisateurs.",
- "shared": "Commun",
- "shared_with": "Partagé avec",
- "unshared": "Non partagé",
+ "no_users_shared": "Aucun utilisateur",
+ "not_shared_with": "Pas encore partagé avec",
+ "share_desc": "Partager cette collection avec d'autres utilisateurs.",
+ "shared": "Partagé",
+ "shared_with": "Déjà partagé avec",
+ "unshared": "Partage désactivé pour",
"with": "avec",
"go_to_settings": "Allez dans les paramètres",
- "no_shared_found": "Aucune collection trouvée partagée avec vous.",
- "set_public": "Afin de permettre aux utilisateurs de partager avec vous, vous devez définir votre profil comme public."
+ "no_shared_found": "Aucune collection ne semble encore avoir été partagée avec vous.",
+ "set_public": "Afin de permettre aux utilisateurs de partager avec vous, vous devez rendre votre profil public."
},
"profile": {
"member_since": "Membre depuis",
- "user_stats": "Statistiques des utilisateurs",
+ "user_stats": "Statistiques de l'utilisateur",
"visited_countries": "Pays visités",
"visited_regions": "Régions visitées",
"visited_cities": "Villes visitées"
@@ -563,7 +563,7 @@
"dashboard": {
"add_some": "Pourquoi ne pas commencer à planifier votre prochaine aventure ? \nVous pouvez ajouter une nouvelle aventure en cliquant sur le bouton ci-dessous.",
"countries_visited": "Pays visités",
- "no_recent_adventures": "Pas d'aventures récentes ?",
+ "no_recent_adventures": "Pas d'aventure récente ?",
"recent_adventures": "Aventures récentes",
"total_adventures": "Aventures totales",
"total_visited_regions": "Total des régions visitées",
@@ -571,8 +571,8 @@
"total_visited_cities": "Total des villes visitées"
},
"immich": {
- "api_key": "Clé API Immich",
- "api_note": "Remarque : il doit s'agir de l'URL du serveur API Immich, elle se termine donc probablement par /api, sauf si vous disposez d'une configuration personnalisée.",
+ "api_key": "Clé d'API Immich",
+ "api_note": "Remarque : il doit s'agir de l'URL de base de l'API Immich, elle se termine donc généralement par /api, sauf si vous disposez d'une configuration personnalisée.",
"disable": "Désactiver",
"enable_immich": "Activer Immich",
"imageid_required": "L'identifiant de l'image est requis",
@@ -592,7 +592,7 @@
"server_down": "Le serveur Immich est actuellement en panne ou inaccessible",
"server_url": "URL du serveur Immich",
"update_integration": "Intégration des mises à jour",
- "documentation": "Documentation d'intégration Immich",
+ "documentation": "Documentation de l'intégration Immich",
"localhost_note": "Remarque : localhost ne fonctionnera probablement pas à moins que vous n'ayez configuré les réseaux Docker en conséquence. \nIl est recommandé d'utiliser l'adresse IP du serveur ou le nom de domaine."
},
"recomendations": {
@@ -604,31 +604,31 @@
},
"lodging": {
"apartment": "Appartement",
- "bnb": "Bed and petit-déjeuner",
- "cabin": "Cabine",
+ "bnb": "Bed and Breakfast",
+ "cabin": "Châlet",
"campground": "Camping",
"check_in": "Enregistrement",
- "check_out": "Vérifier",
- "date_and_time": "Date",
+ "check_out": "Checkout",
+ "date_and_time": "Date et heure",
"edit": "Modifier",
"edit_lodging": "Modifier l'hébergement",
- "error_editing_lodging": "Édition d'erreurs Hébergement",
+ "error_editing_lodging": "Erreur lors de la modification de l'hébergement",
"hostel": "Auberge",
"hotel": "Hôtel",
"house": "Maison",
- "lodging_added": "L'hébergement a ajouté avec succès!",
+ "lodging_added": "Hébergement ajouté avec succès!",
"lodging_delete_error": "Erreur de suppression de l'hébergement",
- "lodging_deleted": "L'hébergement est supprimé avec succès!",
- "lodging_edit_success": "L'hébergement édité avec succès!",
+ "lodging_deleted": "Hébergement supprimé avec succès!",
+ "lodging_edit_success": "Hébergement modifié avec succès!",
"lodging_type": "Type d'hébergement",
"motel": "Motel",
- "new_lodging": "Nouveau logement",
+ "new_lodging": "Nouvel hébergement",
"other": "Autre",
"provide_start_date": "Veuillez fournir une date de début",
"reservation_number": "Numéro de réservation",
- "resort": "Station balnéaire",
+ "resort": "Complexe touristique",
"start": "Commencer",
- "type": "Taper",
+ "type": "Type",
"villa": "Villa",
"current_timezone": "Fuseau horaire actuel"
}
From 645cc9728b6642e7222e16eb0bb5c74aebf8ce98 Mon Sep 17 00:00:00 2001
From: Shaun Walker
Date: Sat, 3 May 2025 20:50:06 +1000
Subject: [PATCH 59/79] Update en.json - Correct spelling of Search
---
frontend/src/locales/en.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json
index 25a79de..7c4f452 100644
--- a/frontend/src/locales/en.json
+++ b/frontend/src/locales/en.json
@@ -130,7 +130,7 @@
"location": "Location",
"search_for_location": "Search for a location",
"clear_map": "Clear map",
- "search_results": "Searh results",
+ "search_results": "Search results",
"no_results": "No results found",
"wiki_desc": "Pulls excerpt from Wikipedia article matching the name of the adventure.",
"attachments": "Attachments",
From 7442bd70cd9ee8d3f0d838ed24b449c430d9204c Mon Sep 17 00:00:00 2001
From: Sean Morley
Date: Tue, 6 May 2025 14:38:31 -0400
Subject: [PATCH 60/79] Update version to 0.9.0, add DateRangeDropdown
component, enhance LodgingModal with price step, and add invalid date range
message
---
frontend/package.json | 2 +-
.../lib/components/DateRangeDropdown.svelte | 172 ++++++++++++++++++
.../src/lib/components/LodgingModal.svelte | 1 +
frontend/src/locales/en.json | 1 +
frontend/src/routes/datetest/+page.svelte | 13 ++
5 files changed, 188 insertions(+), 1 deletion(-)
create mode 100644 frontend/src/lib/components/DateRangeDropdown.svelte
create mode 100644 frontend/src/routes/datetest/+page.svelte
diff --git a/frontend/package.json b/frontend/package.json
index 96dd929..2d3d3e0 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,6 +1,6 @@
{
"name": "adventurelog-frontend",
- "version": "0.8.0",
+ "version": "0.9.0",
"scripts": {
"dev": "vite dev",
"django": "cd .. && cd backend/server && python3 manage.py runserver",
diff --git a/frontend/src/lib/components/DateRangeDropdown.svelte b/frontend/src/lib/components/DateRangeDropdown.svelte
new file mode 100644
index 0000000..608d8bd
--- /dev/null
+++ b/frontend/src/lib/components/DateRangeDropdown.svelte
@@ -0,0 +1,172 @@
+
+
+
From bbad7b890ccf4fbe55a8b16910985d823b4351fc Mon Sep 17 00:00:00 2001
From: Lars Kiesow
Date: Wed, 7 May 2025 21:10:38 +0200
Subject: [PATCH 61/79] Add support for OpenStreetMap
This patch adds an option to open an item in OpenStreetMap as well as in
Google Maps and Apple Maps.
---
.../src/routes/adventures/[id]/+page.svelte | 36 +++++++++++--------
1 file changed, 22 insertions(+), 14 deletions(-)
diff --git a/frontend/src/routes/adventures/[id]/+page.svelte b/frontend/src/routes/adventures/[id]/+page.svelte
index b32a19e..afbb3ae 100644
--- a/frontend/src/routes/adventures/[id]/+page.svelte
+++ b/frontend/src/routes/adventures/[id]/+page.svelte
@@ -458,20 +458,28 @@