From 246d836459744a8fa79450c398affb87aa38c384 Mon Sep 17 00:00:00 2001 From: alex Date: Sat, 7 Mar 2026 10:20:06 +0000 Subject: [PATCH] feat: redesign itinerary flow and add catppuccin mocha theme --- README.md | 5 +- documentation/docs/intro/voyage_overview.md | 3 + documentation/docs/usage/usage.md | 4 +- .../CollectionItineraryPlanner.svelte | 645 +++++++++++------- frontend/src/lib/index.ts | 2 + frontend/src/locales/ar.json | 5 +- frontend/src/locales/de.json | 5 +- frontend/src/locales/en.json | 4 +- frontend/src/locales/es.json | 5 +- frontend/src/locales/fr.json | 5 +- frontend/src/locales/hu.json | 7 +- frontend/src/locales/it.json | 5 +- frontend/src/locales/ja.json | 5 +- frontend/src/locales/ko.json | 5 +- frontend/src/locales/nl.json | 5 +- frontend/src/locales/no.json | 5 +- frontend/src/locales/pl.json | 5 +- frontend/src/locales/pt-br.json | 5 +- frontend/src/locales/ru.json | 5 +- frontend/src/locales/sk.json | 7 +- frontend/src/locales/sv.json | 5 +- frontend/src/locales/tr.json | 7 +- frontend/src/locales/uk.json | 5 +- frontend/src/locales/zh.json | 5 +- frontend/tailwind.config.js | 27 + 25 files changed, 503 insertions(+), 283 deletions(-) diff --git a/README.md b/README.md index d39ffdf5..71f2ee1e 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Voyage aims to be simple, beautiful, and open to everyone — inheriting Adventu Dashboard

Displays a summary of your locations, including your world travel stats.

Itinerary -

Plan your adventures and travel itinerary with a list of activities and a map view. View your trip in a variety of ways, including an itinerary list, a map view, and a calendar view. Order your plans and details to create the perfect trip.

+

Plan your adventures with a timeline-style itinerary planner. Each day shows numbered stops, compact transportation connectors between locations, and inline controls for adding places. Drag-and-drop reordering, day-level actions, and multiple views help you build the perfect trip.

Countries

Lists all the countries you have visited and plan to visit, with the ability to filter by visit status.

Regions @@ -98,11 +98,14 @@ Voyage aims to be simple, beautiful, and open to everyone — inheriting Adventu - Upload trails and activities to your locations to remember your experiences with detailed maps and stats. - **Plan Your Next Trip** 📃: Take the guesswork out of planning your next adventure with an easy-to-use itinerary planner. - Itineraries can be created for any number of days and can include multiple destinations. + - A timeline-style day view shows ordered stops with numbered markers, compact transportation connector rows (mode, duration, distance), and inline add-place rows per day. + - Day-level quick actions include Auto-fill (to populate an empty itinerary from dated records) and an Optimize placeholder for future route optimization. - Itineraries include many planning features like flight information, notes, checklists, and links to external resources. - Itineraries can be shared with friends and family for collaborative planning. - **Share Your Experiences** 📸: Share your adventures with friends and family and collaborate on trips together. - Locations and itineraries can be shared via a public link or directly with other Voyage users. - Collaborators can view and edit shared itineraries (collections), making planning a breeze. +- **Customizable Themes** 🎨: Choose from 10 built-in themes including Light, Dark, Dim, Night, Forest, Aqua, Catppuccin Mocha, Aesthetic Light, Aesthetic Dark, and Northern Lights. Theme selection persists across sessions. diff --git a/documentation/docs/intro/voyage_overview.md b/documentation/docs/intro/voyage_overview.md index 309e063b..22b094dc 100644 --- a/documentation/docs/intro/voyage_overview.md +++ b/documentation/docs/intro/voyage_overview.md @@ -14,11 +14,14 @@ Voyage is a full-fledged travel companion. With Voyage, you can log your adventu - Upload trails and activities to your locations to remember your experiences with detailed maps and stats. - **Plan Your Next Trip** 📃: Take the guesswork out of planning your next adventure with an easy-to-use itinerary planner. - Itineraries can be created for any number of days and can include multiple destinations. + - A timeline-style day view shows ordered stops with numbered markers, compact transportation connector rows (mode, duration, distance), and inline add-place rows per day. + - Day-level quick actions include Auto-fill (to populate an empty itinerary from dated records) and an Optimize placeholder for future route optimization. - Itineraries include many planning features like flight information, notes, checklists, and links to external resources. - Itineraries can be shared with friends and family for collaborative planning. - **Share Your Experiences** 📸: Share your adventures with friends and family and collaborate on trips together. - Locations and itineraries can be shared via a public link or directly with other Voyage users. - Collaborators can view and edit shared itineraries (collections), making planning a breeze. +- **Customizable Themes** 🎨: Choose from 10 built-in themes including Light, Dark, Dim, Night, Forest, Aqua, Catppuccin Mocha, Aesthetic Light, Aesthetic Dark, and Northern Lights. Theme selection persists across sessions. ## Why Voyage? diff --git a/documentation/docs/usage/usage.md b/documentation/docs/usage/usage.md index 8aaf2113..6715a0fb 100644 --- a/documentation/docs/usage/usage.md +++ b/documentation/docs/usage/usage.md @@ -22,8 +22,8 @@ The term "Location" is now used instead of "Adventure" - the usage remains the s #### Collections -- **Collection**: a collection is a way to group locations 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 locations together. When a start and end date is added to a collection, it acts like a trip to group locations 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 locations. For example, you could have a collection for a trip to Europe with dates so you can plan where you want to visit, a collection of local hiking trails, or a collection for a list of restaurants you want to try. -- **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. +- **Collection**: a collection is a way to group locations 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 locations together. When a start and end date is added to a collection, it acts like a trip to group locations together that were visited during that time period. With start and end dates, the collection is transformed into a full itinerary with a timeline-style day view — each day displays numbered stops, compact transportation connector rows, and an inline add-place row. Day-level quick actions include Auto-fill (populates an empty itinerary from dated records) and an Optimize placeholder for future route optimization. The itinerary also includes a map showing the route taken between locations. For example, you could have a collection for a trip to Europe with dates so you can plan where you want to visit, a collection of local hiking trails, or a collection for a list of restaurants you want to try. +- **Transportation**: a transportation is a collection exclusive feature that allows you to add transportation information to your trip. In the itinerary timeline view, transportation items appear as compact connector rows between stops — showing the travel mode, duration, and distance. 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. diff --git a/frontend/src/lib/components/collections/CollectionItineraryPlanner.svelte b/frontend/src/lib/components/collections/CollectionItineraryPlanner.svelte index 2a6e2fbe..0ba47415 100644 --- a/frontend/src/lib/components/collections/CollectionItineraryPlanner.svelte +++ b/frontend/src/lib/components/collections/CollectionItineraryPlanner.svelte @@ -35,6 +35,7 @@ import { t } from 'svelte-i18n'; import { addToast } from '$lib/toasts'; import Globe from '~icons/mdi/globe'; + import { TRANSPORTATION_TYPES_ICONS } from '$lib'; export let collection: Collection; export let user: any; @@ -396,6 +397,74 @@ return value.includes('T') ? value.split('T')[0] : value; } + function getTransportationIcon(type: string | null | undefined) { + if (type && type in TRANSPORTATION_TYPES_ICONS) { + return TRANSPORTATION_TYPES_ICONS[type as keyof typeof TRANSPORTATION_TYPES_ICONS]; + } + return '🚗'; + } + + function formatTransportationDuration(minutes: number | null | undefined): string | null { + if (minutes === null || minutes === undefined || Number.isNaN(minutes)) return null; + const safeMinutes = Math.max(0, Math.floor(minutes)); + const hours = Math.floor(safeMinutes / 60); + const mins = safeMinutes % 60; + const parts = [] as string[]; + if (hours) parts.push(`${hours}h`); + parts.push(`${mins}m`); + return parts.join(' '); + } + + function formatTransportationDistance(distanceKm: number | null | undefined): string | null { + if (distanceKm === null || distanceKm === undefined || Number.isNaN(distanceKm)) return null; + if (distanceKm < 10) return `${distanceKm.toFixed(1)} km`; + return `${Math.round(distanceKm)} km`; + } + + function editTransportationInline(transportation: Transportation) { + handleEditTransportation({ detail: transportation } as CustomEvent); + } + + async function removeItineraryEntry(item: CollectionItineraryItem) { + if (!item?.id) return; + try { + const res = await fetch(`/api/itineraries/${item.id}`, { + method: 'DELETE' + }); + if (!res.ok) throw new Error('Failed to remove itinerary item'); + handleRemoveItineraryItem(new CustomEvent('removeFromItinerary', { detail: item }) as any); + addToast('info', $t('itinerary.item_remove_success')); + } catch (error) { + console.error('Error removing itinerary item:', error); + addToast('error', $t('itinerary.item_remove_error')); + } + } + + async function deleteTransportationFromItinerary( + item: CollectionItineraryItem, + transportation: Transportation + ) { + const confirmed = window.confirm($t('adventures.transportation_delete_confirm')); + if (!confirmed) return; + + try { + const res = await fetch(`/api/transportations/${transportation.id}`, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json' + } + }); + + if (!res.ok) throw new Error('Failed to delete transportation'); + + addToast('info', $t('transportation.transportation_deleted')); + handleItemDelete(new CustomEvent('delete', { detail: transportation.id }) as any); + } catch (error) { + console.error('Failed to delete transportation:', error); + addToast('error', $t('transportation.transportation_delete_error')); + } + } + function upsertNote(note: Note) { const notes = collection.notes ? [...collection.notes] : []; const idx = notes.findIndex((n) => n.id === note.id); @@ -1944,7 +2013,7 @@ {/if} - +
{#if savingDay === day.date}
@@ -1956,111 +2025,19 @@ {/if} {#if canModify} - + {/if}
- +
{#if day.items.length === 0}
handleDndConsider(dayIndex, e)} on:finalize={(e) => handleDndFinalize(dayIndex, e)} - class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-3" + class="space-y-3" > {#each day.items as item, index (item.id)} {@const objectType = item.item?.type || ''} @@ -2091,173 +2068,223 @@ {@const isDraggingShadow = item[SHADOW_ITEM_MARKER_PROPERTY_NAME]}
{#if resolvedObj} - - {#if canModify} -
+
+
- - - + {index + 1}
+ {#if index < day.items.length - 1} +
+ {/if}
- {/if} - - - - {#if multiDay && objectType === 'lodging'} -
-
- + {#if canModify} +
- - - {$t('itinerary.multi_day')} -
-
- {/if} +
+ + + +
+
+ {/if} - -
- - {#if objectType === 'location'} - moveItemToGlobal(e.detail.type, e.detail.id)} - {user} - {collection} - compact={true} - on:changeDay={(e) => - handleOpenDayPickerForItem( - e.detail.type, - e.detail.item, - e.detail.forcePicker, - day.date - )} - /> - {:else if objectType === 'transportation'} - moveItemToGlobal(e.detail.type, e.detail.id)} - on:changeDay={(e) => - handleOpenDayPickerForItem( - e.detail.type, - e.detail.item, - e.detail.forcePicker, - day.date - )} - /> - {:else if objectType === 'lodging'} - moveItemToGlobal(e.detail.type, e.detail.id)} - on:changeDay={(e) => - handleOpenDayPickerForItem( - e.detail.type, - e.detail.item, - e.detail.forcePicker, - day.date - )} - /> - {:else if objectType === 'note'} - - moveItemToGlobal(e.detail.type, e.detail.id)} - on:changeDay={(e) => - handleOpenDayPickerForItem( - e.detail.type, - e.detail.item, - e.detail.forcePicker, - day.date - )} - /> - {:else if objectType === 'checklist'} - - moveItemToGlobal(e.detail.type, e.detail.id)} - on:changeDay={(e) => - handleOpenDayPickerForItem( - e.detail.type, - e.detail.item, - e.detail.forcePicker, - day.date - )} - /> - {/if} + {#if objectType === 'transportation'} +
+
+
+ {getTransportationIcon(resolvedObj.type)} +

{resolvedObj.name}

+ + {$t(`transportation.modes.${resolvedObj.type}`) || + resolvedObj.type} + +
+
+ {#if formatTransportationDuration(resolvedObj.travel_duration_minutes)} + {formatTransportationDuration( + resolvedObj.travel_duration_minutes + )} + {/if} + {#if formatTransportationDistance(resolvedObj.distance)} + {formatTransportationDistance(resolvedObj.distance)} + {/if} +
+
+
+ {resolvedObj.from_location || '—'} → {resolvedObj.to_location || + '—'} +
+ {#if canModify} +
+ + + + + +
+ {/if} +
+ {:else} + {#if multiDay && objectType === 'lodging'} +
+
+ {$t('itinerary.multi_day')} +
+
+ {/if} + + {#if objectType === 'location'} + + moveItemToGlobal(e.detail.type, e.detail.id)} + {user} + {collection} + compact={true} + on:changeDay={(e) => + handleOpenDayPickerForItem( + e.detail.type, + e.detail.item, + e.detail.forcePicker, + day.date + )} + /> + {:else if objectType === 'lodging'} + + moveItemToGlobal(e.detail.type, e.detail.id)} + on:changeDay={(e) => + handleOpenDayPickerForItem( + e.detail.type, + e.detail.item, + e.detail.forcePicker, + day.date + )} + /> + {:else if objectType === 'note'} + + moveItemToGlobal(e.detail.type, e.detail.id)} + on:changeDay={(e) => + handleOpenDayPickerForItem( + e.detail.type, + e.detail.item, + e.detail.forcePicker, + day.date + )} + /> + {:else if objectType === 'checklist'} + + moveItemToGlobal(e.detail.type, e.detail.id)} + on:changeDay={(e) => + handleOpenDayPickerForItem( + e.detail.type, + e.detail.item, + e.detail.forcePicker, + day.date + )} + /> + {/if} + {/if} +
{:else} @@ -2269,6 +2296,108 @@ {/each}
{/if} + + {#if canModify} +
+
+

{$t('itinerary.add_place')}

+ +
+
+ {/if}
diff --git a/frontend/src/lib/index.ts b/frontend/src/lib/index.ts index c932f181..714e879a 100644 --- a/frontend/src/lib/index.ts +++ b/frontend/src/lib/index.ts @@ -462,6 +462,7 @@ export let themes = [ { name: 'night', label: 'Night' }, { name: 'forest', label: 'Forest' }, { name: 'aqua', label: 'Aqua' }, + { name: 'catppuccinMocha', label: 'Catppuccin Mocha' }, { name: 'aestheticLight', label: 'Aesthetic Light' }, { name: 'aestheticDark', label: 'Aesthetic Dark' }, { name: 'northernLights', label: 'Northern Lights' } @@ -601,6 +602,7 @@ export function getIsDarkMode() { const isDark = theme === 'dark' || theme === 'night' || + theme === 'catppuccinMocha' || theme === 'aestheticDark' || theme === 'northernLights' || theme === 'forest' || diff --git a/frontend/src/locales/ar.json b/frontend/src/locales/ar.json index 7aec2fe2..4ee81234 100644 --- a/frontend/src/locales/ar.json +++ b/frontend/src/locales/ar.json @@ -704,6 +704,7 @@ "aqua": "أكوا", "dark": "مظلم", "dim": "خافت", + "catppuccinMocha": "Catppuccin Mocha", "forest": "غابة", "light": "ضوء", "night": "ليلة", @@ -1138,6 +1139,8 @@ "trip_context": "سياق الرحلة", "trip_context_info": "تنطبق عناصر سياق الرحلة على الرحلة بأكملها — على سبيل المثال المواقع التي تمثل الوجهة نفسها، أو الملاحظات العامة، أو قوائم التعبئة المهمة للرحلة بأكملها.", "unscheduled_items": "العناصر غير المجدولة", - "unscheduled_items_desc": "ترتبط هذه العناصر بهذه الرحلة ولكن لم تتم إضافتها إلى يوم محدد بعد." + "unscheduled_items_desc": "ترتبط هذه العناصر بهذه الرحلة ولكن لم تتم إضافتها إلى يوم محدد بعد.", + "optimize": "Optimize", + "add_place": "+ Add place" } } diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index 907cd86e..e2063126 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -481,6 +481,7 @@ "aqua": "Aqua", "dark": "Dunkel", "dim": "Düster", + "catppuccinMocha": "Catppuccin Mocha", "forest": "Wald", "light": "Hell", "night": "Nacht", @@ -1138,6 +1139,8 @@ "trip_context": "Reisekontext", "trip_context_info": "Reisekontextelemente gelten für die gesamte Reise – zum Beispiel Orte, die das Ziel selbst darstellen, allgemeine Notizen oder Packlisten, die für die gesamte Reise wichtig sind.", "unscheduled_items": "Ungeplante Einträge", - "unscheduled_items_desc": "Diese Einträge sind mit dieser Reise verknüpft, wurden aber noch keinem bestimmten Tag hinzugefügt." + "unscheduled_items_desc": "Diese Einträge sind mit dieser Reise verknüpft, wurden aber noch keinem bestimmten Tag hinzugefügt.", + "optimize": "Optimize", + "add_place": "+ Add place" } } diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 0412741b..ee3667f6 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -1096,7 +1096,9 @@ "failed_to_add_to_trip_context": "Failed to add item to trip context", "remove_from_trip_context": "Remove from Context", "drag_to_reorder": "Drag to reorder", - "add_to_day": "Add to day" + "add_to_day": "Add to day", + "optimize": "Optimize", + "add_place": "+ Add place" }, "common": { "show_less": "Hide details", diff --git a/frontend/src/locales/es.json b/frontend/src/locales/es.json index d7fbfd47..7b777de9 100644 --- a/frontend/src/locales/es.json +++ b/frontend/src/locales/es.json @@ -18,6 +18,7 @@ "aqua": "Agua", "dark": "Oscuro", "dim": "Oscuro", + "catppuccinMocha": "Catppuccin Mocha", "forest": "Bosque", "light": "Luz", "night": "Noche", @@ -1138,6 +1139,8 @@ "trip_context": "Contexto del viaje", "trip_context_info": "Los elementos del contexto del viaje se aplican a todo el viaje; por ejemplo, ubicaciones que son el destino en sí, notas generales o listas de equipaje que son importantes para todo el viaje.", "unscheduled_items": "Artículos no programados", - "unscheduled_items_desc": "Estos elementos están vinculados a este viaje pero aún no se han agregado a un día específico." + "unscheduled_items_desc": "Estos elementos están vinculados a este viaje pero aún no se han agregado a un día específico.", + "optimize": "Optimize", + "add_place": "+ Add place" } } diff --git a/frontend/src/locales/fr.json b/frontend/src/locales/fr.json index 85bada39..cf36e265 100644 --- a/frontend/src/locales/fr.json +++ b/frontend/src/locales/fr.json @@ -481,6 +481,7 @@ "aqua": "Aqua", "dark": "Sombre", "dim": "Faible", + "catppuccinMocha": "Catppuccin Mocha", "forest": "Forêt", "light": "Lumière", "night": "Nuit", @@ -1138,6 +1139,8 @@ "trip_context_info": "Les éléments de contexte du voyage s'appliquent à l'ensemble du voyage, par exemple les lieux qui constituent la destination elle-même, les notes générales ou les listes de colisage importantes pour l'ensemble du voyage.", "unscheduled_items": "Éléments non planifiés", "unscheduled_items_desc": "Ces éléments sont liés à ce voyage mais n'ont pas encore été ajoutés à un jour spécifique.", - "link_existing_item": "Lier un élément existant" + "link_existing_item": "Lier un élément existant", + "optimize": "Optimize", + "add_place": "+ Add place" } } diff --git a/frontend/src/locales/hu.json b/frontend/src/locales/hu.json index aebfee6d..e49bb468 100644 --- a/frontend/src/locales/hu.json +++ b/frontend/src/locales/hu.json @@ -25,7 +25,8 @@ "aestheticDark": "Esztétikus sötét", "aqua": "Akvamarin", "northernLights": "Sarki fény", - "dim": "Halvány" + "dim": "Halvány", + "catppuccinMocha": "Catppuccin Mocha" }, "navigation": "Navigáció", "worldtravel": "Világutazás" @@ -1138,6 +1139,8 @@ "trip_context": "Utazási kontextus", "trip_context_info": "Az utazási kontextus elemei az egész utazásra vonatkoznak – például olyan helyek, amelyek maga az úti cél, általános megjegyzések vagy csomaglisták, amelyek az egész utazás szempontjából fontosak.", "unscheduled_items": "Nem ütemezett tételek", - "unscheduled_items_desc": "Ezek az elemek ehhez az utazáshoz kapcsolódnak, de még nem adták hozzá egy adott naphoz." + "unscheduled_items_desc": "Ezek az elemek ehhez az utazáshoz kapcsolódnak, de még nem adták hozzá egy adott naphoz.", + "optimize": "Optimize", + "add_place": "+ Add place" } } diff --git a/frontend/src/locales/it.json b/frontend/src/locales/it.json index a7f9adf2..d7a91a6c 100644 --- a/frontend/src/locales/it.json +++ b/frontend/src/locales/it.json @@ -481,6 +481,7 @@ "aqua": "Aqua", "dark": "Buio", "dim": "Fioco", + "catppuccinMocha": "Catppuccin Mocha", "forest": "Foresta", "light": "Leggero", "night": "Notte", @@ -1138,6 +1139,8 @@ "trip_context": "Contesto del viaggio", "trip_context_info": "Gli elementi contestuali del viaggio si applicano all'intero viaggio, ad esempio luoghi che rappresentano la destinazione stessa, note generali o liste di cose da portare importanti per l'intero viaggio.", "unscheduled_items": "Elementi non pianificati", - "unscheduled_items_desc": "Questi elementi sono collegati a questo viaggio ma non sono ancora stati aggiunti a un giorno specifico." + "unscheduled_items_desc": "Questi elementi sono collegati a questo viaggio ma non sono ancora stati aggiunti a un giorno specifico.", + "optimize": "Optimize", + "add_place": "+ Add place" } } diff --git a/frontend/src/locales/ja.json b/frontend/src/locales/ja.json index 39d1c8e0..2782895f 100644 --- a/frontend/src/locales/ja.json +++ b/frontend/src/locales/ja.json @@ -704,6 +704,7 @@ "aqua": "アクア", "dark": "暗い", "dim": "薄暗い", + "catppuccinMocha": "Catppuccin Mocha", "forest": "森", "light": "ライト", "night": "夜", @@ -1138,6 +1139,8 @@ "trip_context": "旅行のコンテキスト", "trip_context_info": "旅行コンテキスト項目は、旅行全体に適用されます。たとえば、目的地そのものである場所、一般的なメモ、旅行全体にとって重要な持ち物リストなどです。", "unscheduled_items": "予定外の項目", - "unscheduled_items_desc": "これらのアイテムはこの旅行にリンクされていますが、まだ特定の日に追加されていません。" + "unscheduled_items_desc": "これらのアイテムはこの旅行にリンクされていますが、まだ特定の日に追加されていません。", + "optimize": "Optimize", + "add_place": "+ Add place" } } diff --git a/frontend/src/locales/ko.json b/frontend/src/locales/ko.json index b9a8c4bd..31d1954d 100644 --- a/frontend/src/locales/ko.json +++ b/frontend/src/locales/ko.json @@ -644,6 +644,7 @@ "aqua": "아쿠아", "dark": "어두운", "dim": "어둑한", + "catppuccinMocha": "Catppuccin Mocha", "forest": "숲", "light": "빛", "night": "밤", @@ -1138,6 +1139,8 @@ "trip_context": "여행 상황", "trip_context_info": "여행 컨텍스트 항목은 전체 여행에 적용됩니다. 예를 들어 목적지 자체인 위치, 일반 참고 사항, 전체 여행에 중요한 짐 목록 등이 있습니다.", "unscheduled_items": "예정되지 않은 품목", - "unscheduled_items_desc": "이 항목은 이 여행에 연결되어 있지만 아직 특정 날짜에 추가되지 않았습니다." + "unscheduled_items_desc": "이 항목은 이 여행에 연결되어 있지만 아직 특정 날짜에 추가되지 않았습니다.", + "optimize": "Optimize", + "add_place": "+ Add place" } } diff --git a/frontend/src/locales/nl.json b/frontend/src/locales/nl.json index 1fd266c6..715ad3d9 100644 --- a/frontend/src/locales/nl.json +++ b/frontend/src/locales/nl.json @@ -481,6 +481,7 @@ "aqua": "Aqua", "dark": "Donker", "dim": "Duister", + "catppuccinMocha": "Catppuccin Mocha", "forest": "Woud", "light": "Licht", "night": "Nacht", @@ -1138,6 +1139,8 @@ "trip_context": "Reiscontext", "trip_context_info": "Reiscontextitems zijn van toepassing op de hele reis, bijvoorbeeld locaties die de bestemming zelf vormen, algemene opmerkingen of paklijsten die belangrijk zijn voor de hele reis.", "unscheduled_items": "Niet-geplande items", - "unscheduled_items_desc": "Deze items zijn gekoppeld aan deze reis, maar nog niet toegevoegd aan een specifieke dag." + "unscheduled_items_desc": "Deze items zijn gekoppeld aan deze reis, maar nog niet toegevoegd aan een specifieke dag.", + "optimize": "Optimize", + "add_place": "+ Add place" } } diff --git a/frontend/src/locales/no.json b/frontend/src/locales/no.json index 07cf0645..43d14ca3 100644 --- a/frontend/src/locales/no.json +++ b/frontend/src/locales/no.json @@ -22,6 +22,7 @@ "aqua": "Aqua", "dark": "Mørk", "dim": "Svak", + "catppuccinMocha": "Catppuccin Mocha", "forest": "Skog", "light": "Lys", "night": "Natt", @@ -1138,6 +1139,8 @@ "trip_context": "Turkontekst", "trip_context_info": "Turkontekstelementer gjelder for hele turen – for eksempel steder som er selve destinasjonen, generelle notater eller pakkelister som er viktige for hele turen.", "unscheduled_items": "Ikke-planlagte elementer", - "unscheduled_items_desc": "Disse elementene er knyttet til denne turen, men har ikke blitt lagt til en bestemt dag ennå." + "unscheduled_items_desc": "Disse elementene er knyttet til denne turen, men har ikke blitt lagt til en bestemt dag ennå.", + "optimize": "Optimize", + "add_place": "+ Add place" } } diff --git a/frontend/src/locales/pl.json b/frontend/src/locales/pl.json index 49e89f3a..72a6b207 100644 --- a/frontend/src/locales/pl.json +++ b/frontend/src/locales/pl.json @@ -20,6 +20,7 @@ "aqua": "Aqua", "dark": "Ciemny", "dim": "Ciemny", + "catppuccinMocha": "Catppuccin Mocha", "forest": "Las", "light": "Światło", "night": "Noc", @@ -1138,6 +1139,8 @@ "trip_context": "Kontekst podróży", "trip_context_info": "Elementy kontekstu podróży dotyczą całej podróży — na przykład lokalizacje będące samym celem podróży, uwagi ogólne lub listy rzeczy do spakowania ważne dla całej podróży.", "unscheduled_items": "Niezaplanowane pozycje", - "unscheduled_items_desc": "Te elementy są powiązane z tą podróżą, ale nie zostały jeszcze dodane do konkretnego dnia." + "unscheduled_items_desc": "Te elementy są powiązane z tą podróżą, ale nie zostały jeszcze dodane do konkretnego dnia.", + "optimize": "Optimize", + "add_place": "+ Add place" } } diff --git a/frontend/src/locales/pt-br.json b/frontend/src/locales/pt-br.json index 8a025fca..4fa73db6 100644 --- a/frontend/src/locales/pt-br.json +++ b/frontend/src/locales/pt-br.json @@ -704,6 +704,7 @@ "aqua": "Aqua", "dark": "Escuro", "dim": "Escurecido", + "catppuccinMocha": "Catppuccin Mocha", "forest": "Floresta", "light": "Claro", "night": "Noite", @@ -1138,6 +1139,8 @@ "trip_context": "Contexto da viagem", "trip_context_info": "Os itens de contexto da viagem aplicam-se a toda a viagem — por exemplo, locais que são o próprio destino, notas gerais ou listas de embalagem que são importantes para toda a viagem.", "unscheduled_items": "Itens não programados", - "unscheduled_items_desc": "Esses itens estão vinculados a esta viagem, mas ainda não foram adicionados a um dia específico." + "unscheduled_items_desc": "Esses itens estão vinculados a esta viagem, mas ainda não foram adicionados a um dia específico.", + "optimize": "Optimize", + "add_place": "+ Add place" } } diff --git a/frontend/src/locales/ru.json b/frontend/src/locales/ru.json index 3075f778..260d9084 100644 --- a/frontend/src/locales/ru.json +++ b/frontend/src/locales/ru.json @@ -22,6 +22,7 @@ "aqua": "Аква", "dark": "Темный", "dim": "Тусклый", + "catppuccinMocha": "Catppuccin Mocha", "forest": "Лес", "light": "Свет", "night": "Ночь", @@ -1138,6 +1139,8 @@ "trip_context_info": "Элементы контекста поездки применяются ко всей поездке — например, места, которые являются пунктом назначения, общие заметки или упаковочные листы, важные для всей поездки.", "unscheduled_items": "Незапланированные предметы", "unscheduled_items_desc": "Эти элементы связаны с этой поездкой, но еще не добавлены к определенному дню.", - "link_existing_item": "Связать существующий элемент" + "link_existing_item": "Связать существующий элемент", + "optimize": "Optimize", + "add_place": "+ Add place" } } diff --git a/frontend/src/locales/sk.json b/frontend/src/locales/sk.json index 30d1fb20..e375e131 100644 --- a/frontend/src/locales/sk.json +++ b/frontend/src/locales/sk.json @@ -25,7 +25,8 @@ "aestheticDark": "Estetická tmavá", "aqua": "Aqua", "northernLights": "Polárna žiara", - "dim": "Tlmená" + "dim": "Tlmená", + "catppuccinMocha": "Catppuccin Mocha" }, "navigation": "Navigácia", "worldtravel": "Svetové cestovanie" @@ -1138,6 +1139,8 @@ "trip_context": "Kontext cesty", "trip_context_info": "Kontextové položky cesty sa vzťahujú na celú cestu – napríklad miesta, ktoré sú samotným cieľom, všeobecné poznámky alebo zoznamy balíkov, ktoré sú dôležité pre celú cestu.", "unscheduled_items": "Neplánované položky", - "unscheduled_items_desc": "Tieto položky sú spojené s touto cestou, ale zatiaľ neboli pridané ku konkrétnemu dňu." + "unscheduled_items_desc": "Tieto položky sú spojené s touto cestou, ale zatiaľ neboli pridané ku konkrétnemu dňu.", + "optimize": "Optimize", + "add_place": "+ Add place" } } diff --git a/frontend/src/locales/sv.json b/frontend/src/locales/sv.json index c36d1040..76dc160c 100644 --- a/frontend/src/locales/sv.json +++ b/frontend/src/locales/sv.json @@ -481,6 +481,7 @@ "aqua": "Vatten", "dark": "Mörk", "dim": "Dämpad", + "catppuccinMocha": "Catppuccin Mocha", "forest": "Skog", "light": "Ljus", "night": "Natt", @@ -1138,6 +1139,8 @@ "trip_context": "Resans sammanhang", "trip_context_info": "Resans kontextobjekt gäller för hela resan – till exempel platser som är själva destinationen, allmänna anteckningar eller packlistor som är viktiga för hela resan.", "unscheduled_items": "Oschemalagda objekt", - "unscheduled_items_desc": "Dessa objekt är länkade till denna resa men har inte lagts till en specifik dag än." + "unscheduled_items_desc": "Dessa objekt är länkade till denna resa men har inte lagts till en specifik dag än.", + "optimize": "Optimize", + "add_place": "+ Add place" } } diff --git a/frontend/src/locales/tr.json b/frontend/src/locales/tr.json index a277dca5..4e8938e1 100644 --- a/frontend/src/locales/tr.json +++ b/frontend/src/locales/tr.json @@ -25,7 +25,8 @@ "aestheticDark": "Estetik Karanlık", "aqua": "Su", "northernLights": "Kuzey Işıkları", - "dim": "Loş" + "dim": "Loş", + "catppuccinMocha": "Catppuccin Mocha" }, "navigation": "Navigasyon", "worldtravel": "Dünya Seyahati" @@ -1138,6 +1139,8 @@ "trip_context": "Seyahat İçeriği", "trip_context_info": "Seyahat bağlamı öğeleri seyahatin tamamı için geçerlidir; örneğin varış noktasının kendisi olan konumlar, genel notlar veya seyahatin tamamı için önemli olan paket listeleri.", "unscheduled_items": "Planlanmamış Öğeler", - "unscheduled_items_desc": "Bu öğeler bu geziye bağlı ancak henüz belirli bir güne eklenmedi." + "unscheduled_items_desc": "Bu öğeler bu geziye bağlı ancak henüz belirli bir güne eklenmedi.", + "optimize": "Optimize", + "add_place": "+ Add place" } } diff --git a/frontend/src/locales/uk.json b/frontend/src/locales/uk.json index 5188378a..53eebd64 100644 --- a/frontend/src/locales/uk.json +++ b/frontend/src/locales/uk.json @@ -705,6 +705,7 @@ "aqua": "Аква", "dark": "Темний", "dim": "тьмяний", + "catppuccinMocha": "Catppuccin Mocha", "forest": "Ліс", "light": "світло", "night": "ніч", @@ -1138,6 +1139,8 @@ "trip_context": "Контекст поїздки", "trip_context_info": "Елементи контексту подорожі застосовуються до всієї подорожі — наприклад, місця, які є самим пунктом призначення, загальні примітки або пакувальні листи, важливі для всієї подорожі.", "unscheduled_items": "Позапланові пункти", - "unscheduled_items_desc": "Ці елементи пов’язані з цією поїздкою, але ще не додані до певного дня." + "unscheduled_items_desc": "Ці елементи пов’язані з цією поїздкою, але ще не додані до певного дня.", + "optimize": "Optimize", + "add_place": "+ Add place" } } diff --git a/frontend/src/locales/zh.json b/frontend/src/locales/zh.json index 2e6a0a44..5afbf0b3 100644 --- a/frontend/src/locales/zh.json +++ b/frontend/src/locales/zh.json @@ -17,6 +17,7 @@ "aqua": "水色", "dark": "深色", "dim": "昏暗", + "catppuccinMocha": "Catppuccin Mocha", "forest": "森林", "light": "浅色", "night": "夜色", @@ -1098,7 +1099,9 @@ "remove_from_trip_context": "从上下文中删除", "drag_to_reorder": "拖动以重新排序", "add_to_day": "添加到日期", - "add_to_trip_context": "添加旅行背景" + "add_to_trip_context": "添加旅行背景", + "optimize": "Optimize", + "add_place": "+ Add place" }, "collections": { "all_items": "所有项目", diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index 91281aa4..560f5080 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -61,6 +61,33 @@ export default { fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace' }, + catppuccinMocha: { + primary: '#89b4fa', + 'primary-focus': '#74c7ec', + 'primary-content': '#1e1e2e', + + secondary: '#f5c2e7', + 'secondary-focus': '#cba6f7', + 'secondary-content': '#1e1e2e', + + accent: '#a6e3a1', + 'accent-focus': '#94e2d5', + 'accent-content': '#1e1e2e', + + neutral: '#313244', + 'neutral-focus': '#45475a', + 'neutral-content': '#cdd6f4', + + 'base-100': '#1e1e2e', + 'base-200': '#181825', + 'base-300': '#11111b', + 'base-content': '#cdd6f4', + + info: '#89dceb', + success: '#a6e3a1', + warning: '#f9e2af', + error: '#f38ba8' + }, aestheticLight: { primary: '#5a7c65', 'primary-focus': '#48604f',