v2.5.0 — Addon System, Vacay, Atlas, Dashboard Widgets & Mobile Overhaul
The biggest NOMAD update yet. Introduces a modular addon architecture and three major new features. Addon System: - Admin panel addon management with enable/disable toggles - Trip addons (Packing List, Budget, Documents) dynamically show/hide in trip tabs - Global addons appear in the main navigation for all users Vacay — Vacation Day Planner (Global Addon): - Monthly calendar view with international public holidays (100+ countries via Nager.Date API) - Company holidays with auto-cleanup of conflicting entries - User-based system: each NOMAD user is a person in the calendar - Fusion system: invite other users to share a combined calendar with real-time WebSocket sync - Vacation entitlement tracking with automatic carry-over to next year - Full settings: block weekends, public holidays, company holidays, carry-over toggle - Invite/accept/decline flow with forced confirmation modal - Color management per user with collision detection on fusion - Dissolve fusion with preserved entries Atlas — Travel World Map (Global Addon): - Fullscreen Leaflet world map with colored country polygons (GeoJSON) - Glass-effect bottom panel with stats, continent breakdown, streak tracking - Country tooltips with trip count, places visited, first/last visit dates - Liquid glass hover effect on the stats panel - Canvas renderer with tile preloading for maximum performance - Responsive: mobile stats bars, no zoom controls on touch Dashboard Widgets: - Currency converter with 50 currencies, CustomSelect dropdowns, localStorage persistence - Timezone widget with customizable city list, live updating clock - Per-user toggle via settings button, bottom sheet on mobile Admin Panel: - Consistent dark mode across all tabs (CSS variable overrides) - Online/offline status badges on user list via WebSocket - Unified heading sizes and subtitles across all sections - Responsive tab grid on mobile Mobile Improvements: - Vacay: slide-in sidebar drawer, floating toolbar, responsive calendar grid - Atlas: top/bottom glass stat bars, no popups - Trip Planner: fixed position content container prevents overscroll, portal-based sidebar buttons - Dashboard: fixed viewport container, mobile widget bottom sheet - Admin: responsive tab grid, compact buttons - Global: overscroll-behavior fixes, modal scroll containment Other: - Trip tab labels: Planung→Karte, Packliste→Liste, Buchungen→Buchung (DE mobile) - Reservation form responsive layout - Backup panel responsive buttons
This commit is contained in:
@@ -48,6 +48,9 @@ const de = {
|
||||
'dashboard.subtitle.activeMany': '{count} aktive Reisen',
|
||||
'dashboard.subtitle.archivedSuffix': ' · {count} archiviert',
|
||||
'dashboard.newTrip': 'Neue Reise',
|
||||
'dashboard.currency': 'Währung',
|
||||
'dashboard.timezone': 'Zeitzonen',
|
||||
'dashboard.localTime': 'Lokal',
|
||||
'dashboard.emptyTitle': 'Noch keine Reisen',
|
||||
'dashboard.emptyText': 'Erstelle deine erste Reise und beginne mit der Planung von Orten, Tagesabläufen und Packlisten.',
|
||||
'dashboard.emptyButton': 'Erste Reise erstellen',
|
||||
@@ -227,6 +230,7 @@ const de = {
|
||||
'admin.allowRegistration': 'Registrierung erlauben',
|
||||
'admin.allowRegistrationHint': 'Neue Benutzer können sich selbst registrieren',
|
||||
'admin.apiKeys': 'API-Schlüssel',
|
||||
'admin.apiKeysHint': 'Optional. Aktiviert erweiterte Ortsdaten wie Fotos und Wetter.',
|
||||
'admin.mapsKey': 'Google Maps API Key',
|
||||
'admin.mapsKeyHint': 'Für Ortsuche benötigt. Erstellen unter console.cloud.google.com',
|
||||
'admin.mapsKeyHintLong': 'Ohne API Key wird OpenStreetMap für die Ortssuche genutzt. Mit Google API Key können zusätzlich Bilder, Bewertungen und Öffnungszeiten geladen werden. Erstellen unter console.cloud.google.com.',
|
||||
@@ -244,11 +248,137 @@ const de = {
|
||||
'admin.oidcIssuerHint': 'Die OpenID Connect Issuer URL des Anbieters. z.B. https://accounts.google.com',
|
||||
'admin.oidcSaved': 'OIDC-Konfiguration gespeichert',
|
||||
|
||||
// Addons
|
||||
'admin.tabs.addons': 'Addons',
|
||||
'admin.addons.title': 'Addons',
|
||||
'admin.addons.subtitle': 'Aktiviere oder deaktiviere Funktionen, um NOMAD nach deinen Wünschen anzupassen.',
|
||||
'admin.addons.enabled': 'Aktiviert',
|
||||
'admin.addons.disabled': 'Deaktiviert',
|
||||
'admin.addons.type.trip': 'Trip',
|
||||
'admin.addons.type.global': 'Global',
|
||||
'admin.addons.tripHint': 'Verfügbar als Tab innerhalb jedes Trips',
|
||||
'admin.addons.globalHint': 'Verfügbar als eigenständiger Bereich in der Navigation',
|
||||
'admin.addons.toast.updated': 'Addon aktualisiert',
|
||||
'admin.addons.toast.error': 'Addon konnte nicht aktualisiert werden',
|
||||
'admin.addons.noAddons': 'Keine Addons verfügbar',
|
||||
|
||||
// Vacay addon
|
||||
'vacay.subtitle': 'Urlaubstage planen und verwalten',
|
||||
'vacay.settings': 'Einstellungen',
|
||||
'vacay.year': 'Jahr',
|
||||
'vacay.addYear': 'Jahr hinzufügen',
|
||||
'vacay.removeYear': 'Jahr entfernen',
|
||||
'vacay.removeYearConfirm': '{year} entfernen?',
|
||||
'vacay.removeYearHint': 'Alle Urlaubseinträge und Betriebsferien für dieses Jahr werden unwiderruflich gelöscht.',
|
||||
'vacay.remove': 'Entfernen',
|
||||
'vacay.persons': 'Personen',
|
||||
'vacay.noPersons': 'Keine Personen angelegt',
|
||||
'vacay.addPerson': 'Person hinzufügen',
|
||||
'vacay.editPerson': 'Person bearbeiten',
|
||||
'vacay.removePerson': 'Person entfernen',
|
||||
'vacay.removePersonConfirm': '{name} wirklich entfernen?',
|
||||
'vacay.removePersonHint': 'Alle Urlaubseinträge dieser Person werden unwiderruflich gelöscht.',
|
||||
'vacay.personName': 'Name',
|
||||
'vacay.personNamePlaceholder': 'Name eingeben',
|
||||
'vacay.color': 'Farbe',
|
||||
'vacay.add': 'Hinzufügen',
|
||||
'vacay.legend': 'Legende',
|
||||
'vacay.publicHoliday': 'Feiertag',
|
||||
'vacay.companyHoliday': 'Betriebsferien',
|
||||
'vacay.weekend': 'Wochenende',
|
||||
'vacay.modeVacation': 'Urlaub',
|
||||
'vacay.modeCompany': 'Betriebsferien',
|
||||
'vacay.entitlement': 'Urlaubsanspruch',
|
||||
'vacay.entitlementDays': 'Tage',
|
||||
'vacay.used': 'Weg',
|
||||
'vacay.remaining': 'Rest',
|
||||
'vacay.carriedOver': 'aus {year}',
|
||||
'vacay.blockWeekends': 'Wochenenden sperren',
|
||||
'vacay.blockWeekendsHint': 'Verhindert Urlaubseinträge an Samstagen und Sonntagen',
|
||||
'vacay.publicHolidays': 'Feiertage',
|
||||
'vacay.publicHolidaysHint': 'Feiertage im Kalender markieren',
|
||||
'vacay.selectCountry': 'Land wählen',
|
||||
'vacay.selectRegion': 'Region wählen (optional)',
|
||||
'vacay.companyHolidays': 'Betriebsferien',
|
||||
'vacay.companyHolidaysHint': 'Erlaubt das Markieren von unternehmensweiten Feiertagen',
|
||||
'vacay.companyHolidaysNoDeduct': 'Betriebsferien werden nicht vom Urlaubskontingent abgezogen.',
|
||||
'vacay.carryOver': 'Urlaubsmitnahme',
|
||||
'vacay.carryOverHint': 'Resturlaub automatisch ins Folgejahr übertragen',
|
||||
'vacay.sharing': 'Teilen',
|
||||
'vacay.sharingHint': 'Teile deinen Urlaubsplan mit anderen NOMAD-Benutzern',
|
||||
'vacay.owner': 'Besitzer',
|
||||
'vacay.shareEmailPlaceholder': 'E-Mail des NOMAD-Benutzers',
|
||||
'vacay.shareSuccess': 'Plan erfolgreich geteilt',
|
||||
'vacay.shareError': 'Plan konnte nicht geteilt werden',
|
||||
'vacay.dissolve': 'Fusion auflösen',
|
||||
'vacay.dissolveHint': 'Kalender wieder trennen. Deine Einträge bleiben erhalten.',
|
||||
'vacay.dissolveAction': 'Auflösen',
|
||||
'vacay.dissolved': 'Kalender getrennt',
|
||||
'vacay.fusedWith': 'Fusioniert mit',
|
||||
'vacay.you': 'du',
|
||||
'vacay.noData': 'Keine Daten',
|
||||
'vacay.changeColor': 'Farbe ändern',
|
||||
'vacay.inviteUser': 'Benutzer einladen',
|
||||
'vacay.inviteHint': 'Lade einen anderen NOMAD-Benutzer ein, um einen gemeinsamen Urlaubskalender zu teilen.',
|
||||
'vacay.selectUser': 'Benutzer wählen',
|
||||
'vacay.sendInvite': 'Einladung senden',
|
||||
'vacay.inviteSent': 'Einladung gesendet',
|
||||
'vacay.inviteError': 'Einladung konnte nicht gesendet werden',
|
||||
'vacay.pending': 'ausstehend',
|
||||
'vacay.noUsersAvailable': 'Keine Benutzer verfügbar',
|
||||
'vacay.accept': 'Annehmen',
|
||||
'vacay.decline': 'Ablehnen',
|
||||
'vacay.acceptFusion': 'Annehmen & Fusionieren',
|
||||
'vacay.inviteTitle': 'Fusionsanfrage',
|
||||
'vacay.inviteWantsToFuse': 'möchte einen Urlaubskalender mit dir teilen.',
|
||||
'vacay.fuseInfo1': 'Beide sehen alle Urlaubseinträge in einem gemeinsamen Kalender.',
|
||||
'vacay.fuseInfo2': 'Beide können Einträge für den jeweils anderen erstellen und bearbeiten.',
|
||||
'vacay.fuseInfo3': 'Beide können Einträge löschen und den Urlaubsanspruch ändern.',
|
||||
'vacay.fuseInfo4': 'Einstellungen wie Feiertage und Betriebsferien werden geteilt.',
|
||||
'vacay.fuseInfo5': 'Die Fusion kann jederzeit von beiden Seiten aufgelöst werden. Einträge bleiben erhalten.',
|
||||
'nav.myTrips': 'Meine Trips',
|
||||
|
||||
// Atlas addon
|
||||
'atlas.subtitle': 'Dein Reise-Fußabdruck auf der Welt',
|
||||
'atlas.countries': 'Länder',
|
||||
'atlas.trips': 'Reisen',
|
||||
'atlas.places': 'Orte',
|
||||
'atlas.days': 'Tage',
|
||||
'atlas.visitedCountries': 'Besuchte Länder',
|
||||
'atlas.cities': 'Städte',
|
||||
'atlas.noData': 'Noch keine Reisedaten',
|
||||
'atlas.noDataHint': 'Erstelle einen Trip und füge Orte hinzu',
|
||||
'atlas.lastTrip': 'Letzter Trip',
|
||||
'atlas.nextTrip': 'Nächster Trip',
|
||||
'atlas.daysLeft': 'Tage',
|
||||
'atlas.streak': 'Streak',
|
||||
'atlas.year': 'Jahr',
|
||||
'atlas.years': 'Jahre',
|
||||
'atlas.yearInRow': 'Jahr in Folge',
|
||||
'atlas.yearsInRow': 'Jahre in Folge',
|
||||
'atlas.tripIn': 'Reise in',
|
||||
'atlas.tripsIn': 'Reisen in',
|
||||
'atlas.since': 'seit',
|
||||
'atlas.europe': 'Europa',
|
||||
'atlas.asia': 'Asien',
|
||||
'atlas.northAmerica': 'N-Amerika',
|
||||
'atlas.southAmerica': 'S-Amerika',
|
||||
'atlas.africa': 'Afrika',
|
||||
'atlas.oceania': 'Ozeanien',
|
||||
'atlas.other': 'Andere',
|
||||
'atlas.firstVisit': 'Erste Reise',
|
||||
'atlas.lastVisitLabel': 'Letzte Reise',
|
||||
'atlas.tripSingular': 'Reise',
|
||||
'atlas.tripPlural': 'Reisen',
|
||||
'atlas.placeVisited': 'Ort besucht',
|
||||
'atlas.placesVisited': 'Orte besucht',
|
||||
|
||||
// Trip Planner
|
||||
'trip.tabs.plan': 'Planung',
|
||||
'trip.tabs.plan': 'Karte',
|
||||
'trip.tabs.reservations': 'Buchungen',
|
||||
'trip.tabs.packing': 'Packliste',
|
||||
'trip.tabs.packingShort': 'Packliste',
|
||||
'trip.tabs.reservationsShort': 'Buchung',
|
||||
'trip.tabs.packing': 'Liste',
|
||||
'trip.tabs.packingShort': 'Liste',
|
||||
'trip.tabs.budget': 'Budget',
|
||||
'trip.tabs.files': 'Dateien',
|
||||
'trip.loading': 'Reise wird geladen...',
|
||||
|
||||
@@ -48,6 +48,9 @@ const en = {
|
||||
'dashboard.subtitle.activeMany': '{count} active trips',
|
||||
'dashboard.subtitle.archivedSuffix': ' · {count} archived',
|
||||
'dashboard.newTrip': 'New Trip',
|
||||
'dashboard.currency': 'Currency',
|
||||
'dashboard.timezone': 'Timezones',
|
||||
'dashboard.localTime': 'Local',
|
||||
'dashboard.emptyTitle': 'No trips yet',
|
||||
'dashboard.emptyText': 'Create your first trip and start planning!',
|
||||
'dashboard.emptyButton': 'Create First Trip',
|
||||
@@ -227,6 +230,7 @@ const en = {
|
||||
'admin.allowRegistration': 'Allow Registration',
|
||||
'admin.allowRegistrationHint': 'New users can register themselves',
|
||||
'admin.apiKeys': 'API Keys',
|
||||
'admin.apiKeysHint': 'Optional. Enables extended place data like photos and weather.',
|
||||
'admin.mapsKey': 'Google Maps API Key',
|
||||
'admin.mapsKeyHint': 'Required for place search. Get at console.cloud.google.com',
|
||||
'admin.mapsKeyHintLong': 'Without an API key, OpenStreetMap is used for place search. With a Google API key, photos, ratings, and opening hours can be loaded as well. Get one at console.cloud.google.com.',
|
||||
@@ -244,9 +248,135 @@ const en = {
|
||||
'admin.oidcIssuerHint': 'The OpenID Connect Issuer URL of the provider. e.g. https://accounts.google.com',
|
||||
'admin.oidcSaved': 'OIDC configuration saved',
|
||||
|
||||
// Addons
|
||||
'admin.tabs.addons': 'Addons',
|
||||
'admin.addons.title': 'Addons',
|
||||
'admin.addons.subtitle': 'Enable or disable features to customize your NOMAD experience.',
|
||||
'admin.addons.enabled': 'Enabled',
|
||||
'admin.addons.disabled': 'Disabled',
|
||||
'admin.addons.type.trip': 'Trip',
|
||||
'admin.addons.type.global': 'Global',
|
||||
'admin.addons.tripHint': 'Available as a tab within each trip',
|
||||
'admin.addons.globalHint': 'Available as a standalone section in the main navigation',
|
||||
'admin.addons.toast.updated': 'Addon updated',
|
||||
'admin.addons.toast.error': 'Failed to update addon',
|
||||
'admin.addons.noAddons': 'No addons available',
|
||||
|
||||
// Vacay addon
|
||||
'vacay.subtitle': 'Plan and manage vacation days',
|
||||
'vacay.settings': 'Settings',
|
||||
'vacay.year': 'Year',
|
||||
'vacay.addYear': 'Add year',
|
||||
'vacay.removeYear': 'Remove year',
|
||||
'vacay.removeYearConfirm': 'Remove {year}?',
|
||||
'vacay.removeYearHint': 'All vacation entries and company holidays for this year will be permanently deleted.',
|
||||
'vacay.remove': 'Remove',
|
||||
'vacay.persons': 'Persons',
|
||||
'vacay.noPersons': 'No persons added',
|
||||
'vacay.addPerson': 'Add Person',
|
||||
'vacay.editPerson': 'Edit Person',
|
||||
'vacay.removePerson': 'Remove Person',
|
||||
'vacay.removePersonConfirm': 'Remove {name}?',
|
||||
'vacay.removePersonHint': 'All vacation entries for this person will be permanently deleted.',
|
||||
'vacay.personName': 'Name',
|
||||
'vacay.personNamePlaceholder': 'Enter name',
|
||||
'vacay.color': 'Color',
|
||||
'vacay.add': 'Add',
|
||||
'vacay.legend': 'Legend',
|
||||
'vacay.publicHoliday': 'Public Holiday',
|
||||
'vacay.companyHoliday': 'Company Holiday',
|
||||
'vacay.weekend': 'Weekend',
|
||||
'vacay.modeVacation': 'Vacation',
|
||||
'vacay.modeCompany': 'Company Holiday',
|
||||
'vacay.entitlement': 'Entitlement',
|
||||
'vacay.entitlementDays': 'Days',
|
||||
'vacay.used': 'Used',
|
||||
'vacay.remaining': 'Left',
|
||||
'vacay.carriedOver': 'from {year}',
|
||||
'vacay.blockWeekends': 'Block Weekends',
|
||||
'vacay.blockWeekendsHint': 'Prevent vacation entries on Saturdays and Sundays',
|
||||
'vacay.publicHolidays': 'Public Holidays',
|
||||
'vacay.publicHolidaysHint': 'Mark public holidays in the calendar',
|
||||
'vacay.selectCountry': 'Select country',
|
||||
'vacay.selectRegion': 'Select region (optional)',
|
||||
'vacay.companyHolidays': 'Company Holidays',
|
||||
'vacay.companyHolidaysHint': 'Allow marking company-wide holiday days',
|
||||
'vacay.companyHolidaysNoDeduct': 'Company holidays do not count towards vacation days.',
|
||||
'vacay.carryOver': 'Carry Over',
|
||||
'vacay.carryOverHint': 'Automatically carry remaining vacation days into the next year',
|
||||
'vacay.sharing': 'Sharing',
|
||||
'vacay.sharingHint': 'Share your vacation plan with other NOMAD users',
|
||||
'vacay.owner': 'Owner',
|
||||
'vacay.shareEmailPlaceholder': 'Email of NOMAD user',
|
||||
'vacay.shareSuccess': 'Plan shared successfully',
|
||||
'vacay.shareError': 'Could not share plan',
|
||||
'vacay.dissolve': 'Dissolve Fusion',
|
||||
'vacay.dissolveHint': 'Separate calendars again. Your entries will be kept.',
|
||||
'vacay.dissolveAction': 'Dissolve',
|
||||
'vacay.dissolved': 'Calendar separated',
|
||||
'vacay.fusedWith': 'Fused with',
|
||||
'vacay.you': 'you',
|
||||
'vacay.noData': 'No data',
|
||||
'vacay.changeColor': 'Change color',
|
||||
'vacay.inviteUser': 'Invite User',
|
||||
'vacay.inviteHint': 'Invite another NOMAD user to share a combined vacation calendar.',
|
||||
'vacay.selectUser': 'Select user',
|
||||
'vacay.sendInvite': 'Send Invite',
|
||||
'vacay.inviteSent': 'Invite sent',
|
||||
'vacay.inviteError': 'Could not send invite',
|
||||
'vacay.pending': 'pending',
|
||||
'vacay.noUsersAvailable': 'No users available',
|
||||
'vacay.accept': 'Accept',
|
||||
'vacay.decline': 'Decline',
|
||||
'vacay.acceptFusion': 'Accept & Fuse',
|
||||
'vacay.inviteTitle': 'Fusion Request',
|
||||
'vacay.inviteWantsToFuse': 'wants to share a vacation calendar with you.',
|
||||
'vacay.fuseInfo1': 'Both of you will see all vacation entries in one shared calendar.',
|
||||
'vacay.fuseInfo2': 'Both parties can create and edit entries for each other.',
|
||||
'vacay.fuseInfo3': 'Both parties can delete entries and change vacation entitlements.',
|
||||
'vacay.fuseInfo4': 'Settings like public holidays and company holidays are shared.',
|
||||
'vacay.fuseInfo5': 'The fusion can be dissolved at any time by either party. Your entries will be preserved.',
|
||||
'nav.myTrips': 'My Trips',
|
||||
|
||||
// Atlas addon
|
||||
'atlas.subtitle': 'Your travel footprint around the world',
|
||||
'atlas.countries': 'Countries',
|
||||
'atlas.trips': 'Trips',
|
||||
'atlas.places': 'Places',
|
||||
'atlas.days': 'Days',
|
||||
'atlas.visitedCountries': 'Visited Countries',
|
||||
'atlas.cities': 'Cities',
|
||||
'atlas.noData': 'No travel data yet',
|
||||
'atlas.noDataHint': 'Create a trip and add places to see your world map',
|
||||
'atlas.lastTrip': 'Last trip',
|
||||
'atlas.nextTrip': 'Next trip',
|
||||
'atlas.daysLeft': 'days left',
|
||||
'atlas.streak': 'Streak',
|
||||
'atlas.year': 'year',
|
||||
'atlas.years': 'years',
|
||||
'atlas.yearInRow': 'year in a row',
|
||||
'atlas.yearsInRow': 'years in a row',
|
||||
'atlas.tripIn': 'trip in',
|
||||
'atlas.tripsIn': 'trips in',
|
||||
'atlas.since': 'since',
|
||||
'atlas.europe': 'Europe',
|
||||
'atlas.asia': 'Asia',
|
||||
'atlas.northAmerica': 'N. America',
|
||||
'atlas.southAmerica': 'S. America',
|
||||
'atlas.africa': 'Africa',
|
||||
'atlas.oceania': 'Oceania',
|
||||
'atlas.other': 'Other',
|
||||
'atlas.firstVisit': 'First trip',
|
||||
'atlas.lastVisitLabel': 'Last trip',
|
||||
'atlas.tripSingular': 'Trip',
|
||||
'atlas.tripPlural': 'Trips',
|
||||
'atlas.placeVisited': 'Place visited',
|
||||
'atlas.placesVisited': 'Places visited',
|
||||
|
||||
// Trip Planner
|
||||
'trip.tabs.plan': 'Plan',
|
||||
'trip.tabs.reservations': 'Bookings',
|
||||
'trip.tabs.reservationsShort': 'Book',
|
||||
'trip.tabs.packing': 'Packing List',
|
||||
'trip.tabs.packingShort': 'Packing',
|
||||
'trip.tabs.budget': 'Budget',
|
||||
|
||||
Reference in New Issue
Block a user