diff --git a/.github/workflows/backend-test.yml b/.github/workflows/backend-test.yml index afe3fa8..0ce9d7c 100644 --- a/.github/workflows/backend-test.yml +++ b/.github/workflows/backend-test.yml @@ -1,5 +1,8 @@ name: Test Backend +permissions: + contents: read + on: pull_request: paths: diff --git a/.github/workflows/frontend-test.yml b/.github/workflows/frontend-test.yml index 04df55a..73d9b24 100644 --- a/.github/workflows/frontend-test.yml +++ b/.github/workflows/frontend-test.yml @@ -1,5 +1,8 @@ name: Test Frontend +permissions: + contents: read + on: pull_request: paths: diff --git a/backend/nginx.conf b/backend/nginx.conf index 8074aa6..e63dcfe 100644 --- a/backend/nginx.conf +++ b/backend/nginx.conf @@ -36,10 +36,19 @@ http { # Serve protected media files with X-Accel-Redirect location /protectedMedia/ { - internal; # Only internal requests are allowed - alias /code/media/; # This should match Django MEDIA_ROOT - try_files $uri =404; # Return a 404 if the file doesn't exist - } + internal; + alias /code/media/; + try_files $uri =404; + + # Nested location for PDFs + location ~* \.pdf$ { + add_header Content-Security-Policy "default-src 'self'; script-src 'none'; object-src 'none'; base-uri 'none'" always; + add_header X-Content-Type-Options nosniff always; + add_header X-Frame-Options SAMEORIGIN always; + add_header Content-Disposition "inline" always; + } +} + } } diff --git a/backend/server/adventures/admin.py b/backend/server/adventures/admin.py index 3210f7a..a893834 100644 --- a/backend/server/adventures/admin.py +++ b/backend/server/adventures/admin.py @@ -8,10 +8,25 @@ from allauth.account.decorators import secure_admin_login admin.autodiscover() admin.site.login = secure_admin_login(admin.site.login) +@admin.action(description="Trigger geocoding") +def trigger_geocoding(modeladmin, request, queryset): + count = 0 + for adventure in queryset: + try: + adventure.save() # Triggers geocoding logic in your model + count += 1 + except Exception as e: + modeladmin.message_user(request, f"Error geocoding {adventure}: {e}", level='error') + modeladmin.message_user(request, f"Geocoding triggered for {count} adventures.", level='success') + + + class AdventureAdmin(admin.ModelAdmin): list_display = ('name', 'get_category', 'get_visit_count', 'user_id', 'is_public') list_filter = ( 'user_id', 'is_public') search_fields = ('name',) + readonly_fields = ('city', 'region', 'country') + actions = [trigger_geocoding] def get_category(self, obj): if obj.category and obj.category.display_name and obj.category.icon: diff --git a/backend/server/adventures/geocoding.py b/backend/server/adventures/geocoding.py new file mode 100644 index 0000000..31ba13f --- /dev/null +++ b/backend/server/adventures/geocoding.py @@ -0,0 +1,71 @@ +import requests +from worldtravel.models import Region, City, VisitedRegion, VisitedCity + +def extractIsoCode(user, data): + """ + Extract the ISO code from the response data. + Returns a dictionary containing the region name, country name, and ISO code if found. + """ + iso_code = None + town_city_or_county = None + display_name = None + country_code = None + city = None + visited_city = None + location_name = None + + # town = None + # city = None + # county = None + + if 'name' in data.keys(): + location_name = data['name'] + + if 'address' in data.keys(): + keys = data['address'].keys() + for key in keys: + if key.find("ISO") != -1: + iso_code = data['address'][key] + if 'town' in keys: + town_city_or_county = data['address']['town'] + if 'county' in keys: + town_city_or_county = data['address']['county'] + if 'city' in keys: + town_city_or_county = data['address']['city'] + if not iso_code: + return {"error": "No region found"} + + region = Region.objects.filter(id=iso_code).first() + visited_region = VisitedRegion.objects.filter(region=region, user_id=user).first() + + region_visited = False + city_visited = False + country_code = iso_code[:2] + + if region: + if town_city_or_county: + display_name = f"{town_city_or_county}, {region.name}, {country_code}" + city = City.objects.filter(name__contains=town_city_or_county, region=region).first() + visited_city = VisitedCity.objects.filter(city=city, user_id=user).first() + + if visited_region: + region_visited = True + if visited_city: + city_visited = True + if region: + return {"region_id": iso_code, "region": region.name, "country": region.country.name, "country_id": region.country.country_code, "region_visited": region_visited, "display_name": display_name, "city": city.name if city else None, "city_id": city.id if city else None, "city_visited": city_visited, 'location_name': location_name} + return {"error": "No region found"} + +def reverse_geocode(lat, lon, user): + """ + Reverse geocode the given latitude and longitude using Nominatim API. + Returns a dictionary containing the region name, country name, and ISO code if found. + """ + url = f"https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat={lat}&lon={lon}" + headers = {'User-Agent': 'AdventureLog Server'} + response = requests.get(url, headers=headers) + try: + data = response.json() + except requests.exceptions.JSONDecodeError: + return {"error": "Invalid response from geocoding service"} + return extractIsoCode(user, data) diff --git a/backend/server/adventures/middleware.py b/backend/server/adventures/middleware.py index 10050b0..fae6b0b 100644 --- a/backend/server/adventures/middleware.py +++ b/backend/server/adventures/middleware.py @@ -29,4 +29,12 @@ class XSessionTokenMiddleware(MiddlewareMixin): class DisableCSRFForSessionTokenMiddleware(MiddlewareMixin): def process_request(self, request): if 'X-Session-Token' in request.headers: - setattr(request, '_dont_enforce_csrf_checks', True) \ No newline at end of file + setattr(request, '_dont_enforce_csrf_checks', True) + +class DisableCSRFForMobileLoginSignup(MiddlewareMixin): + def process_request(self, request): + is_mobile = request.headers.get('X-Is-Mobile', '').lower() == 'true' + is_login_or_signup = request.path in ['/auth/browser/v1/auth/login', '/auth/browser/v1/auth/signup'] + if is_mobile and is_login_or_signup: + setattr(request, '_dont_enforce_csrf_checks', True) + \ No newline at end of file diff --git a/backend/server/adventures/migrations/0026_visit_timezone.py b/backend/server/adventures/migrations/0026_visit_timezone.py new file mode 100644 index 0000000..f0642dd --- /dev/null +++ b/backend/server/adventures/migrations/0026_visit_timezone.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.11 on 2025-05-10 14:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('adventures', '0025_alter_visit_end_date_alter_visit_start_date'), + ] + + operations = [ + migrations.AddField( + model_name='visit', + name='timezone', + field=models.CharField(blank=True, choices=[('Africa/Abidjan', 'Africa/Abidjan'), ('Africa/Accra', 'Africa/Accra'), ('Africa/Addis_Ababa', 'Africa/Addis_Ababa'), ('Africa/Algiers', 'Africa/Algiers'), ('Africa/Asmera', 'Africa/Asmera'), ('Africa/Bamako', 'Africa/Bamako'), ('Africa/Bangui', 'Africa/Bangui'), ('Africa/Banjul', 'Africa/Banjul'), ('Africa/Bissau', 'Africa/Bissau'), ('Africa/Blantyre', 'Africa/Blantyre'), ('Africa/Brazzaville', 'Africa/Brazzaville'), ('Africa/Bujumbura', 'Africa/Bujumbura'), ('Africa/Cairo', 'Africa/Cairo'), ('Africa/Casablanca', 'Africa/Casablanca'), ('Africa/Ceuta', 'Africa/Ceuta'), ('Africa/Conakry', 'Africa/Conakry'), ('Africa/Dakar', 'Africa/Dakar'), ('Africa/Dar_es_Salaam', 'Africa/Dar_es_Salaam'), ('Africa/Djibouti', 'Africa/Djibouti'), ('Africa/Douala', 'Africa/Douala'), ('Africa/El_Aaiun', 'Africa/El_Aaiun'), ('Africa/Freetown', 'Africa/Freetown'), ('Africa/Gaborone', 'Africa/Gaborone'), ('Africa/Harare', 'Africa/Harare'), ('Africa/Johannesburg', 'Africa/Johannesburg'), ('Africa/Juba', 'Africa/Juba'), ('Africa/Kampala', 'Africa/Kampala'), ('Africa/Khartoum', 'Africa/Khartoum'), ('Africa/Kigali', 'Africa/Kigali'), ('Africa/Kinshasa', 'Africa/Kinshasa'), ('Africa/Lagos', 'Africa/Lagos'), ('Africa/Libreville', 'Africa/Libreville'), ('Africa/Lome', 'Africa/Lome'), ('Africa/Luanda', 'Africa/Luanda'), ('Africa/Lubumbashi', 'Africa/Lubumbashi'), ('Africa/Lusaka', 'Africa/Lusaka'), ('Africa/Malabo', 'Africa/Malabo'), ('Africa/Maputo', 'Africa/Maputo'), ('Africa/Maseru', 'Africa/Maseru'), ('Africa/Mbabane', 'Africa/Mbabane'), ('Africa/Mogadishu', 'Africa/Mogadishu'), ('Africa/Monrovia', 'Africa/Monrovia'), ('Africa/Nairobi', 'Africa/Nairobi'), ('Africa/Ndjamena', 'Africa/Ndjamena'), ('Africa/Niamey', 'Africa/Niamey'), ('Africa/Nouakchott', 'Africa/Nouakchott'), ('Africa/Ouagadougou', 'Africa/Ouagadougou'), ('Africa/Porto-Novo', 'Africa/Porto-Novo'), ('Africa/Sao_Tome', 'Africa/Sao_Tome'), ('Africa/Tripoli', 'Africa/Tripoli'), ('Africa/Tunis', 'Africa/Tunis'), ('Africa/Windhoek', 'Africa/Windhoek'), ('America/Adak', 'America/Adak'), ('America/Anchorage', 'America/Anchorage'), ('America/Anguilla', 'America/Anguilla'), ('America/Antigua', 'America/Antigua'), ('America/Araguaina', 'America/Araguaina'), ('America/Argentina/La_Rioja', 'America/Argentina/La_Rioja'), ('America/Argentina/Rio_Gallegos', 'America/Argentina/Rio_Gallegos'), ('America/Argentina/Salta', 'America/Argentina/Salta'), ('America/Argentina/San_Juan', 'America/Argentina/San_Juan'), ('America/Argentina/San_Luis', 'America/Argentina/San_Luis'), ('America/Argentina/Tucuman', 'America/Argentina/Tucuman'), ('America/Argentina/Ushuaia', 'America/Argentina/Ushuaia'), ('America/Aruba', 'America/Aruba'), ('America/Asuncion', 'America/Asuncion'), ('America/Bahia', 'America/Bahia'), ('America/Bahia_Banderas', 'America/Bahia_Banderas'), ('America/Barbados', 'America/Barbados'), ('America/Belem', 'America/Belem'), ('America/Belize', 'America/Belize'), ('America/Blanc-Sablon', 'America/Blanc-Sablon'), ('America/Boa_Vista', 'America/Boa_Vista'), ('America/Bogota', 'America/Bogota'), ('America/Boise', 'America/Boise'), ('America/Buenos_Aires', 'America/Buenos_Aires'), ('America/Cambridge_Bay', 'America/Cambridge_Bay'), ('America/Campo_Grande', 'America/Campo_Grande'), ('America/Cancun', 'America/Cancun'), ('America/Caracas', 'America/Caracas'), ('America/Catamarca', 'America/Catamarca'), ('America/Cayenne', 'America/Cayenne'), ('America/Cayman', 'America/Cayman'), ('America/Chicago', 'America/Chicago'), ('America/Chihuahua', 'America/Chihuahua'), ('America/Ciudad_Juarez', 'America/Ciudad_Juarez'), ('America/Coral_Harbour', 'America/Coral_Harbour'), ('America/Cordoba', 'America/Cordoba'), ('America/Costa_Rica', 'America/Costa_Rica'), ('America/Creston', 'America/Creston'), ('America/Cuiaba', 'America/Cuiaba'), ('America/Curacao', 'America/Curacao'), ('America/Danmarkshavn', 'America/Danmarkshavn'), ('America/Dawson', 'America/Dawson'), ('America/Dawson_Creek', 'America/Dawson_Creek'), ('America/Denver', 'America/Denver'), ('America/Detroit', 'America/Detroit'), ('America/Dominica', 'America/Dominica'), ('America/Edmonton', 'America/Edmonton'), ('America/Eirunepe', 'America/Eirunepe'), ('America/El_Salvador', 'America/El_Salvador'), ('America/Fort_Nelson', 'America/Fort_Nelson'), ('America/Fortaleza', 'America/Fortaleza'), ('America/Glace_Bay', 'America/Glace_Bay'), ('America/Godthab', 'America/Godthab'), ('America/Goose_Bay', 'America/Goose_Bay'), ('America/Grand_Turk', 'America/Grand_Turk'), ('America/Grenada', 'America/Grenada'), ('America/Guadeloupe', 'America/Guadeloupe'), ('America/Guatemala', 'America/Guatemala'), ('America/Guayaquil', 'America/Guayaquil'), ('America/Guyana', 'America/Guyana'), ('America/Halifax', 'America/Halifax'), ('America/Havana', 'America/Havana'), ('America/Hermosillo', 'America/Hermosillo'), ('America/Indiana/Knox', 'America/Indiana/Knox'), ('America/Indiana/Marengo', 'America/Indiana/Marengo'), ('America/Indiana/Petersburg', 'America/Indiana/Petersburg'), ('America/Indiana/Tell_City', 'America/Indiana/Tell_City'), ('America/Indiana/Vevay', 'America/Indiana/Vevay'), ('America/Indiana/Vincennes', 'America/Indiana/Vincennes'), ('America/Indiana/Winamac', 'America/Indiana/Winamac'), ('America/Indianapolis', 'America/Indianapolis'), ('America/Inuvik', 'America/Inuvik'), ('America/Iqaluit', 'America/Iqaluit'), ('America/Jamaica', 'America/Jamaica'), ('America/Jujuy', 'America/Jujuy'), ('America/Juneau', 'America/Juneau'), ('America/Kentucky/Monticello', 'America/Kentucky/Monticello'), ('America/Kralendijk', 'America/Kralendijk'), ('America/La_Paz', 'America/La_Paz'), ('America/Lima', 'America/Lima'), ('America/Los_Angeles', 'America/Los_Angeles'), ('America/Louisville', 'America/Louisville'), ('America/Lower_Princes', 'America/Lower_Princes'), ('America/Maceio', 'America/Maceio'), ('America/Managua', 'America/Managua'), ('America/Manaus', 'America/Manaus'), ('America/Marigot', 'America/Marigot'), ('America/Martinique', 'America/Martinique'), ('America/Matamoros', 'America/Matamoros'), ('America/Mazatlan', 'America/Mazatlan'), ('America/Mendoza', 'America/Mendoza'), ('America/Menominee', 'America/Menominee'), ('America/Merida', 'America/Merida'), ('America/Metlakatla', 'America/Metlakatla'), ('America/Mexico_City', 'America/Mexico_City'), ('America/Miquelon', 'America/Miquelon'), ('America/Moncton', 'America/Moncton'), ('America/Monterrey', 'America/Monterrey'), ('America/Montevideo', 'America/Montevideo'), ('America/Montserrat', 'America/Montserrat'), ('America/Nassau', 'America/Nassau'), ('America/New_York', 'America/New_York'), ('America/Nome', 'America/Nome'), ('America/Noronha', 'America/Noronha'), ('America/North_Dakota/Beulah', 'America/North_Dakota/Beulah'), ('America/North_Dakota/Center', 'America/North_Dakota/Center'), ('America/North_Dakota/New_Salem', 'America/North_Dakota/New_Salem'), ('America/Ojinaga', 'America/Ojinaga'), ('America/Panama', 'America/Panama'), ('America/Paramaribo', 'America/Paramaribo'), ('America/Phoenix', 'America/Phoenix'), ('America/Port-au-Prince', 'America/Port-au-Prince'), ('America/Port_of_Spain', 'America/Port_of_Spain'), ('America/Porto_Velho', 'America/Porto_Velho'), ('America/Puerto_Rico', 'America/Puerto_Rico'), ('America/Punta_Arenas', 'America/Punta_Arenas'), ('America/Rankin_Inlet', 'America/Rankin_Inlet'), ('America/Recife', 'America/Recife'), ('America/Regina', 'America/Regina'), ('America/Resolute', 'America/Resolute'), ('America/Rio_Branco', 'America/Rio_Branco'), ('America/Santarem', 'America/Santarem'), ('America/Santiago', 'America/Santiago'), ('America/Santo_Domingo', 'America/Santo_Domingo'), ('America/Sao_Paulo', 'America/Sao_Paulo'), ('America/Scoresbysund', 'America/Scoresbysund'), ('America/Sitka', 'America/Sitka'), ('America/St_Barthelemy', 'America/St_Barthelemy'), ('America/St_Johns', 'America/St_Johns'), ('America/St_Kitts', 'America/St_Kitts'), ('America/St_Lucia', 'America/St_Lucia'), ('America/St_Thomas', 'America/St_Thomas'), ('America/St_Vincent', 'America/St_Vincent'), ('America/Swift_Current', 'America/Swift_Current'), ('America/Tegucigalpa', 'America/Tegucigalpa'), ('America/Thule', 'America/Thule'), ('America/Tijuana', 'America/Tijuana'), ('America/Toronto', 'America/Toronto'), ('America/Tortola', 'America/Tortola'), ('America/Vancouver', 'America/Vancouver'), ('America/Whitehorse', 'America/Whitehorse'), ('America/Winnipeg', 'America/Winnipeg'), ('America/Yakutat', 'America/Yakutat'), ('Antarctica/Casey', 'Antarctica/Casey'), ('Antarctica/Davis', 'Antarctica/Davis'), ('Antarctica/DumontDUrville', 'Antarctica/DumontDUrville'), ('Antarctica/Macquarie', 'Antarctica/Macquarie'), ('Antarctica/Mawson', 'Antarctica/Mawson'), ('Antarctica/McMurdo', 'Antarctica/McMurdo'), ('Antarctica/Palmer', 'Antarctica/Palmer'), ('Antarctica/Rothera', 'Antarctica/Rothera'), ('Antarctica/Syowa', 'Antarctica/Syowa'), ('Antarctica/Troll', 'Antarctica/Troll'), ('Antarctica/Vostok', 'Antarctica/Vostok'), ('Arctic/Longyearbyen', 'Arctic/Longyearbyen'), ('Asia/Aden', 'Asia/Aden'), ('Asia/Almaty', 'Asia/Almaty'), ('Asia/Amman', 'Asia/Amman'), ('Asia/Anadyr', 'Asia/Anadyr'), ('Asia/Aqtau', 'Asia/Aqtau'), ('Asia/Aqtobe', 'Asia/Aqtobe'), ('Asia/Ashgabat', 'Asia/Ashgabat'), ('Asia/Atyrau', 'Asia/Atyrau'), ('Asia/Baghdad', 'Asia/Baghdad'), ('Asia/Bahrain', 'Asia/Bahrain'), ('Asia/Baku', 'Asia/Baku'), ('Asia/Bangkok', 'Asia/Bangkok'), ('Asia/Barnaul', 'Asia/Barnaul'), ('Asia/Beirut', 'Asia/Beirut'), ('Asia/Bishkek', 'Asia/Bishkek'), ('Asia/Brunei', 'Asia/Brunei'), ('Asia/Calcutta', 'Asia/Calcutta'), ('Asia/Chita', 'Asia/Chita'), ('Asia/Colombo', 'Asia/Colombo'), ('Asia/Damascus', 'Asia/Damascus'), ('Asia/Dhaka', 'Asia/Dhaka'), ('Asia/Dili', 'Asia/Dili'), ('Asia/Dubai', 'Asia/Dubai'), ('Asia/Dushanbe', 'Asia/Dushanbe'), ('Asia/Famagusta', 'Asia/Famagusta'), ('Asia/Gaza', 'Asia/Gaza'), ('Asia/Hebron', 'Asia/Hebron'), ('Asia/Hong_Kong', 'Asia/Hong_Kong'), ('Asia/Hovd', 'Asia/Hovd'), ('Asia/Irkutsk', 'Asia/Irkutsk'), ('Asia/Jakarta', 'Asia/Jakarta'), ('Asia/Jayapura', 'Asia/Jayapura'), ('Asia/Jerusalem', 'Asia/Jerusalem'), ('Asia/Kabul', 'Asia/Kabul'), ('Asia/Kamchatka', 'Asia/Kamchatka'), ('Asia/Karachi', 'Asia/Karachi'), ('Asia/Katmandu', 'Asia/Katmandu'), ('Asia/Khandyga', 'Asia/Khandyga'), ('Asia/Krasnoyarsk', 'Asia/Krasnoyarsk'), ('Asia/Kuala_Lumpur', 'Asia/Kuala_Lumpur'), ('Asia/Kuching', 'Asia/Kuching'), ('Asia/Kuwait', 'Asia/Kuwait'), ('Asia/Macau', 'Asia/Macau'), ('Asia/Magadan', 'Asia/Magadan'), ('Asia/Makassar', 'Asia/Makassar'), ('Asia/Manila', 'Asia/Manila'), ('Asia/Muscat', 'Asia/Muscat'), ('Asia/Nicosia', 'Asia/Nicosia'), ('Asia/Novokuznetsk', 'Asia/Novokuznetsk'), ('Asia/Novosibirsk', 'Asia/Novosibirsk'), ('Asia/Omsk', 'Asia/Omsk'), ('Asia/Oral', 'Asia/Oral'), ('Asia/Phnom_Penh', 'Asia/Phnom_Penh'), ('Asia/Pontianak', 'Asia/Pontianak'), ('Asia/Pyongyang', 'Asia/Pyongyang'), ('Asia/Qatar', 'Asia/Qatar'), ('Asia/Qostanay', 'Asia/Qostanay'), ('Asia/Qyzylorda', 'Asia/Qyzylorda'), ('Asia/Rangoon', 'Asia/Rangoon'), ('Asia/Riyadh', 'Asia/Riyadh'), ('Asia/Saigon', 'Asia/Saigon'), ('Asia/Sakhalin', 'Asia/Sakhalin'), ('Asia/Samarkand', 'Asia/Samarkand'), ('Asia/Seoul', 'Asia/Seoul'), ('Asia/Shanghai', 'Asia/Shanghai'), ('Asia/Singapore', 'Asia/Singapore'), ('Asia/Srednekolymsk', 'Asia/Srednekolymsk'), ('Asia/Taipei', 'Asia/Taipei'), ('Asia/Tashkent', 'Asia/Tashkent'), ('Asia/Tbilisi', 'Asia/Tbilisi'), ('Asia/Tehran', 'Asia/Tehran'), ('Asia/Thimphu', 'Asia/Thimphu'), ('Asia/Tokyo', 'Asia/Tokyo'), ('Asia/Tomsk', 'Asia/Tomsk'), ('Asia/Ulaanbaatar', 'Asia/Ulaanbaatar'), ('Asia/Urumqi', 'Asia/Urumqi'), ('Asia/Ust-Nera', 'Asia/Ust-Nera'), ('Asia/Vientiane', 'Asia/Vientiane'), ('Asia/Vladivostok', 'Asia/Vladivostok'), ('Asia/Yakutsk', 'Asia/Yakutsk'), ('Asia/Yekaterinburg', 'Asia/Yekaterinburg'), ('Asia/Yerevan', 'Asia/Yerevan'), ('Atlantic/Azores', 'Atlantic/Azores'), ('Atlantic/Bermuda', 'Atlantic/Bermuda'), ('Atlantic/Canary', 'Atlantic/Canary'), ('Atlantic/Cape_Verde', 'Atlantic/Cape_Verde'), ('Atlantic/Faeroe', 'Atlantic/Faeroe'), ('Atlantic/Madeira', 'Atlantic/Madeira'), ('Atlantic/Reykjavik', 'Atlantic/Reykjavik'), ('Atlantic/South_Georgia', 'Atlantic/South_Georgia'), ('Atlantic/St_Helena', 'Atlantic/St_Helena'), ('Atlantic/Stanley', 'Atlantic/Stanley'), ('Australia/Adelaide', 'Australia/Adelaide'), ('Australia/Brisbane', 'Australia/Brisbane'), ('Australia/Broken_Hill', 'Australia/Broken_Hill'), ('Australia/Darwin', 'Australia/Darwin'), ('Australia/Eucla', 'Australia/Eucla'), ('Australia/Hobart', 'Australia/Hobart'), ('Australia/Lindeman', 'Australia/Lindeman'), ('Australia/Lord_Howe', 'Australia/Lord_Howe'), ('Australia/Melbourne', 'Australia/Melbourne'), ('Australia/Perth', 'Australia/Perth'), ('Australia/Sydney', 'Australia/Sydney'), ('Europe/Amsterdam', 'Europe/Amsterdam'), ('Europe/Andorra', 'Europe/Andorra'), ('Europe/Astrakhan', 'Europe/Astrakhan'), ('Europe/Athens', 'Europe/Athens'), ('Europe/Belgrade', 'Europe/Belgrade'), ('Europe/Berlin', 'Europe/Berlin'), ('Europe/Bratislava', 'Europe/Bratislava'), ('Europe/Brussels', 'Europe/Brussels'), ('Europe/Bucharest', 'Europe/Bucharest'), ('Europe/Budapest', 'Europe/Budapest'), ('Europe/Busingen', 'Europe/Busingen'), ('Europe/Chisinau', 'Europe/Chisinau'), ('Europe/Copenhagen', 'Europe/Copenhagen'), ('Europe/Dublin', 'Europe/Dublin'), ('Europe/Gibraltar', 'Europe/Gibraltar'), ('Europe/Guernsey', 'Europe/Guernsey'), ('Europe/Helsinki', 'Europe/Helsinki'), ('Europe/Isle_of_Man', 'Europe/Isle_of_Man'), ('Europe/Istanbul', 'Europe/Istanbul'), ('Europe/Jersey', 'Europe/Jersey'), ('Europe/Kaliningrad', 'Europe/Kaliningrad'), ('Europe/Kiev', 'Europe/Kiev'), ('Europe/Kirov', 'Europe/Kirov'), ('Europe/Lisbon', 'Europe/Lisbon'), ('Europe/Ljubljana', 'Europe/Ljubljana'), ('Europe/London', 'Europe/London'), ('Europe/Luxembourg', 'Europe/Luxembourg'), ('Europe/Madrid', 'Europe/Madrid'), ('Europe/Malta', 'Europe/Malta'), ('Europe/Mariehamn', 'Europe/Mariehamn'), ('Europe/Minsk', 'Europe/Minsk'), ('Europe/Monaco', 'Europe/Monaco'), ('Europe/Moscow', 'Europe/Moscow'), ('Europe/Oslo', 'Europe/Oslo'), ('Europe/Paris', 'Europe/Paris'), ('Europe/Podgorica', 'Europe/Podgorica'), ('Europe/Prague', 'Europe/Prague'), ('Europe/Riga', 'Europe/Riga'), ('Europe/Rome', 'Europe/Rome'), ('Europe/Samara', 'Europe/Samara'), ('Europe/San_Marino', 'Europe/San_Marino'), ('Europe/Sarajevo', 'Europe/Sarajevo'), ('Europe/Saratov', 'Europe/Saratov'), ('Europe/Simferopol', 'Europe/Simferopol'), ('Europe/Skopje', 'Europe/Skopje'), ('Europe/Sofia', 'Europe/Sofia'), ('Europe/Stockholm', 'Europe/Stockholm'), ('Europe/Tallinn', 'Europe/Tallinn'), ('Europe/Tirane', 'Europe/Tirane'), ('Europe/Ulyanovsk', 'Europe/Ulyanovsk'), ('Europe/Vaduz', 'Europe/Vaduz'), ('Europe/Vatican', 'Europe/Vatican'), ('Europe/Vienna', 'Europe/Vienna'), ('Europe/Vilnius', 'Europe/Vilnius'), ('Europe/Volgograd', 'Europe/Volgograd'), ('Europe/Warsaw', 'Europe/Warsaw'), ('Europe/Zagreb', 'Europe/Zagreb'), ('Europe/Zurich', 'Europe/Zurich'), ('Indian/Antananarivo', 'Indian/Antananarivo'), ('Indian/Chagos', 'Indian/Chagos'), ('Indian/Christmas', 'Indian/Christmas'), ('Indian/Cocos', 'Indian/Cocos'), ('Indian/Comoro', 'Indian/Comoro'), ('Indian/Kerguelen', 'Indian/Kerguelen'), ('Indian/Mahe', 'Indian/Mahe'), ('Indian/Maldives', 'Indian/Maldives'), ('Indian/Mauritius', 'Indian/Mauritius'), ('Indian/Mayotte', 'Indian/Mayotte'), ('Indian/Reunion', 'Indian/Reunion'), ('Pacific/Apia', 'Pacific/Apia'), ('Pacific/Auckland', 'Pacific/Auckland'), ('Pacific/Bougainville', 'Pacific/Bougainville'), ('Pacific/Chatham', 'Pacific/Chatham'), ('Pacific/Easter', 'Pacific/Easter'), ('Pacific/Efate', 'Pacific/Efate'), ('Pacific/Enderbury', 'Pacific/Enderbury'), ('Pacific/Fakaofo', 'Pacific/Fakaofo'), ('Pacific/Fiji', 'Pacific/Fiji'), ('Pacific/Funafuti', 'Pacific/Funafuti'), ('Pacific/Galapagos', 'Pacific/Galapagos'), ('Pacific/Gambier', 'Pacific/Gambier'), ('Pacific/Guadalcanal', 'Pacific/Guadalcanal'), ('Pacific/Guam', 'Pacific/Guam'), ('Pacific/Honolulu', 'Pacific/Honolulu'), ('Pacific/Kiritimati', 'Pacific/Kiritimati'), ('Pacific/Kosrae', 'Pacific/Kosrae'), ('Pacific/Kwajalein', 'Pacific/Kwajalein'), ('Pacific/Majuro', 'Pacific/Majuro'), ('Pacific/Marquesas', 'Pacific/Marquesas'), ('Pacific/Midway', 'Pacific/Midway'), ('Pacific/Nauru', 'Pacific/Nauru'), ('Pacific/Niue', 'Pacific/Niue'), ('Pacific/Norfolk', 'Pacific/Norfolk'), ('Pacific/Noumea', 'Pacific/Noumea'), ('Pacific/Pago_Pago', 'Pacific/Pago_Pago'), ('Pacific/Palau', 'Pacific/Palau'), ('Pacific/Pitcairn', 'Pacific/Pitcairn'), ('Pacific/Ponape', 'Pacific/Ponape'), ('Pacific/Port_Moresby', 'Pacific/Port_Moresby'), ('Pacific/Rarotonga', 'Pacific/Rarotonga'), ('Pacific/Saipan', 'Pacific/Saipan'), ('Pacific/Tahiti', 'Pacific/Tahiti'), ('Pacific/Tarawa', 'Pacific/Tarawa'), ('Pacific/Tongatapu', 'Pacific/Tongatapu'), ('Pacific/Truk', 'Pacific/Truk'), ('Pacific/Wake', 'Pacific/Wake'), ('Pacific/Wallis', 'Pacific/Wallis')], max_length=50, null=True), + ), + ] diff --git a/backend/server/adventures/migrations/0027_transportation_end_timezone_and_more.py b/backend/server/adventures/migrations/0027_transportation_end_timezone_and_more.py new file mode 100644 index 0000000..6331225 --- /dev/null +++ b/backend/server/adventures/migrations/0027_transportation_end_timezone_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 5.0.11 on 2025-05-10 15:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('adventures', '0026_visit_timezone'), + ] + + operations = [ + migrations.AddField( + model_name='transportation', + name='end_timezone', + field=models.CharField(blank=True, choices=[('Africa/Abidjan', 'Africa/Abidjan'), ('Africa/Accra', 'Africa/Accra'), ('Africa/Addis_Ababa', 'Africa/Addis_Ababa'), ('Africa/Algiers', 'Africa/Algiers'), ('Africa/Asmera', 'Africa/Asmera'), ('Africa/Bamako', 'Africa/Bamako'), ('Africa/Bangui', 'Africa/Bangui'), ('Africa/Banjul', 'Africa/Banjul'), ('Africa/Bissau', 'Africa/Bissau'), ('Africa/Blantyre', 'Africa/Blantyre'), ('Africa/Brazzaville', 'Africa/Brazzaville'), ('Africa/Bujumbura', 'Africa/Bujumbura'), ('Africa/Cairo', 'Africa/Cairo'), ('Africa/Casablanca', 'Africa/Casablanca'), ('Africa/Ceuta', 'Africa/Ceuta'), ('Africa/Conakry', 'Africa/Conakry'), ('Africa/Dakar', 'Africa/Dakar'), ('Africa/Dar_es_Salaam', 'Africa/Dar_es_Salaam'), ('Africa/Djibouti', 'Africa/Djibouti'), ('Africa/Douala', 'Africa/Douala'), ('Africa/El_Aaiun', 'Africa/El_Aaiun'), ('Africa/Freetown', 'Africa/Freetown'), ('Africa/Gaborone', 'Africa/Gaborone'), ('Africa/Harare', 'Africa/Harare'), ('Africa/Johannesburg', 'Africa/Johannesburg'), ('Africa/Juba', 'Africa/Juba'), ('Africa/Kampala', 'Africa/Kampala'), ('Africa/Khartoum', 'Africa/Khartoum'), ('Africa/Kigali', 'Africa/Kigali'), ('Africa/Kinshasa', 'Africa/Kinshasa'), ('Africa/Lagos', 'Africa/Lagos'), ('Africa/Libreville', 'Africa/Libreville'), ('Africa/Lome', 'Africa/Lome'), ('Africa/Luanda', 'Africa/Luanda'), ('Africa/Lubumbashi', 'Africa/Lubumbashi'), ('Africa/Lusaka', 'Africa/Lusaka'), ('Africa/Malabo', 'Africa/Malabo'), ('Africa/Maputo', 'Africa/Maputo'), ('Africa/Maseru', 'Africa/Maseru'), ('Africa/Mbabane', 'Africa/Mbabane'), ('Africa/Mogadishu', 'Africa/Mogadishu'), ('Africa/Monrovia', 'Africa/Monrovia'), ('Africa/Nairobi', 'Africa/Nairobi'), ('Africa/Ndjamena', 'Africa/Ndjamena'), ('Africa/Niamey', 'Africa/Niamey'), ('Africa/Nouakchott', 'Africa/Nouakchott'), ('Africa/Ouagadougou', 'Africa/Ouagadougou'), ('Africa/Porto-Novo', 'Africa/Porto-Novo'), ('Africa/Sao_Tome', 'Africa/Sao_Tome'), ('Africa/Tripoli', 'Africa/Tripoli'), ('Africa/Tunis', 'Africa/Tunis'), ('Africa/Windhoek', 'Africa/Windhoek'), ('America/Adak', 'America/Adak'), ('America/Anchorage', 'America/Anchorage'), ('America/Anguilla', 'America/Anguilla'), ('America/Antigua', 'America/Antigua'), ('America/Araguaina', 'America/Araguaina'), ('America/Argentina/La_Rioja', 'America/Argentina/La_Rioja'), ('America/Argentina/Rio_Gallegos', 'America/Argentina/Rio_Gallegos'), ('America/Argentina/Salta', 'America/Argentina/Salta'), ('America/Argentina/San_Juan', 'America/Argentina/San_Juan'), ('America/Argentina/San_Luis', 'America/Argentina/San_Luis'), ('America/Argentina/Tucuman', 'America/Argentina/Tucuman'), ('America/Argentina/Ushuaia', 'America/Argentina/Ushuaia'), ('America/Aruba', 'America/Aruba'), ('America/Asuncion', 'America/Asuncion'), ('America/Bahia', 'America/Bahia'), ('America/Bahia_Banderas', 'America/Bahia_Banderas'), ('America/Barbados', 'America/Barbados'), ('America/Belem', 'America/Belem'), ('America/Belize', 'America/Belize'), ('America/Blanc-Sablon', 'America/Blanc-Sablon'), ('America/Boa_Vista', 'America/Boa_Vista'), ('America/Bogota', 'America/Bogota'), ('America/Boise', 'America/Boise'), ('America/Buenos_Aires', 'America/Buenos_Aires'), ('America/Cambridge_Bay', 'America/Cambridge_Bay'), ('America/Campo_Grande', 'America/Campo_Grande'), ('America/Cancun', 'America/Cancun'), ('America/Caracas', 'America/Caracas'), ('America/Catamarca', 'America/Catamarca'), ('America/Cayenne', 'America/Cayenne'), ('America/Cayman', 'America/Cayman'), ('America/Chicago', 'America/Chicago'), ('America/Chihuahua', 'America/Chihuahua'), ('America/Ciudad_Juarez', 'America/Ciudad_Juarez'), ('America/Coral_Harbour', 'America/Coral_Harbour'), ('America/Cordoba', 'America/Cordoba'), ('America/Costa_Rica', 'America/Costa_Rica'), ('America/Creston', 'America/Creston'), ('America/Cuiaba', 'America/Cuiaba'), ('America/Curacao', 'America/Curacao'), ('America/Danmarkshavn', 'America/Danmarkshavn'), ('America/Dawson', 'America/Dawson'), ('America/Dawson_Creek', 'America/Dawson_Creek'), ('America/Denver', 'America/Denver'), ('America/Detroit', 'America/Detroit'), ('America/Dominica', 'America/Dominica'), ('America/Edmonton', 'America/Edmonton'), ('America/Eirunepe', 'America/Eirunepe'), ('America/El_Salvador', 'America/El_Salvador'), ('America/Fort_Nelson', 'America/Fort_Nelson'), ('America/Fortaleza', 'America/Fortaleza'), ('America/Glace_Bay', 'America/Glace_Bay'), ('America/Godthab', 'America/Godthab'), ('America/Goose_Bay', 'America/Goose_Bay'), ('America/Grand_Turk', 'America/Grand_Turk'), ('America/Grenada', 'America/Grenada'), ('America/Guadeloupe', 'America/Guadeloupe'), ('America/Guatemala', 'America/Guatemala'), ('America/Guayaquil', 'America/Guayaquil'), ('America/Guyana', 'America/Guyana'), ('America/Halifax', 'America/Halifax'), ('America/Havana', 'America/Havana'), ('America/Hermosillo', 'America/Hermosillo'), ('America/Indiana/Knox', 'America/Indiana/Knox'), ('America/Indiana/Marengo', 'America/Indiana/Marengo'), ('America/Indiana/Petersburg', 'America/Indiana/Petersburg'), ('America/Indiana/Tell_City', 'America/Indiana/Tell_City'), ('America/Indiana/Vevay', 'America/Indiana/Vevay'), ('America/Indiana/Vincennes', 'America/Indiana/Vincennes'), ('America/Indiana/Winamac', 'America/Indiana/Winamac'), ('America/Indianapolis', 'America/Indianapolis'), ('America/Inuvik', 'America/Inuvik'), ('America/Iqaluit', 'America/Iqaluit'), ('America/Jamaica', 'America/Jamaica'), ('America/Jujuy', 'America/Jujuy'), ('America/Juneau', 'America/Juneau'), ('America/Kentucky/Monticello', 'America/Kentucky/Monticello'), ('America/Kralendijk', 'America/Kralendijk'), ('America/La_Paz', 'America/La_Paz'), ('America/Lima', 'America/Lima'), ('America/Los_Angeles', 'America/Los_Angeles'), ('America/Louisville', 'America/Louisville'), ('America/Lower_Princes', 'America/Lower_Princes'), ('America/Maceio', 'America/Maceio'), ('America/Managua', 'America/Managua'), ('America/Manaus', 'America/Manaus'), ('America/Marigot', 'America/Marigot'), ('America/Martinique', 'America/Martinique'), ('America/Matamoros', 'America/Matamoros'), ('America/Mazatlan', 'America/Mazatlan'), ('America/Mendoza', 'America/Mendoza'), ('America/Menominee', 'America/Menominee'), ('America/Merida', 'America/Merida'), ('America/Metlakatla', 'America/Metlakatla'), ('America/Mexico_City', 'America/Mexico_City'), ('America/Miquelon', 'America/Miquelon'), ('America/Moncton', 'America/Moncton'), ('America/Monterrey', 'America/Monterrey'), ('America/Montevideo', 'America/Montevideo'), ('America/Montserrat', 'America/Montserrat'), ('America/Nassau', 'America/Nassau'), ('America/New_York', 'America/New_York'), ('America/Nome', 'America/Nome'), ('America/Noronha', 'America/Noronha'), ('America/North_Dakota/Beulah', 'America/North_Dakota/Beulah'), ('America/North_Dakota/Center', 'America/North_Dakota/Center'), ('America/North_Dakota/New_Salem', 'America/North_Dakota/New_Salem'), ('America/Ojinaga', 'America/Ojinaga'), ('America/Panama', 'America/Panama'), ('America/Paramaribo', 'America/Paramaribo'), ('America/Phoenix', 'America/Phoenix'), ('America/Port-au-Prince', 'America/Port-au-Prince'), ('America/Port_of_Spain', 'America/Port_of_Spain'), ('America/Porto_Velho', 'America/Porto_Velho'), ('America/Puerto_Rico', 'America/Puerto_Rico'), ('America/Punta_Arenas', 'America/Punta_Arenas'), ('America/Rankin_Inlet', 'America/Rankin_Inlet'), ('America/Recife', 'America/Recife'), ('America/Regina', 'America/Regina'), ('America/Resolute', 'America/Resolute'), ('America/Rio_Branco', 'America/Rio_Branco'), ('America/Santarem', 'America/Santarem'), ('America/Santiago', 'America/Santiago'), ('America/Santo_Domingo', 'America/Santo_Domingo'), ('America/Sao_Paulo', 'America/Sao_Paulo'), ('America/Scoresbysund', 'America/Scoresbysund'), ('America/Sitka', 'America/Sitka'), ('America/St_Barthelemy', 'America/St_Barthelemy'), ('America/St_Johns', 'America/St_Johns'), ('America/St_Kitts', 'America/St_Kitts'), ('America/St_Lucia', 'America/St_Lucia'), ('America/St_Thomas', 'America/St_Thomas'), ('America/St_Vincent', 'America/St_Vincent'), ('America/Swift_Current', 'America/Swift_Current'), ('America/Tegucigalpa', 'America/Tegucigalpa'), ('America/Thule', 'America/Thule'), ('America/Tijuana', 'America/Tijuana'), ('America/Toronto', 'America/Toronto'), ('America/Tortola', 'America/Tortola'), ('America/Vancouver', 'America/Vancouver'), ('America/Whitehorse', 'America/Whitehorse'), ('America/Winnipeg', 'America/Winnipeg'), ('America/Yakutat', 'America/Yakutat'), ('Antarctica/Casey', 'Antarctica/Casey'), ('Antarctica/Davis', 'Antarctica/Davis'), ('Antarctica/DumontDUrville', 'Antarctica/DumontDUrville'), ('Antarctica/Macquarie', 'Antarctica/Macquarie'), ('Antarctica/Mawson', 'Antarctica/Mawson'), ('Antarctica/McMurdo', 'Antarctica/McMurdo'), ('Antarctica/Palmer', 'Antarctica/Palmer'), ('Antarctica/Rothera', 'Antarctica/Rothera'), ('Antarctica/Syowa', 'Antarctica/Syowa'), ('Antarctica/Troll', 'Antarctica/Troll'), ('Antarctica/Vostok', 'Antarctica/Vostok'), ('Arctic/Longyearbyen', 'Arctic/Longyearbyen'), ('Asia/Aden', 'Asia/Aden'), ('Asia/Almaty', 'Asia/Almaty'), ('Asia/Amman', 'Asia/Amman'), ('Asia/Anadyr', 'Asia/Anadyr'), ('Asia/Aqtau', 'Asia/Aqtau'), ('Asia/Aqtobe', 'Asia/Aqtobe'), ('Asia/Ashgabat', 'Asia/Ashgabat'), ('Asia/Atyrau', 'Asia/Atyrau'), ('Asia/Baghdad', 'Asia/Baghdad'), ('Asia/Bahrain', 'Asia/Bahrain'), ('Asia/Baku', 'Asia/Baku'), ('Asia/Bangkok', 'Asia/Bangkok'), ('Asia/Barnaul', 'Asia/Barnaul'), ('Asia/Beirut', 'Asia/Beirut'), ('Asia/Bishkek', 'Asia/Bishkek'), ('Asia/Brunei', 'Asia/Brunei'), ('Asia/Calcutta', 'Asia/Calcutta'), ('Asia/Chita', 'Asia/Chita'), ('Asia/Colombo', 'Asia/Colombo'), ('Asia/Damascus', 'Asia/Damascus'), ('Asia/Dhaka', 'Asia/Dhaka'), ('Asia/Dili', 'Asia/Dili'), ('Asia/Dubai', 'Asia/Dubai'), ('Asia/Dushanbe', 'Asia/Dushanbe'), ('Asia/Famagusta', 'Asia/Famagusta'), ('Asia/Gaza', 'Asia/Gaza'), ('Asia/Hebron', 'Asia/Hebron'), ('Asia/Hong_Kong', 'Asia/Hong_Kong'), ('Asia/Hovd', 'Asia/Hovd'), ('Asia/Irkutsk', 'Asia/Irkutsk'), ('Asia/Jakarta', 'Asia/Jakarta'), ('Asia/Jayapura', 'Asia/Jayapura'), ('Asia/Jerusalem', 'Asia/Jerusalem'), ('Asia/Kabul', 'Asia/Kabul'), ('Asia/Kamchatka', 'Asia/Kamchatka'), ('Asia/Karachi', 'Asia/Karachi'), ('Asia/Katmandu', 'Asia/Katmandu'), ('Asia/Khandyga', 'Asia/Khandyga'), ('Asia/Krasnoyarsk', 'Asia/Krasnoyarsk'), ('Asia/Kuala_Lumpur', 'Asia/Kuala_Lumpur'), ('Asia/Kuching', 'Asia/Kuching'), ('Asia/Kuwait', 'Asia/Kuwait'), ('Asia/Macau', 'Asia/Macau'), ('Asia/Magadan', 'Asia/Magadan'), ('Asia/Makassar', 'Asia/Makassar'), ('Asia/Manila', 'Asia/Manila'), ('Asia/Muscat', 'Asia/Muscat'), ('Asia/Nicosia', 'Asia/Nicosia'), ('Asia/Novokuznetsk', 'Asia/Novokuznetsk'), ('Asia/Novosibirsk', 'Asia/Novosibirsk'), ('Asia/Omsk', 'Asia/Omsk'), ('Asia/Oral', 'Asia/Oral'), ('Asia/Phnom_Penh', 'Asia/Phnom_Penh'), ('Asia/Pontianak', 'Asia/Pontianak'), ('Asia/Pyongyang', 'Asia/Pyongyang'), ('Asia/Qatar', 'Asia/Qatar'), ('Asia/Qostanay', 'Asia/Qostanay'), ('Asia/Qyzylorda', 'Asia/Qyzylorda'), ('Asia/Rangoon', 'Asia/Rangoon'), ('Asia/Riyadh', 'Asia/Riyadh'), ('Asia/Saigon', 'Asia/Saigon'), ('Asia/Sakhalin', 'Asia/Sakhalin'), ('Asia/Samarkand', 'Asia/Samarkand'), ('Asia/Seoul', 'Asia/Seoul'), ('Asia/Shanghai', 'Asia/Shanghai'), ('Asia/Singapore', 'Asia/Singapore'), ('Asia/Srednekolymsk', 'Asia/Srednekolymsk'), ('Asia/Taipei', 'Asia/Taipei'), ('Asia/Tashkent', 'Asia/Tashkent'), ('Asia/Tbilisi', 'Asia/Tbilisi'), ('Asia/Tehran', 'Asia/Tehran'), ('Asia/Thimphu', 'Asia/Thimphu'), ('Asia/Tokyo', 'Asia/Tokyo'), ('Asia/Tomsk', 'Asia/Tomsk'), ('Asia/Ulaanbaatar', 'Asia/Ulaanbaatar'), ('Asia/Urumqi', 'Asia/Urumqi'), ('Asia/Ust-Nera', 'Asia/Ust-Nera'), ('Asia/Vientiane', 'Asia/Vientiane'), ('Asia/Vladivostok', 'Asia/Vladivostok'), ('Asia/Yakutsk', 'Asia/Yakutsk'), ('Asia/Yekaterinburg', 'Asia/Yekaterinburg'), ('Asia/Yerevan', 'Asia/Yerevan'), ('Atlantic/Azores', 'Atlantic/Azores'), ('Atlantic/Bermuda', 'Atlantic/Bermuda'), ('Atlantic/Canary', 'Atlantic/Canary'), ('Atlantic/Cape_Verde', 'Atlantic/Cape_Verde'), ('Atlantic/Faeroe', 'Atlantic/Faeroe'), ('Atlantic/Madeira', 'Atlantic/Madeira'), ('Atlantic/Reykjavik', 'Atlantic/Reykjavik'), ('Atlantic/South_Georgia', 'Atlantic/South_Georgia'), ('Atlantic/St_Helena', 'Atlantic/St_Helena'), ('Atlantic/Stanley', 'Atlantic/Stanley'), ('Australia/Adelaide', 'Australia/Adelaide'), ('Australia/Brisbane', 'Australia/Brisbane'), ('Australia/Broken_Hill', 'Australia/Broken_Hill'), ('Australia/Darwin', 'Australia/Darwin'), ('Australia/Eucla', 'Australia/Eucla'), ('Australia/Hobart', 'Australia/Hobart'), ('Australia/Lindeman', 'Australia/Lindeman'), ('Australia/Lord_Howe', 'Australia/Lord_Howe'), ('Australia/Melbourne', 'Australia/Melbourne'), ('Australia/Perth', 'Australia/Perth'), ('Australia/Sydney', 'Australia/Sydney'), ('Europe/Amsterdam', 'Europe/Amsterdam'), ('Europe/Andorra', 'Europe/Andorra'), ('Europe/Astrakhan', 'Europe/Astrakhan'), ('Europe/Athens', 'Europe/Athens'), ('Europe/Belgrade', 'Europe/Belgrade'), ('Europe/Berlin', 'Europe/Berlin'), ('Europe/Bratislava', 'Europe/Bratislava'), ('Europe/Brussels', 'Europe/Brussels'), ('Europe/Bucharest', 'Europe/Bucharest'), ('Europe/Budapest', 'Europe/Budapest'), ('Europe/Busingen', 'Europe/Busingen'), ('Europe/Chisinau', 'Europe/Chisinau'), ('Europe/Copenhagen', 'Europe/Copenhagen'), ('Europe/Dublin', 'Europe/Dublin'), ('Europe/Gibraltar', 'Europe/Gibraltar'), ('Europe/Guernsey', 'Europe/Guernsey'), ('Europe/Helsinki', 'Europe/Helsinki'), ('Europe/Isle_of_Man', 'Europe/Isle_of_Man'), ('Europe/Istanbul', 'Europe/Istanbul'), ('Europe/Jersey', 'Europe/Jersey'), ('Europe/Kaliningrad', 'Europe/Kaliningrad'), ('Europe/Kiev', 'Europe/Kiev'), ('Europe/Kirov', 'Europe/Kirov'), ('Europe/Lisbon', 'Europe/Lisbon'), ('Europe/Ljubljana', 'Europe/Ljubljana'), ('Europe/London', 'Europe/London'), ('Europe/Luxembourg', 'Europe/Luxembourg'), ('Europe/Madrid', 'Europe/Madrid'), ('Europe/Malta', 'Europe/Malta'), ('Europe/Mariehamn', 'Europe/Mariehamn'), ('Europe/Minsk', 'Europe/Minsk'), ('Europe/Monaco', 'Europe/Monaco'), ('Europe/Moscow', 'Europe/Moscow'), ('Europe/Oslo', 'Europe/Oslo'), ('Europe/Paris', 'Europe/Paris'), ('Europe/Podgorica', 'Europe/Podgorica'), ('Europe/Prague', 'Europe/Prague'), ('Europe/Riga', 'Europe/Riga'), ('Europe/Rome', 'Europe/Rome'), ('Europe/Samara', 'Europe/Samara'), ('Europe/San_Marino', 'Europe/San_Marino'), ('Europe/Sarajevo', 'Europe/Sarajevo'), ('Europe/Saratov', 'Europe/Saratov'), ('Europe/Simferopol', 'Europe/Simferopol'), ('Europe/Skopje', 'Europe/Skopje'), ('Europe/Sofia', 'Europe/Sofia'), ('Europe/Stockholm', 'Europe/Stockholm'), ('Europe/Tallinn', 'Europe/Tallinn'), ('Europe/Tirane', 'Europe/Tirane'), ('Europe/Ulyanovsk', 'Europe/Ulyanovsk'), ('Europe/Vaduz', 'Europe/Vaduz'), ('Europe/Vatican', 'Europe/Vatican'), ('Europe/Vienna', 'Europe/Vienna'), ('Europe/Vilnius', 'Europe/Vilnius'), ('Europe/Volgograd', 'Europe/Volgograd'), ('Europe/Warsaw', 'Europe/Warsaw'), ('Europe/Zagreb', 'Europe/Zagreb'), ('Europe/Zurich', 'Europe/Zurich'), ('Indian/Antananarivo', 'Indian/Antananarivo'), ('Indian/Chagos', 'Indian/Chagos'), ('Indian/Christmas', 'Indian/Christmas'), ('Indian/Cocos', 'Indian/Cocos'), ('Indian/Comoro', 'Indian/Comoro'), ('Indian/Kerguelen', 'Indian/Kerguelen'), ('Indian/Mahe', 'Indian/Mahe'), ('Indian/Maldives', 'Indian/Maldives'), ('Indian/Mauritius', 'Indian/Mauritius'), ('Indian/Mayotte', 'Indian/Mayotte'), ('Indian/Reunion', 'Indian/Reunion'), ('Pacific/Apia', 'Pacific/Apia'), ('Pacific/Auckland', 'Pacific/Auckland'), ('Pacific/Bougainville', 'Pacific/Bougainville'), ('Pacific/Chatham', 'Pacific/Chatham'), ('Pacific/Easter', 'Pacific/Easter'), ('Pacific/Efate', 'Pacific/Efate'), ('Pacific/Enderbury', 'Pacific/Enderbury'), ('Pacific/Fakaofo', 'Pacific/Fakaofo'), ('Pacific/Fiji', 'Pacific/Fiji'), ('Pacific/Funafuti', 'Pacific/Funafuti'), ('Pacific/Galapagos', 'Pacific/Galapagos'), ('Pacific/Gambier', 'Pacific/Gambier'), ('Pacific/Guadalcanal', 'Pacific/Guadalcanal'), ('Pacific/Guam', 'Pacific/Guam'), ('Pacific/Honolulu', 'Pacific/Honolulu'), ('Pacific/Kiritimati', 'Pacific/Kiritimati'), ('Pacific/Kosrae', 'Pacific/Kosrae'), ('Pacific/Kwajalein', 'Pacific/Kwajalein'), ('Pacific/Majuro', 'Pacific/Majuro'), ('Pacific/Marquesas', 'Pacific/Marquesas'), ('Pacific/Midway', 'Pacific/Midway'), ('Pacific/Nauru', 'Pacific/Nauru'), ('Pacific/Niue', 'Pacific/Niue'), ('Pacific/Norfolk', 'Pacific/Norfolk'), ('Pacific/Noumea', 'Pacific/Noumea'), ('Pacific/Pago_Pago', 'Pacific/Pago_Pago'), ('Pacific/Palau', 'Pacific/Palau'), ('Pacific/Pitcairn', 'Pacific/Pitcairn'), ('Pacific/Ponape', 'Pacific/Ponape'), ('Pacific/Port_Moresby', 'Pacific/Port_Moresby'), ('Pacific/Rarotonga', 'Pacific/Rarotonga'), ('Pacific/Saipan', 'Pacific/Saipan'), ('Pacific/Tahiti', 'Pacific/Tahiti'), ('Pacific/Tarawa', 'Pacific/Tarawa'), ('Pacific/Tongatapu', 'Pacific/Tongatapu'), ('Pacific/Truk', 'Pacific/Truk'), ('Pacific/Wake', 'Pacific/Wake'), ('Pacific/Wallis', 'Pacific/Wallis')], max_length=50, null=True), + ), + migrations.AddField( + model_name='transportation', + name='start_timezone', + field=models.CharField(blank=True, choices=[('Africa/Abidjan', 'Africa/Abidjan'), ('Africa/Accra', 'Africa/Accra'), ('Africa/Addis_Ababa', 'Africa/Addis_Ababa'), ('Africa/Algiers', 'Africa/Algiers'), ('Africa/Asmera', 'Africa/Asmera'), ('Africa/Bamako', 'Africa/Bamako'), ('Africa/Bangui', 'Africa/Bangui'), ('Africa/Banjul', 'Africa/Banjul'), ('Africa/Bissau', 'Africa/Bissau'), ('Africa/Blantyre', 'Africa/Blantyre'), ('Africa/Brazzaville', 'Africa/Brazzaville'), ('Africa/Bujumbura', 'Africa/Bujumbura'), ('Africa/Cairo', 'Africa/Cairo'), ('Africa/Casablanca', 'Africa/Casablanca'), ('Africa/Ceuta', 'Africa/Ceuta'), ('Africa/Conakry', 'Africa/Conakry'), ('Africa/Dakar', 'Africa/Dakar'), ('Africa/Dar_es_Salaam', 'Africa/Dar_es_Salaam'), ('Africa/Djibouti', 'Africa/Djibouti'), ('Africa/Douala', 'Africa/Douala'), ('Africa/El_Aaiun', 'Africa/El_Aaiun'), ('Africa/Freetown', 'Africa/Freetown'), ('Africa/Gaborone', 'Africa/Gaborone'), ('Africa/Harare', 'Africa/Harare'), ('Africa/Johannesburg', 'Africa/Johannesburg'), ('Africa/Juba', 'Africa/Juba'), ('Africa/Kampala', 'Africa/Kampala'), ('Africa/Khartoum', 'Africa/Khartoum'), ('Africa/Kigali', 'Africa/Kigali'), ('Africa/Kinshasa', 'Africa/Kinshasa'), ('Africa/Lagos', 'Africa/Lagos'), ('Africa/Libreville', 'Africa/Libreville'), ('Africa/Lome', 'Africa/Lome'), ('Africa/Luanda', 'Africa/Luanda'), ('Africa/Lubumbashi', 'Africa/Lubumbashi'), ('Africa/Lusaka', 'Africa/Lusaka'), ('Africa/Malabo', 'Africa/Malabo'), ('Africa/Maputo', 'Africa/Maputo'), ('Africa/Maseru', 'Africa/Maseru'), ('Africa/Mbabane', 'Africa/Mbabane'), ('Africa/Mogadishu', 'Africa/Mogadishu'), ('Africa/Monrovia', 'Africa/Monrovia'), ('Africa/Nairobi', 'Africa/Nairobi'), ('Africa/Ndjamena', 'Africa/Ndjamena'), ('Africa/Niamey', 'Africa/Niamey'), ('Africa/Nouakchott', 'Africa/Nouakchott'), ('Africa/Ouagadougou', 'Africa/Ouagadougou'), ('Africa/Porto-Novo', 'Africa/Porto-Novo'), ('Africa/Sao_Tome', 'Africa/Sao_Tome'), ('Africa/Tripoli', 'Africa/Tripoli'), ('Africa/Tunis', 'Africa/Tunis'), ('Africa/Windhoek', 'Africa/Windhoek'), ('America/Adak', 'America/Adak'), ('America/Anchorage', 'America/Anchorage'), ('America/Anguilla', 'America/Anguilla'), ('America/Antigua', 'America/Antigua'), ('America/Araguaina', 'America/Araguaina'), ('America/Argentina/La_Rioja', 'America/Argentina/La_Rioja'), ('America/Argentina/Rio_Gallegos', 'America/Argentina/Rio_Gallegos'), ('America/Argentina/Salta', 'America/Argentina/Salta'), ('America/Argentina/San_Juan', 'America/Argentina/San_Juan'), ('America/Argentina/San_Luis', 'America/Argentina/San_Luis'), ('America/Argentina/Tucuman', 'America/Argentina/Tucuman'), ('America/Argentina/Ushuaia', 'America/Argentina/Ushuaia'), ('America/Aruba', 'America/Aruba'), ('America/Asuncion', 'America/Asuncion'), ('America/Bahia', 'America/Bahia'), ('America/Bahia_Banderas', 'America/Bahia_Banderas'), ('America/Barbados', 'America/Barbados'), ('America/Belem', 'America/Belem'), ('America/Belize', 'America/Belize'), ('America/Blanc-Sablon', 'America/Blanc-Sablon'), ('America/Boa_Vista', 'America/Boa_Vista'), ('America/Bogota', 'America/Bogota'), ('America/Boise', 'America/Boise'), ('America/Buenos_Aires', 'America/Buenos_Aires'), ('America/Cambridge_Bay', 'America/Cambridge_Bay'), ('America/Campo_Grande', 'America/Campo_Grande'), ('America/Cancun', 'America/Cancun'), ('America/Caracas', 'America/Caracas'), ('America/Catamarca', 'America/Catamarca'), ('America/Cayenne', 'America/Cayenne'), ('America/Cayman', 'America/Cayman'), ('America/Chicago', 'America/Chicago'), ('America/Chihuahua', 'America/Chihuahua'), ('America/Ciudad_Juarez', 'America/Ciudad_Juarez'), ('America/Coral_Harbour', 'America/Coral_Harbour'), ('America/Cordoba', 'America/Cordoba'), ('America/Costa_Rica', 'America/Costa_Rica'), ('America/Creston', 'America/Creston'), ('America/Cuiaba', 'America/Cuiaba'), ('America/Curacao', 'America/Curacao'), ('America/Danmarkshavn', 'America/Danmarkshavn'), ('America/Dawson', 'America/Dawson'), ('America/Dawson_Creek', 'America/Dawson_Creek'), ('America/Denver', 'America/Denver'), ('America/Detroit', 'America/Detroit'), ('America/Dominica', 'America/Dominica'), ('America/Edmonton', 'America/Edmonton'), ('America/Eirunepe', 'America/Eirunepe'), ('America/El_Salvador', 'America/El_Salvador'), ('America/Fort_Nelson', 'America/Fort_Nelson'), ('America/Fortaleza', 'America/Fortaleza'), ('America/Glace_Bay', 'America/Glace_Bay'), ('America/Godthab', 'America/Godthab'), ('America/Goose_Bay', 'America/Goose_Bay'), ('America/Grand_Turk', 'America/Grand_Turk'), ('America/Grenada', 'America/Grenada'), ('America/Guadeloupe', 'America/Guadeloupe'), ('America/Guatemala', 'America/Guatemala'), ('America/Guayaquil', 'America/Guayaquil'), ('America/Guyana', 'America/Guyana'), ('America/Halifax', 'America/Halifax'), ('America/Havana', 'America/Havana'), ('America/Hermosillo', 'America/Hermosillo'), ('America/Indiana/Knox', 'America/Indiana/Knox'), ('America/Indiana/Marengo', 'America/Indiana/Marengo'), ('America/Indiana/Petersburg', 'America/Indiana/Petersburg'), ('America/Indiana/Tell_City', 'America/Indiana/Tell_City'), ('America/Indiana/Vevay', 'America/Indiana/Vevay'), ('America/Indiana/Vincennes', 'America/Indiana/Vincennes'), ('America/Indiana/Winamac', 'America/Indiana/Winamac'), ('America/Indianapolis', 'America/Indianapolis'), ('America/Inuvik', 'America/Inuvik'), ('America/Iqaluit', 'America/Iqaluit'), ('America/Jamaica', 'America/Jamaica'), ('America/Jujuy', 'America/Jujuy'), ('America/Juneau', 'America/Juneau'), ('America/Kentucky/Monticello', 'America/Kentucky/Monticello'), ('America/Kralendijk', 'America/Kralendijk'), ('America/La_Paz', 'America/La_Paz'), ('America/Lima', 'America/Lima'), ('America/Los_Angeles', 'America/Los_Angeles'), ('America/Louisville', 'America/Louisville'), ('America/Lower_Princes', 'America/Lower_Princes'), ('America/Maceio', 'America/Maceio'), ('America/Managua', 'America/Managua'), ('America/Manaus', 'America/Manaus'), ('America/Marigot', 'America/Marigot'), ('America/Martinique', 'America/Martinique'), ('America/Matamoros', 'America/Matamoros'), ('America/Mazatlan', 'America/Mazatlan'), ('America/Mendoza', 'America/Mendoza'), ('America/Menominee', 'America/Menominee'), ('America/Merida', 'America/Merida'), ('America/Metlakatla', 'America/Metlakatla'), ('America/Mexico_City', 'America/Mexico_City'), ('America/Miquelon', 'America/Miquelon'), ('America/Moncton', 'America/Moncton'), ('America/Monterrey', 'America/Monterrey'), ('America/Montevideo', 'America/Montevideo'), ('America/Montserrat', 'America/Montserrat'), ('America/Nassau', 'America/Nassau'), ('America/New_York', 'America/New_York'), ('America/Nome', 'America/Nome'), ('America/Noronha', 'America/Noronha'), ('America/North_Dakota/Beulah', 'America/North_Dakota/Beulah'), ('America/North_Dakota/Center', 'America/North_Dakota/Center'), ('America/North_Dakota/New_Salem', 'America/North_Dakota/New_Salem'), ('America/Ojinaga', 'America/Ojinaga'), ('America/Panama', 'America/Panama'), ('America/Paramaribo', 'America/Paramaribo'), ('America/Phoenix', 'America/Phoenix'), ('America/Port-au-Prince', 'America/Port-au-Prince'), ('America/Port_of_Spain', 'America/Port_of_Spain'), ('America/Porto_Velho', 'America/Porto_Velho'), ('America/Puerto_Rico', 'America/Puerto_Rico'), ('America/Punta_Arenas', 'America/Punta_Arenas'), ('America/Rankin_Inlet', 'America/Rankin_Inlet'), ('America/Recife', 'America/Recife'), ('America/Regina', 'America/Regina'), ('America/Resolute', 'America/Resolute'), ('America/Rio_Branco', 'America/Rio_Branco'), ('America/Santarem', 'America/Santarem'), ('America/Santiago', 'America/Santiago'), ('America/Santo_Domingo', 'America/Santo_Domingo'), ('America/Sao_Paulo', 'America/Sao_Paulo'), ('America/Scoresbysund', 'America/Scoresbysund'), ('America/Sitka', 'America/Sitka'), ('America/St_Barthelemy', 'America/St_Barthelemy'), ('America/St_Johns', 'America/St_Johns'), ('America/St_Kitts', 'America/St_Kitts'), ('America/St_Lucia', 'America/St_Lucia'), ('America/St_Thomas', 'America/St_Thomas'), ('America/St_Vincent', 'America/St_Vincent'), ('America/Swift_Current', 'America/Swift_Current'), ('America/Tegucigalpa', 'America/Tegucigalpa'), ('America/Thule', 'America/Thule'), ('America/Tijuana', 'America/Tijuana'), ('America/Toronto', 'America/Toronto'), ('America/Tortola', 'America/Tortola'), ('America/Vancouver', 'America/Vancouver'), ('America/Whitehorse', 'America/Whitehorse'), ('America/Winnipeg', 'America/Winnipeg'), ('America/Yakutat', 'America/Yakutat'), ('Antarctica/Casey', 'Antarctica/Casey'), ('Antarctica/Davis', 'Antarctica/Davis'), ('Antarctica/DumontDUrville', 'Antarctica/DumontDUrville'), ('Antarctica/Macquarie', 'Antarctica/Macquarie'), ('Antarctica/Mawson', 'Antarctica/Mawson'), ('Antarctica/McMurdo', 'Antarctica/McMurdo'), ('Antarctica/Palmer', 'Antarctica/Palmer'), ('Antarctica/Rothera', 'Antarctica/Rothera'), ('Antarctica/Syowa', 'Antarctica/Syowa'), ('Antarctica/Troll', 'Antarctica/Troll'), ('Antarctica/Vostok', 'Antarctica/Vostok'), ('Arctic/Longyearbyen', 'Arctic/Longyearbyen'), ('Asia/Aden', 'Asia/Aden'), ('Asia/Almaty', 'Asia/Almaty'), ('Asia/Amman', 'Asia/Amman'), ('Asia/Anadyr', 'Asia/Anadyr'), ('Asia/Aqtau', 'Asia/Aqtau'), ('Asia/Aqtobe', 'Asia/Aqtobe'), ('Asia/Ashgabat', 'Asia/Ashgabat'), ('Asia/Atyrau', 'Asia/Atyrau'), ('Asia/Baghdad', 'Asia/Baghdad'), ('Asia/Bahrain', 'Asia/Bahrain'), ('Asia/Baku', 'Asia/Baku'), ('Asia/Bangkok', 'Asia/Bangkok'), ('Asia/Barnaul', 'Asia/Barnaul'), ('Asia/Beirut', 'Asia/Beirut'), ('Asia/Bishkek', 'Asia/Bishkek'), ('Asia/Brunei', 'Asia/Brunei'), ('Asia/Calcutta', 'Asia/Calcutta'), ('Asia/Chita', 'Asia/Chita'), ('Asia/Colombo', 'Asia/Colombo'), ('Asia/Damascus', 'Asia/Damascus'), ('Asia/Dhaka', 'Asia/Dhaka'), ('Asia/Dili', 'Asia/Dili'), ('Asia/Dubai', 'Asia/Dubai'), ('Asia/Dushanbe', 'Asia/Dushanbe'), ('Asia/Famagusta', 'Asia/Famagusta'), ('Asia/Gaza', 'Asia/Gaza'), ('Asia/Hebron', 'Asia/Hebron'), ('Asia/Hong_Kong', 'Asia/Hong_Kong'), ('Asia/Hovd', 'Asia/Hovd'), ('Asia/Irkutsk', 'Asia/Irkutsk'), ('Asia/Jakarta', 'Asia/Jakarta'), ('Asia/Jayapura', 'Asia/Jayapura'), ('Asia/Jerusalem', 'Asia/Jerusalem'), ('Asia/Kabul', 'Asia/Kabul'), ('Asia/Kamchatka', 'Asia/Kamchatka'), ('Asia/Karachi', 'Asia/Karachi'), ('Asia/Katmandu', 'Asia/Katmandu'), ('Asia/Khandyga', 'Asia/Khandyga'), ('Asia/Krasnoyarsk', 'Asia/Krasnoyarsk'), ('Asia/Kuala_Lumpur', 'Asia/Kuala_Lumpur'), ('Asia/Kuching', 'Asia/Kuching'), ('Asia/Kuwait', 'Asia/Kuwait'), ('Asia/Macau', 'Asia/Macau'), ('Asia/Magadan', 'Asia/Magadan'), ('Asia/Makassar', 'Asia/Makassar'), ('Asia/Manila', 'Asia/Manila'), ('Asia/Muscat', 'Asia/Muscat'), ('Asia/Nicosia', 'Asia/Nicosia'), ('Asia/Novokuznetsk', 'Asia/Novokuznetsk'), ('Asia/Novosibirsk', 'Asia/Novosibirsk'), ('Asia/Omsk', 'Asia/Omsk'), ('Asia/Oral', 'Asia/Oral'), ('Asia/Phnom_Penh', 'Asia/Phnom_Penh'), ('Asia/Pontianak', 'Asia/Pontianak'), ('Asia/Pyongyang', 'Asia/Pyongyang'), ('Asia/Qatar', 'Asia/Qatar'), ('Asia/Qostanay', 'Asia/Qostanay'), ('Asia/Qyzylorda', 'Asia/Qyzylorda'), ('Asia/Rangoon', 'Asia/Rangoon'), ('Asia/Riyadh', 'Asia/Riyadh'), ('Asia/Saigon', 'Asia/Saigon'), ('Asia/Sakhalin', 'Asia/Sakhalin'), ('Asia/Samarkand', 'Asia/Samarkand'), ('Asia/Seoul', 'Asia/Seoul'), ('Asia/Shanghai', 'Asia/Shanghai'), ('Asia/Singapore', 'Asia/Singapore'), ('Asia/Srednekolymsk', 'Asia/Srednekolymsk'), ('Asia/Taipei', 'Asia/Taipei'), ('Asia/Tashkent', 'Asia/Tashkent'), ('Asia/Tbilisi', 'Asia/Tbilisi'), ('Asia/Tehran', 'Asia/Tehran'), ('Asia/Thimphu', 'Asia/Thimphu'), ('Asia/Tokyo', 'Asia/Tokyo'), ('Asia/Tomsk', 'Asia/Tomsk'), ('Asia/Ulaanbaatar', 'Asia/Ulaanbaatar'), ('Asia/Urumqi', 'Asia/Urumqi'), ('Asia/Ust-Nera', 'Asia/Ust-Nera'), ('Asia/Vientiane', 'Asia/Vientiane'), ('Asia/Vladivostok', 'Asia/Vladivostok'), ('Asia/Yakutsk', 'Asia/Yakutsk'), ('Asia/Yekaterinburg', 'Asia/Yekaterinburg'), ('Asia/Yerevan', 'Asia/Yerevan'), ('Atlantic/Azores', 'Atlantic/Azores'), ('Atlantic/Bermuda', 'Atlantic/Bermuda'), ('Atlantic/Canary', 'Atlantic/Canary'), ('Atlantic/Cape_Verde', 'Atlantic/Cape_Verde'), ('Atlantic/Faeroe', 'Atlantic/Faeroe'), ('Atlantic/Madeira', 'Atlantic/Madeira'), ('Atlantic/Reykjavik', 'Atlantic/Reykjavik'), ('Atlantic/South_Georgia', 'Atlantic/South_Georgia'), ('Atlantic/St_Helena', 'Atlantic/St_Helena'), ('Atlantic/Stanley', 'Atlantic/Stanley'), ('Australia/Adelaide', 'Australia/Adelaide'), ('Australia/Brisbane', 'Australia/Brisbane'), ('Australia/Broken_Hill', 'Australia/Broken_Hill'), ('Australia/Darwin', 'Australia/Darwin'), ('Australia/Eucla', 'Australia/Eucla'), ('Australia/Hobart', 'Australia/Hobart'), ('Australia/Lindeman', 'Australia/Lindeman'), ('Australia/Lord_Howe', 'Australia/Lord_Howe'), ('Australia/Melbourne', 'Australia/Melbourne'), ('Australia/Perth', 'Australia/Perth'), ('Australia/Sydney', 'Australia/Sydney'), ('Europe/Amsterdam', 'Europe/Amsterdam'), ('Europe/Andorra', 'Europe/Andorra'), ('Europe/Astrakhan', 'Europe/Astrakhan'), ('Europe/Athens', 'Europe/Athens'), ('Europe/Belgrade', 'Europe/Belgrade'), ('Europe/Berlin', 'Europe/Berlin'), ('Europe/Bratislava', 'Europe/Bratislava'), ('Europe/Brussels', 'Europe/Brussels'), ('Europe/Bucharest', 'Europe/Bucharest'), ('Europe/Budapest', 'Europe/Budapest'), ('Europe/Busingen', 'Europe/Busingen'), ('Europe/Chisinau', 'Europe/Chisinau'), ('Europe/Copenhagen', 'Europe/Copenhagen'), ('Europe/Dublin', 'Europe/Dublin'), ('Europe/Gibraltar', 'Europe/Gibraltar'), ('Europe/Guernsey', 'Europe/Guernsey'), ('Europe/Helsinki', 'Europe/Helsinki'), ('Europe/Isle_of_Man', 'Europe/Isle_of_Man'), ('Europe/Istanbul', 'Europe/Istanbul'), ('Europe/Jersey', 'Europe/Jersey'), ('Europe/Kaliningrad', 'Europe/Kaliningrad'), ('Europe/Kiev', 'Europe/Kiev'), ('Europe/Kirov', 'Europe/Kirov'), ('Europe/Lisbon', 'Europe/Lisbon'), ('Europe/Ljubljana', 'Europe/Ljubljana'), ('Europe/London', 'Europe/London'), ('Europe/Luxembourg', 'Europe/Luxembourg'), ('Europe/Madrid', 'Europe/Madrid'), ('Europe/Malta', 'Europe/Malta'), ('Europe/Mariehamn', 'Europe/Mariehamn'), ('Europe/Minsk', 'Europe/Minsk'), ('Europe/Monaco', 'Europe/Monaco'), ('Europe/Moscow', 'Europe/Moscow'), ('Europe/Oslo', 'Europe/Oslo'), ('Europe/Paris', 'Europe/Paris'), ('Europe/Podgorica', 'Europe/Podgorica'), ('Europe/Prague', 'Europe/Prague'), ('Europe/Riga', 'Europe/Riga'), ('Europe/Rome', 'Europe/Rome'), ('Europe/Samara', 'Europe/Samara'), ('Europe/San_Marino', 'Europe/San_Marino'), ('Europe/Sarajevo', 'Europe/Sarajevo'), ('Europe/Saratov', 'Europe/Saratov'), ('Europe/Simferopol', 'Europe/Simferopol'), ('Europe/Skopje', 'Europe/Skopje'), ('Europe/Sofia', 'Europe/Sofia'), ('Europe/Stockholm', 'Europe/Stockholm'), ('Europe/Tallinn', 'Europe/Tallinn'), ('Europe/Tirane', 'Europe/Tirane'), ('Europe/Ulyanovsk', 'Europe/Ulyanovsk'), ('Europe/Vaduz', 'Europe/Vaduz'), ('Europe/Vatican', 'Europe/Vatican'), ('Europe/Vienna', 'Europe/Vienna'), ('Europe/Vilnius', 'Europe/Vilnius'), ('Europe/Volgograd', 'Europe/Volgograd'), ('Europe/Warsaw', 'Europe/Warsaw'), ('Europe/Zagreb', 'Europe/Zagreb'), ('Europe/Zurich', 'Europe/Zurich'), ('Indian/Antananarivo', 'Indian/Antananarivo'), ('Indian/Chagos', 'Indian/Chagos'), ('Indian/Christmas', 'Indian/Christmas'), ('Indian/Cocos', 'Indian/Cocos'), ('Indian/Comoro', 'Indian/Comoro'), ('Indian/Kerguelen', 'Indian/Kerguelen'), ('Indian/Mahe', 'Indian/Mahe'), ('Indian/Maldives', 'Indian/Maldives'), ('Indian/Mauritius', 'Indian/Mauritius'), ('Indian/Mayotte', 'Indian/Mayotte'), ('Indian/Reunion', 'Indian/Reunion'), ('Pacific/Apia', 'Pacific/Apia'), ('Pacific/Auckland', 'Pacific/Auckland'), ('Pacific/Bougainville', 'Pacific/Bougainville'), ('Pacific/Chatham', 'Pacific/Chatham'), ('Pacific/Easter', 'Pacific/Easter'), ('Pacific/Efate', 'Pacific/Efate'), ('Pacific/Enderbury', 'Pacific/Enderbury'), ('Pacific/Fakaofo', 'Pacific/Fakaofo'), ('Pacific/Fiji', 'Pacific/Fiji'), ('Pacific/Funafuti', 'Pacific/Funafuti'), ('Pacific/Galapagos', 'Pacific/Galapagos'), ('Pacific/Gambier', 'Pacific/Gambier'), ('Pacific/Guadalcanal', 'Pacific/Guadalcanal'), ('Pacific/Guam', 'Pacific/Guam'), ('Pacific/Honolulu', 'Pacific/Honolulu'), ('Pacific/Kiritimati', 'Pacific/Kiritimati'), ('Pacific/Kosrae', 'Pacific/Kosrae'), ('Pacific/Kwajalein', 'Pacific/Kwajalein'), ('Pacific/Majuro', 'Pacific/Majuro'), ('Pacific/Marquesas', 'Pacific/Marquesas'), ('Pacific/Midway', 'Pacific/Midway'), ('Pacific/Nauru', 'Pacific/Nauru'), ('Pacific/Niue', 'Pacific/Niue'), ('Pacific/Norfolk', 'Pacific/Norfolk'), ('Pacific/Noumea', 'Pacific/Noumea'), ('Pacific/Pago_Pago', 'Pacific/Pago_Pago'), ('Pacific/Palau', 'Pacific/Palau'), ('Pacific/Pitcairn', 'Pacific/Pitcairn'), ('Pacific/Ponape', 'Pacific/Ponape'), ('Pacific/Port_Moresby', 'Pacific/Port_Moresby'), ('Pacific/Rarotonga', 'Pacific/Rarotonga'), ('Pacific/Saipan', 'Pacific/Saipan'), ('Pacific/Tahiti', 'Pacific/Tahiti'), ('Pacific/Tarawa', 'Pacific/Tarawa'), ('Pacific/Tongatapu', 'Pacific/Tongatapu'), ('Pacific/Truk', 'Pacific/Truk'), ('Pacific/Wake', 'Pacific/Wake'), ('Pacific/Wallis', 'Pacific/Wallis')], max_length=50, null=True), + ), + ] diff --git a/backend/server/adventures/migrations/0028_lodging_timezone.py b/backend/server/adventures/migrations/0028_lodging_timezone.py new file mode 100644 index 0000000..d9513f7 --- /dev/null +++ b/backend/server/adventures/migrations/0028_lodging_timezone.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.11 on 2025-05-10 15:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('adventures', '0027_transportation_end_timezone_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='lodging', + name='timezone', + field=models.CharField(blank=True, choices=[('Africa/Abidjan', 'Africa/Abidjan'), ('Africa/Accra', 'Africa/Accra'), ('Africa/Addis_Ababa', 'Africa/Addis_Ababa'), ('Africa/Algiers', 'Africa/Algiers'), ('Africa/Asmera', 'Africa/Asmera'), ('Africa/Bamako', 'Africa/Bamako'), ('Africa/Bangui', 'Africa/Bangui'), ('Africa/Banjul', 'Africa/Banjul'), ('Africa/Bissau', 'Africa/Bissau'), ('Africa/Blantyre', 'Africa/Blantyre'), ('Africa/Brazzaville', 'Africa/Brazzaville'), ('Africa/Bujumbura', 'Africa/Bujumbura'), ('Africa/Cairo', 'Africa/Cairo'), ('Africa/Casablanca', 'Africa/Casablanca'), ('Africa/Ceuta', 'Africa/Ceuta'), ('Africa/Conakry', 'Africa/Conakry'), ('Africa/Dakar', 'Africa/Dakar'), ('Africa/Dar_es_Salaam', 'Africa/Dar_es_Salaam'), ('Africa/Djibouti', 'Africa/Djibouti'), ('Africa/Douala', 'Africa/Douala'), ('Africa/El_Aaiun', 'Africa/El_Aaiun'), ('Africa/Freetown', 'Africa/Freetown'), ('Africa/Gaborone', 'Africa/Gaborone'), ('Africa/Harare', 'Africa/Harare'), ('Africa/Johannesburg', 'Africa/Johannesburg'), ('Africa/Juba', 'Africa/Juba'), ('Africa/Kampala', 'Africa/Kampala'), ('Africa/Khartoum', 'Africa/Khartoum'), ('Africa/Kigali', 'Africa/Kigali'), ('Africa/Kinshasa', 'Africa/Kinshasa'), ('Africa/Lagos', 'Africa/Lagos'), ('Africa/Libreville', 'Africa/Libreville'), ('Africa/Lome', 'Africa/Lome'), ('Africa/Luanda', 'Africa/Luanda'), ('Africa/Lubumbashi', 'Africa/Lubumbashi'), ('Africa/Lusaka', 'Africa/Lusaka'), ('Africa/Malabo', 'Africa/Malabo'), ('Africa/Maputo', 'Africa/Maputo'), ('Africa/Maseru', 'Africa/Maseru'), ('Africa/Mbabane', 'Africa/Mbabane'), ('Africa/Mogadishu', 'Africa/Mogadishu'), ('Africa/Monrovia', 'Africa/Monrovia'), ('Africa/Nairobi', 'Africa/Nairobi'), ('Africa/Ndjamena', 'Africa/Ndjamena'), ('Africa/Niamey', 'Africa/Niamey'), ('Africa/Nouakchott', 'Africa/Nouakchott'), ('Africa/Ouagadougou', 'Africa/Ouagadougou'), ('Africa/Porto-Novo', 'Africa/Porto-Novo'), ('Africa/Sao_Tome', 'Africa/Sao_Tome'), ('Africa/Tripoli', 'Africa/Tripoli'), ('Africa/Tunis', 'Africa/Tunis'), ('Africa/Windhoek', 'Africa/Windhoek'), ('America/Adak', 'America/Adak'), ('America/Anchorage', 'America/Anchorage'), ('America/Anguilla', 'America/Anguilla'), ('America/Antigua', 'America/Antigua'), ('America/Araguaina', 'America/Araguaina'), ('America/Argentina/La_Rioja', 'America/Argentina/La_Rioja'), ('America/Argentina/Rio_Gallegos', 'America/Argentina/Rio_Gallegos'), ('America/Argentina/Salta', 'America/Argentina/Salta'), ('America/Argentina/San_Juan', 'America/Argentina/San_Juan'), ('America/Argentina/San_Luis', 'America/Argentina/San_Luis'), ('America/Argentina/Tucuman', 'America/Argentina/Tucuman'), ('America/Argentina/Ushuaia', 'America/Argentina/Ushuaia'), ('America/Aruba', 'America/Aruba'), ('America/Asuncion', 'America/Asuncion'), ('America/Bahia', 'America/Bahia'), ('America/Bahia_Banderas', 'America/Bahia_Banderas'), ('America/Barbados', 'America/Barbados'), ('America/Belem', 'America/Belem'), ('America/Belize', 'America/Belize'), ('America/Blanc-Sablon', 'America/Blanc-Sablon'), ('America/Boa_Vista', 'America/Boa_Vista'), ('America/Bogota', 'America/Bogota'), ('America/Boise', 'America/Boise'), ('America/Buenos_Aires', 'America/Buenos_Aires'), ('America/Cambridge_Bay', 'America/Cambridge_Bay'), ('America/Campo_Grande', 'America/Campo_Grande'), ('America/Cancun', 'America/Cancun'), ('America/Caracas', 'America/Caracas'), ('America/Catamarca', 'America/Catamarca'), ('America/Cayenne', 'America/Cayenne'), ('America/Cayman', 'America/Cayman'), ('America/Chicago', 'America/Chicago'), ('America/Chihuahua', 'America/Chihuahua'), ('America/Ciudad_Juarez', 'America/Ciudad_Juarez'), ('America/Coral_Harbour', 'America/Coral_Harbour'), ('America/Cordoba', 'America/Cordoba'), ('America/Costa_Rica', 'America/Costa_Rica'), ('America/Creston', 'America/Creston'), ('America/Cuiaba', 'America/Cuiaba'), ('America/Curacao', 'America/Curacao'), ('America/Danmarkshavn', 'America/Danmarkshavn'), ('America/Dawson', 'America/Dawson'), ('America/Dawson_Creek', 'America/Dawson_Creek'), ('America/Denver', 'America/Denver'), ('America/Detroit', 'America/Detroit'), ('America/Dominica', 'America/Dominica'), ('America/Edmonton', 'America/Edmonton'), ('America/Eirunepe', 'America/Eirunepe'), ('America/El_Salvador', 'America/El_Salvador'), ('America/Fort_Nelson', 'America/Fort_Nelson'), ('America/Fortaleza', 'America/Fortaleza'), ('America/Glace_Bay', 'America/Glace_Bay'), ('America/Godthab', 'America/Godthab'), ('America/Goose_Bay', 'America/Goose_Bay'), ('America/Grand_Turk', 'America/Grand_Turk'), ('America/Grenada', 'America/Grenada'), ('America/Guadeloupe', 'America/Guadeloupe'), ('America/Guatemala', 'America/Guatemala'), ('America/Guayaquil', 'America/Guayaquil'), ('America/Guyana', 'America/Guyana'), ('America/Halifax', 'America/Halifax'), ('America/Havana', 'America/Havana'), ('America/Hermosillo', 'America/Hermosillo'), ('America/Indiana/Knox', 'America/Indiana/Knox'), ('America/Indiana/Marengo', 'America/Indiana/Marengo'), ('America/Indiana/Petersburg', 'America/Indiana/Petersburg'), ('America/Indiana/Tell_City', 'America/Indiana/Tell_City'), ('America/Indiana/Vevay', 'America/Indiana/Vevay'), ('America/Indiana/Vincennes', 'America/Indiana/Vincennes'), ('America/Indiana/Winamac', 'America/Indiana/Winamac'), ('America/Indianapolis', 'America/Indianapolis'), ('America/Inuvik', 'America/Inuvik'), ('America/Iqaluit', 'America/Iqaluit'), ('America/Jamaica', 'America/Jamaica'), ('America/Jujuy', 'America/Jujuy'), ('America/Juneau', 'America/Juneau'), ('America/Kentucky/Monticello', 'America/Kentucky/Monticello'), ('America/Kralendijk', 'America/Kralendijk'), ('America/La_Paz', 'America/La_Paz'), ('America/Lima', 'America/Lima'), ('America/Los_Angeles', 'America/Los_Angeles'), ('America/Louisville', 'America/Louisville'), ('America/Lower_Princes', 'America/Lower_Princes'), ('America/Maceio', 'America/Maceio'), ('America/Managua', 'America/Managua'), ('America/Manaus', 'America/Manaus'), ('America/Marigot', 'America/Marigot'), ('America/Martinique', 'America/Martinique'), ('America/Matamoros', 'America/Matamoros'), ('America/Mazatlan', 'America/Mazatlan'), ('America/Mendoza', 'America/Mendoza'), ('America/Menominee', 'America/Menominee'), ('America/Merida', 'America/Merida'), ('America/Metlakatla', 'America/Metlakatla'), ('America/Mexico_City', 'America/Mexico_City'), ('America/Miquelon', 'America/Miquelon'), ('America/Moncton', 'America/Moncton'), ('America/Monterrey', 'America/Monterrey'), ('America/Montevideo', 'America/Montevideo'), ('America/Montserrat', 'America/Montserrat'), ('America/Nassau', 'America/Nassau'), ('America/New_York', 'America/New_York'), ('America/Nome', 'America/Nome'), ('America/Noronha', 'America/Noronha'), ('America/North_Dakota/Beulah', 'America/North_Dakota/Beulah'), ('America/North_Dakota/Center', 'America/North_Dakota/Center'), ('America/North_Dakota/New_Salem', 'America/North_Dakota/New_Salem'), ('America/Ojinaga', 'America/Ojinaga'), ('America/Panama', 'America/Panama'), ('America/Paramaribo', 'America/Paramaribo'), ('America/Phoenix', 'America/Phoenix'), ('America/Port-au-Prince', 'America/Port-au-Prince'), ('America/Port_of_Spain', 'America/Port_of_Spain'), ('America/Porto_Velho', 'America/Porto_Velho'), ('America/Puerto_Rico', 'America/Puerto_Rico'), ('America/Punta_Arenas', 'America/Punta_Arenas'), ('America/Rankin_Inlet', 'America/Rankin_Inlet'), ('America/Recife', 'America/Recife'), ('America/Regina', 'America/Regina'), ('America/Resolute', 'America/Resolute'), ('America/Rio_Branco', 'America/Rio_Branco'), ('America/Santarem', 'America/Santarem'), ('America/Santiago', 'America/Santiago'), ('America/Santo_Domingo', 'America/Santo_Domingo'), ('America/Sao_Paulo', 'America/Sao_Paulo'), ('America/Scoresbysund', 'America/Scoresbysund'), ('America/Sitka', 'America/Sitka'), ('America/St_Barthelemy', 'America/St_Barthelemy'), ('America/St_Johns', 'America/St_Johns'), ('America/St_Kitts', 'America/St_Kitts'), ('America/St_Lucia', 'America/St_Lucia'), ('America/St_Thomas', 'America/St_Thomas'), ('America/St_Vincent', 'America/St_Vincent'), ('America/Swift_Current', 'America/Swift_Current'), ('America/Tegucigalpa', 'America/Tegucigalpa'), ('America/Thule', 'America/Thule'), ('America/Tijuana', 'America/Tijuana'), ('America/Toronto', 'America/Toronto'), ('America/Tortola', 'America/Tortola'), ('America/Vancouver', 'America/Vancouver'), ('America/Whitehorse', 'America/Whitehorse'), ('America/Winnipeg', 'America/Winnipeg'), ('America/Yakutat', 'America/Yakutat'), ('Antarctica/Casey', 'Antarctica/Casey'), ('Antarctica/Davis', 'Antarctica/Davis'), ('Antarctica/DumontDUrville', 'Antarctica/DumontDUrville'), ('Antarctica/Macquarie', 'Antarctica/Macquarie'), ('Antarctica/Mawson', 'Antarctica/Mawson'), ('Antarctica/McMurdo', 'Antarctica/McMurdo'), ('Antarctica/Palmer', 'Antarctica/Palmer'), ('Antarctica/Rothera', 'Antarctica/Rothera'), ('Antarctica/Syowa', 'Antarctica/Syowa'), ('Antarctica/Troll', 'Antarctica/Troll'), ('Antarctica/Vostok', 'Antarctica/Vostok'), ('Arctic/Longyearbyen', 'Arctic/Longyearbyen'), ('Asia/Aden', 'Asia/Aden'), ('Asia/Almaty', 'Asia/Almaty'), ('Asia/Amman', 'Asia/Amman'), ('Asia/Anadyr', 'Asia/Anadyr'), ('Asia/Aqtau', 'Asia/Aqtau'), ('Asia/Aqtobe', 'Asia/Aqtobe'), ('Asia/Ashgabat', 'Asia/Ashgabat'), ('Asia/Atyrau', 'Asia/Atyrau'), ('Asia/Baghdad', 'Asia/Baghdad'), ('Asia/Bahrain', 'Asia/Bahrain'), ('Asia/Baku', 'Asia/Baku'), ('Asia/Bangkok', 'Asia/Bangkok'), ('Asia/Barnaul', 'Asia/Barnaul'), ('Asia/Beirut', 'Asia/Beirut'), ('Asia/Bishkek', 'Asia/Bishkek'), ('Asia/Brunei', 'Asia/Brunei'), ('Asia/Calcutta', 'Asia/Calcutta'), ('Asia/Chita', 'Asia/Chita'), ('Asia/Colombo', 'Asia/Colombo'), ('Asia/Damascus', 'Asia/Damascus'), ('Asia/Dhaka', 'Asia/Dhaka'), ('Asia/Dili', 'Asia/Dili'), ('Asia/Dubai', 'Asia/Dubai'), ('Asia/Dushanbe', 'Asia/Dushanbe'), ('Asia/Famagusta', 'Asia/Famagusta'), ('Asia/Gaza', 'Asia/Gaza'), ('Asia/Hebron', 'Asia/Hebron'), ('Asia/Hong_Kong', 'Asia/Hong_Kong'), ('Asia/Hovd', 'Asia/Hovd'), ('Asia/Irkutsk', 'Asia/Irkutsk'), ('Asia/Jakarta', 'Asia/Jakarta'), ('Asia/Jayapura', 'Asia/Jayapura'), ('Asia/Jerusalem', 'Asia/Jerusalem'), ('Asia/Kabul', 'Asia/Kabul'), ('Asia/Kamchatka', 'Asia/Kamchatka'), ('Asia/Karachi', 'Asia/Karachi'), ('Asia/Katmandu', 'Asia/Katmandu'), ('Asia/Khandyga', 'Asia/Khandyga'), ('Asia/Krasnoyarsk', 'Asia/Krasnoyarsk'), ('Asia/Kuala_Lumpur', 'Asia/Kuala_Lumpur'), ('Asia/Kuching', 'Asia/Kuching'), ('Asia/Kuwait', 'Asia/Kuwait'), ('Asia/Macau', 'Asia/Macau'), ('Asia/Magadan', 'Asia/Magadan'), ('Asia/Makassar', 'Asia/Makassar'), ('Asia/Manila', 'Asia/Manila'), ('Asia/Muscat', 'Asia/Muscat'), ('Asia/Nicosia', 'Asia/Nicosia'), ('Asia/Novokuznetsk', 'Asia/Novokuznetsk'), ('Asia/Novosibirsk', 'Asia/Novosibirsk'), ('Asia/Omsk', 'Asia/Omsk'), ('Asia/Oral', 'Asia/Oral'), ('Asia/Phnom_Penh', 'Asia/Phnom_Penh'), ('Asia/Pontianak', 'Asia/Pontianak'), ('Asia/Pyongyang', 'Asia/Pyongyang'), ('Asia/Qatar', 'Asia/Qatar'), ('Asia/Qostanay', 'Asia/Qostanay'), ('Asia/Qyzylorda', 'Asia/Qyzylorda'), ('Asia/Rangoon', 'Asia/Rangoon'), ('Asia/Riyadh', 'Asia/Riyadh'), ('Asia/Saigon', 'Asia/Saigon'), ('Asia/Sakhalin', 'Asia/Sakhalin'), ('Asia/Samarkand', 'Asia/Samarkand'), ('Asia/Seoul', 'Asia/Seoul'), ('Asia/Shanghai', 'Asia/Shanghai'), ('Asia/Singapore', 'Asia/Singapore'), ('Asia/Srednekolymsk', 'Asia/Srednekolymsk'), ('Asia/Taipei', 'Asia/Taipei'), ('Asia/Tashkent', 'Asia/Tashkent'), ('Asia/Tbilisi', 'Asia/Tbilisi'), ('Asia/Tehran', 'Asia/Tehran'), ('Asia/Thimphu', 'Asia/Thimphu'), ('Asia/Tokyo', 'Asia/Tokyo'), ('Asia/Tomsk', 'Asia/Tomsk'), ('Asia/Ulaanbaatar', 'Asia/Ulaanbaatar'), ('Asia/Urumqi', 'Asia/Urumqi'), ('Asia/Ust-Nera', 'Asia/Ust-Nera'), ('Asia/Vientiane', 'Asia/Vientiane'), ('Asia/Vladivostok', 'Asia/Vladivostok'), ('Asia/Yakutsk', 'Asia/Yakutsk'), ('Asia/Yekaterinburg', 'Asia/Yekaterinburg'), ('Asia/Yerevan', 'Asia/Yerevan'), ('Atlantic/Azores', 'Atlantic/Azores'), ('Atlantic/Bermuda', 'Atlantic/Bermuda'), ('Atlantic/Canary', 'Atlantic/Canary'), ('Atlantic/Cape_Verde', 'Atlantic/Cape_Verde'), ('Atlantic/Faeroe', 'Atlantic/Faeroe'), ('Atlantic/Madeira', 'Atlantic/Madeira'), ('Atlantic/Reykjavik', 'Atlantic/Reykjavik'), ('Atlantic/South_Georgia', 'Atlantic/South_Georgia'), ('Atlantic/St_Helena', 'Atlantic/St_Helena'), ('Atlantic/Stanley', 'Atlantic/Stanley'), ('Australia/Adelaide', 'Australia/Adelaide'), ('Australia/Brisbane', 'Australia/Brisbane'), ('Australia/Broken_Hill', 'Australia/Broken_Hill'), ('Australia/Darwin', 'Australia/Darwin'), ('Australia/Eucla', 'Australia/Eucla'), ('Australia/Hobart', 'Australia/Hobart'), ('Australia/Lindeman', 'Australia/Lindeman'), ('Australia/Lord_Howe', 'Australia/Lord_Howe'), ('Australia/Melbourne', 'Australia/Melbourne'), ('Australia/Perth', 'Australia/Perth'), ('Australia/Sydney', 'Australia/Sydney'), ('Europe/Amsterdam', 'Europe/Amsterdam'), ('Europe/Andorra', 'Europe/Andorra'), ('Europe/Astrakhan', 'Europe/Astrakhan'), ('Europe/Athens', 'Europe/Athens'), ('Europe/Belgrade', 'Europe/Belgrade'), ('Europe/Berlin', 'Europe/Berlin'), ('Europe/Bratislava', 'Europe/Bratislava'), ('Europe/Brussels', 'Europe/Brussels'), ('Europe/Bucharest', 'Europe/Bucharest'), ('Europe/Budapest', 'Europe/Budapest'), ('Europe/Busingen', 'Europe/Busingen'), ('Europe/Chisinau', 'Europe/Chisinau'), ('Europe/Copenhagen', 'Europe/Copenhagen'), ('Europe/Dublin', 'Europe/Dublin'), ('Europe/Gibraltar', 'Europe/Gibraltar'), ('Europe/Guernsey', 'Europe/Guernsey'), ('Europe/Helsinki', 'Europe/Helsinki'), ('Europe/Isle_of_Man', 'Europe/Isle_of_Man'), ('Europe/Istanbul', 'Europe/Istanbul'), ('Europe/Jersey', 'Europe/Jersey'), ('Europe/Kaliningrad', 'Europe/Kaliningrad'), ('Europe/Kiev', 'Europe/Kiev'), ('Europe/Kirov', 'Europe/Kirov'), ('Europe/Lisbon', 'Europe/Lisbon'), ('Europe/Ljubljana', 'Europe/Ljubljana'), ('Europe/London', 'Europe/London'), ('Europe/Luxembourg', 'Europe/Luxembourg'), ('Europe/Madrid', 'Europe/Madrid'), ('Europe/Malta', 'Europe/Malta'), ('Europe/Mariehamn', 'Europe/Mariehamn'), ('Europe/Minsk', 'Europe/Minsk'), ('Europe/Monaco', 'Europe/Monaco'), ('Europe/Moscow', 'Europe/Moscow'), ('Europe/Oslo', 'Europe/Oslo'), ('Europe/Paris', 'Europe/Paris'), ('Europe/Podgorica', 'Europe/Podgorica'), ('Europe/Prague', 'Europe/Prague'), ('Europe/Riga', 'Europe/Riga'), ('Europe/Rome', 'Europe/Rome'), ('Europe/Samara', 'Europe/Samara'), ('Europe/San_Marino', 'Europe/San_Marino'), ('Europe/Sarajevo', 'Europe/Sarajevo'), ('Europe/Saratov', 'Europe/Saratov'), ('Europe/Simferopol', 'Europe/Simferopol'), ('Europe/Skopje', 'Europe/Skopje'), ('Europe/Sofia', 'Europe/Sofia'), ('Europe/Stockholm', 'Europe/Stockholm'), ('Europe/Tallinn', 'Europe/Tallinn'), ('Europe/Tirane', 'Europe/Tirane'), ('Europe/Ulyanovsk', 'Europe/Ulyanovsk'), ('Europe/Vaduz', 'Europe/Vaduz'), ('Europe/Vatican', 'Europe/Vatican'), ('Europe/Vienna', 'Europe/Vienna'), ('Europe/Vilnius', 'Europe/Vilnius'), ('Europe/Volgograd', 'Europe/Volgograd'), ('Europe/Warsaw', 'Europe/Warsaw'), ('Europe/Zagreb', 'Europe/Zagreb'), ('Europe/Zurich', 'Europe/Zurich'), ('Indian/Antananarivo', 'Indian/Antananarivo'), ('Indian/Chagos', 'Indian/Chagos'), ('Indian/Christmas', 'Indian/Christmas'), ('Indian/Cocos', 'Indian/Cocos'), ('Indian/Comoro', 'Indian/Comoro'), ('Indian/Kerguelen', 'Indian/Kerguelen'), ('Indian/Mahe', 'Indian/Mahe'), ('Indian/Maldives', 'Indian/Maldives'), ('Indian/Mauritius', 'Indian/Mauritius'), ('Indian/Mayotte', 'Indian/Mayotte'), ('Indian/Reunion', 'Indian/Reunion'), ('Pacific/Apia', 'Pacific/Apia'), ('Pacific/Auckland', 'Pacific/Auckland'), ('Pacific/Bougainville', 'Pacific/Bougainville'), ('Pacific/Chatham', 'Pacific/Chatham'), ('Pacific/Easter', 'Pacific/Easter'), ('Pacific/Efate', 'Pacific/Efate'), ('Pacific/Enderbury', 'Pacific/Enderbury'), ('Pacific/Fakaofo', 'Pacific/Fakaofo'), ('Pacific/Fiji', 'Pacific/Fiji'), ('Pacific/Funafuti', 'Pacific/Funafuti'), ('Pacific/Galapagos', 'Pacific/Galapagos'), ('Pacific/Gambier', 'Pacific/Gambier'), ('Pacific/Guadalcanal', 'Pacific/Guadalcanal'), ('Pacific/Guam', 'Pacific/Guam'), ('Pacific/Honolulu', 'Pacific/Honolulu'), ('Pacific/Kiritimati', 'Pacific/Kiritimati'), ('Pacific/Kosrae', 'Pacific/Kosrae'), ('Pacific/Kwajalein', 'Pacific/Kwajalein'), ('Pacific/Majuro', 'Pacific/Majuro'), ('Pacific/Marquesas', 'Pacific/Marquesas'), ('Pacific/Midway', 'Pacific/Midway'), ('Pacific/Nauru', 'Pacific/Nauru'), ('Pacific/Niue', 'Pacific/Niue'), ('Pacific/Norfolk', 'Pacific/Norfolk'), ('Pacific/Noumea', 'Pacific/Noumea'), ('Pacific/Pago_Pago', 'Pacific/Pago_Pago'), ('Pacific/Palau', 'Pacific/Palau'), ('Pacific/Pitcairn', 'Pacific/Pitcairn'), ('Pacific/Ponape', 'Pacific/Ponape'), ('Pacific/Port_Moresby', 'Pacific/Port_Moresby'), ('Pacific/Rarotonga', 'Pacific/Rarotonga'), ('Pacific/Saipan', 'Pacific/Saipan'), ('Pacific/Tahiti', 'Pacific/Tahiti'), ('Pacific/Tarawa', 'Pacific/Tarawa'), ('Pacific/Tongatapu', 'Pacific/Tongatapu'), ('Pacific/Truk', 'Pacific/Truk'), ('Pacific/Wake', 'Pacific/Wake'), ('Pacific/Wallis', 'Pacific/Wallis')], max_length=50, null=True), + ), + ] diff --git a/backend/server/adventures/migrations/0029_adventure_city_adventure_country_adventure_region.py b/backend/server/adventures/migrations/0029_adventure_city_adventure_country_adventure_region.py new file mode 100644 index 0000000..3df78e0 --- /dev/null +++ b/backend/server/adventures/migrations/0029_adventure_city_adventure_country_adventure_region.py @@ -0,0 +1,30 @@ +# Generated by Django 5.0.11 on 2025-05-22 22:48 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('adventures', '0028_lodging_timezone'), + ('worldtravel', '0015_city_insert_id_country_insert_id_region_insert_id'), + ] + + operations = [ + migrations.AddField( + model_name='adventure', + name='city', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='worldtravel.city'), + ), + migrations.AddField( + model_name='adventure', + name='country', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='worldtravel.country'), + ), + migrations.AddField( + model_name='adventure', + name='region', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='worldtravel.region'), + ), + ] diff --git a/backend/server/adventures/models.py b/backend/server/adventures/models.py index 6f5b3b7..29ac2ce 100644 --- a/backend/server/adventures/models.py +++ b/backend/server/adventures/models.py @@ -9,6 +9,9 @@ from django.contrib.auth import get_user_model from django.contrib.postgres.fields import ArrayField from django.forms import ValidationError from django_resized import ResizedImageField +from worldtravel.models import City, Country, Region, VisitedCity, VisitedRegion +from adventures.geocoding import reverse_geocode +from django.utils import timezone def validate_file_extension(value): import os @@ -43,6 +46,426 @@ ADVENTURE_TYPES = [ ('other', 'Other') ] +TIMEZONES = [ + "Africa/Abidjan", + "Africa/Accra", + "Africa/Addis_Ababa", + "Africa/Algiers", + "Africa/Asmera", + "Africa/Bamako", + "Africa/Bangui", + "Africa/Banjul", + "Africa/Bissau", + "Africa/Blantyre", + "Africa/Brazzaville", + "Africa/Bujumbura", + "Africa/Cairo", + "Africa/Casablanca", + "Africa/Ceuta", + "Africa/Conakry", + "Africa/Dakar", + "Africa/Dar_es_Salaam", + "Africa/Djibouti", + "Africa/Douala", + "Africa/El_Aaiun", + "Africa/Freetown", + "Africa/Gaborone", + "Africa/Harare", + "Africa/Johannesburg", + "Africa/Juba", + "Africa/Kampala", + "Africa/Khartoum", + "Africa/Kigali", + "Africa/Kinshasa", + "Africa/Lagos", + "Africa/Libreville", + "Africa/Lome", + "Africa/Luanda", + "Africa/Lubumbashi", + "Africa/Lusaka", + "Africa/Malabo", + "Africa/Maputo", + "Africa/Maseru", + "Africa/Mbabane", + "Africa/Mogadishu", + "Africa/Monrovia", + "Africa/Nairobi", + "Africa/Ndjamena", + "Africa/Niamey", + "Africa/Nouakchott", + "Africa/Ouagadougou", + "Africa/Porto-Novo", + "Africa/Sao_Tome", + "Africa/Tripoli", + "Africa/Tunis", + "Africa/Windhoek", + "America/Adak", + "America/Anchorage", + "America/Anguilla", + "America/Antigua", + "America/Araguaina", + "America/Argentina/La_Rioja", + "America/Argentina/Rio_Gallegos", + "America/Argentina/Salta", + "America/Argentina/San_Juan", + "America/Argentina/San_Luis", + "America/Argentina/Tucuman", + "America/Argentina/Ushuaia", + "America/Aruba", + "America/Asuncion", + "America/Bahia", + "America/Bahia_Banderas", + "America/Barbados", + "America/Belem", + "America/Belize", + "America/Blanc-Sablon", + "America/Boa_Vista", + "America/Bogota", + "America/Boise", + "America/Buenos_Aires", + "America/Cambridge_Bay", + "America/Campo_Grande", + "America/Cancun", + "America/Caracas", + "America/Catamarca", + "America/Cayenne", + "America/Cayman", + "America/Chicago", + "America/Chihuahua", + "America/Ciudad_Juarez", + "America/Coral_Harbour", + "America/Cordoba", + "America/Costa_Rica", + "America/Creston", + "America/Cuiaba", + "America/Curacao", + "America/Danmarkshavn", + "America/Dawson", + "America/Dawson_Creek", + "America/Denver", + "America/Detroit", + "America/Dominica", + "America/Edmonton", + "America/Eirunepe", + "America/El_Salvador", + "America/Fort_Nelson", + "America/Fortaleza", + "America/Glace_Bay", + "America/Godthab", + "America/Goose_Bay", + "America/Grand_Turk", + "America/Grenada", + "America/Guadeloupe", + "America/Guatemala", + "America/Guayaquil", + "America/Guyana", + "America/Halifax", + "America/Havana", + "America/Hermosillo", + "America/Indiana/Knox", + "America/Indiana/Marengo", + "America/Indiana/Petersburg", + "America/Indiana/Tell_City", + "America/Indiana/Vevay", + "America/Indiana/Vincennes", + "America/Indiana/Winamac", + "America/Indianapolis", + "America/Inuvik", + "America/Iqaluit", + "America/Jamaica", + "America/Jujuy", + "America/Juneau", + "America/Kentucky/Monticello", + "America/Kralendijk", + "America/La_Paz", + "America/Lima", + "America/Los_Angeles", + "America/Louisville", + "America/Lower_Princes", + "America/Maceio", + "America/Managua", + "America/Manaus", + "America/Marigot", + "America/Martinique", + "America/Matamoros", + "America/Mazatlan", + "America/Mendoza", + "America/Menominee", + "America/Merida", + "America/Metlakatla", + "America/Mexico_City", + "America/Miquelon", + "America/Moncton", + "America/Monterrey", + "America/Montevideo", + "America/Montserrat", + "America/Nassau", + "America/New_York", + "America/Nome", + "America/Noronha", + "America/North_Dakota/Beulah", + "America/North_Dakota/Center", + "America/North_Dakota/New_Salem", + "America/Ojinaga", + "America/Panama", + "America/Paramaribo", + "America/Phoenix", + "America/Port-au-Prince", + "America/Port_of_Spain", + "America/Porto_Velho", + "America/Puerto_Rico", + "America/Punta_Arenas", + "America/Rankin_Inlet", + "America/Recife", + "America/Regina", + "America/Resolute", + "America/Rio_Branco", + "America/Santarem", + "America/Santiago", + "America/Santo_Domingo", + "America/Sao_Paulo", + "America/Scoresbysund", + "America/Sitka", + "America/St_Barthelemy", + "America/St_Johns", + "America/St_Kitts", + "America/St_Lucia", + "America/St_Thomas", + "America/St_Vincent", + "America/Swift_Current", + "America/Tegucigalpa", + "America/Thule", + "America/Tijuana", + "America/Toronto", + "America/Tortola", + "America/Vancouver", + "America/Whitehorse", + "America/Winnipeg", + "America/Yakutat", + "Antarctica/Casey", + "Antarctica/Davis", + "Antarctica/DumontDUrville", + "Antarctica/Macquarie", + "Antarctica/Mawson", + "Antarctica/McMurdo", + "Antarctica/Palmer", + "Antarctica/Rothera", + "Antarctica/Syowa", + "Antarctica/Troll", + "Antarctica/Vostok", + "Arctic/Longyearbyen", + "Asia/Aden", + "Asia/Almaty", + "Asia/Amman", + "Asia/Anadyr", + "Asia/Aqtau", + "Asia/Aqtobe", + "Asia/Ashgabat", + "Asia/Atyrau", + "Asia/Baghdad", + "Asia/Bahrain", + "Asia/Baku", + "Asia/Bangkok", + "Asia/Barnaul", + "Asia/Beirut", + "Asia/Bishkek", + "Asia/Brunei", + "Asia/Calcutta", + "Asia/Chita", + "Asia/Colombo", + "Asia/Damascus", + "Asia/Dhaka", + "Asia/Dili", + "Asia/Dubai", + "Asia/Dushanbe", + "Asia/Famagusta", + "Asia/Gaza", + "Asia/Hebron", + "Asia/Hong_Kong", + "Asia/Hovd", + "Asia/Irkutsk", + "Asia/Jakarta", + "Asia/Jayapura", + "Asia/Jerusalem", + "Asia/Kabul", + "Asia/Kamchatka", + "Asia/Karachi", + "Asia/Katmandu", + "Asia/Khandyga", + "Asia/Krasnoyarsk", + "Asia/Kuala_Lumpur", + "Asia/Kuching", + "Asia/Kuwait", + "Asia/Macau", + "Asia/Magadan", + "Asia/Makassar", + "Asia/Manila", + "Asia/Muscat", + "Asia/Nicosia", + "Asia/Novokuznetsk", + "Asia/Novosibirsk", + "Asia/Omsk", + "Asia/Oral", + "Asia/Phnom_Penh", + "Asia/Pontianak", + "Asia/Pyongyang", + "Asia/Qatar", + "Asia/Qostanay", + "Asia/Qyzylorda", + "Asia/Rangoon", + "Asia/Riyadh", + "Asia/Saigon", + "Asia/Sakhalin", + "Asia/Samarkand", + "Asia/Seoul", + "Asia/Shanghai", + "Asia/Singapore", + "Asia/Srednekolymsk", + "Asia/Taipei", + "Asia/Tashkent", + "Asia/Tbilisi", + "Asia/Tehran", + "Asia/Thimphu", + "Asia/Tokyo", + "Asia/Tomsk", + "Asia/Ulaanbaatar", + "Asia/Urumqi", + "Asia/Ust-Nera", + "Asia/Vientiane", + "Asia/Vladivostok", + "Asia/Yakutsk", + "Asia/Yekaterinburg", + "Asia/Yerevan", + "Atlantic/Azores", + "Atlantic/Bermuda", + "Atlantic/Canary", + "Atlantic/Cape_Verde", + "Atlantic/Faeroe", + "Atlantic/Madeira", + "Atlantic/Reykjavik", + "Atlantic/South_Georgia", + "Atlantic/St_Helena", + "Atlantic/Stanley", + "Australia/Adelaide", + "Australia/Brisbane", + "Australia/Broken_Hill", + "Australia/Darwin", + "Australia/Eucla", + "Australia/Hobart", + "Australia/Lindeman", + "Australia/Lord_Howe", + "Australia/Melbourne", + "Australia/Perth", + "Australia/Sydney", + "Europe/Amsterdam", + "Europe/Andorra", + "Europe/Astrakhan", + "Europe/Athens", + "Europe/Belgrade", + "Europe/Berlin", + "Europe/Bratislava", + "Europe/Brussels", + "Europe/Bucharest", + "Europe/Budapest", + "Europe/Busingen", + "Europe/Chisinau", + "Europe/Copenhagen", + "Europe/Dublin", + "Europe/Gibraltar", + "Europe/Guernsey", + "Europe/Helsinki", + "Europe/Isle_of_Man", + "Europe/Istanbul", + "Europe/Jersey", + "Europe/Kaliningrad", + "Europe/Kiev", + "Europe/Kirov", + "Europe/Lisbon", + "Europe/Ljubljana", + "Europe/London", + "Europe/Luxembourg", + "Europe/Madrid", + "Europe/Malta", + "Europe/Mariehamn", + "Europe/Minsk", + "Europe/Monaco", + "Europe/Moscow", + "Europe/Oslo", + "Europe/Paris", + "Europe/Podgorica", + "Europe/Prague", + "Europe/Riga", + "Europe/Rome", + "Europe/Samara", + "Europe/San_Marino", + "Europe/Sarajevo", + "Europe/Saratov", + "Europe/Simferopol", + "Europe/Skopje", + "Europe/Sofia", + "Europe/Stockholm", + "Europe/Tallinn", + "Europe/Tirane", + "Europe/Ulyanovsk", + "Europe/Vaduz", + "Europe/Vatican", + "Europe/Vienna", + "Europe/Vilnius", + "Europe/Volgograd", + "Europe/Warsaw", + "Europe/Zagreb", + "Europe/Zurich", + "Indian/Antananarivo", + "Indian/Chagos", + "Indian/Christmas", + "Indian/Cocos", + "Indian/Comoro", + "Indian/Kerguelen", + "Indian/Mahe", + "Indian/Maldives", + "Indian/Mauritius", + "Indian/Mayotte", + "Indian/Reunion", + "Pacific/Apia", + "Pacific/Auckland", + "Pacific/Bougainville", + "Pacific/Chatham", + "Pacific/Easter", + "Pacific/Efate", + "Pacific/Enderbury", + "Pacific/Fakaofo", + "Pacific/Fiji", + "Pacific/Funafuti", + "Pacific/Galapagos", + "Pacific/Gambier", + "Pacific/Guadalcanal", + "Pacific/Guam", + "Pacific/Honolulu", + "Pacific/Kiritimati", + "Pacific/Kosrae", + "Pacific/Kwajalein", + "Pacific/Majuro", + "Pacific/Marquesas", + "Pacific/Midway", + "Pacific/Nauru", + "Pacific/Niue", + "Pacific/Norfolk", + "Pacific/Noumea", + "Pacific/Pago_Pago", + "Pacific/Palau", + "Pacific/Pitcairn", + "Pacific/Ponape", + "Pacific/Port_Moresby", + "Pacific/Rarotonga", + "Pacific/Saipan", + "Pacific/Tahiti", + "Pacific/Tarawa", + "Pacific/Tongatapu", + "Pacific/Truk", + "Pacific/Wake", + "Pacific/Wallis" +] + LODGING_TYPES = [ ('hotel', 'Hotel'), ('hostel', 'Hostel'), @@ -78,6 +501,7 @@ class Visit(models.Model): adventure = models.ForeignKey('Adventure', on_delete=models.CASCADE, related_name='visits') start_date = models.DateTimeField(null=True, blank=True) end_date = models.DateTimeField(null=True, blank=True) + timezone = models.CharField(max_length=50, choices=[(tz, tz) for tz in TIMEZONES], null=True, blank=True) notes = models.TextField(blank=True, null=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) @@ -104,9 +528,16 @@ class Adventure(models.Model): rating = models.FloatField(blank=True, null=True) link = models.URLField(blank=True, null=True, max_length=2083) is_public = models.BooleanField(default=False) + longitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True) latitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True) + + city = models.ForeignKey(City, on_delete=models.SET_NULL, blank=True, null=True) + region = models.ForeignKey(Region, on_delete=models.SET_NULL, blank=True, null=True) + country = models.ForeignKey(Country, on_delete=models.SET_NULL, blank=True, null=True) + collection = models.ForeignKey('Collection', on_delete=models.CASCADE, blank=True, null=True) + created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) @@ -119,6 +550,17 @@ class Adventure(models.Model): # end_date = models.DateField(blank=True, null=True) # type = models.CharField(max_length=100, choices=ADVENTURE_TYPES, default='general') + def is_visited_status(self): + current_date = timezone.now().date() + for visit in self.visits.all(): + start_date = visit.start_date.date() if isinstance(visit.start_date, timezone.datetime) else visit.start_date + end_date = visit.end_date.date() if isinstance(visit.end_date, timezone.datetime) else visit.end_date + if start_date and end_date and (start_date <= current_date): + return True + elif start_date and not end_date and (start_date <= current_date): + return True + return False + def clean(self): if self.collection: if self.collection.is_public and not self.is_public: @@ -146,6 +588,32 @@ class Adventure(models.Model): } ) self.category = category + + if self.latitude and self.longitude: + is_visited = self.is_visited_status() + reverse_geocode_result = reverse_geocode(self.latitude, self.longitude, self.user_id) + if 'region_id' in reverse_geocode_result: + region = Region.objects.filter(id=reverse_geocode_result['region_id']).first() + if region: + self.region = region + if is_visited: + visited_region, created = VisitedRegion.objects.get_or_create( + user_id=self.user_id, + region=region + ) + if 'city_id' in reverse_geocode_result: + city = City.objects.filter(id=reverse_geocode_result['city_id']).first() + if city: + self.city = city + if is_visited: + visited_city, created = VisitedCity.objects.get_or_create( + user_id=self.user_id, + city=city + ) + if 'country_id' in reverse_geocode_result: + country = Country.objects.filter(country_code=reverse_geocode_result['country_id']).first() + if country: + self.country = country return super().save(force_insert, force_update, using, update_fields) @@ -191,6 +659,8 @@ class Transportation(models.Model): link = models.URLField(blank=True, null=True) date = models.DateTimeField(blank=True, null=True) end_date = models.DateTimeField(blank=True, null=True) + start_timezone = models.CharField(max_length=50, choices=[(tz, tz) for tz in TIMEZONES], null=True, blank=True) + end_timezone = models.CharField(max_length=50, choices=[(tz, tz) for tz in TIMEZONES], null=True, blank=True) flight_number = models.CharField(max_length=100, blank=True, null=True) from_location = models.CharField(max_length=200, blank=True, null=True) origin_latitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True) @@ -352,6 +822,7 @@ class Lodging(models.Model): link = models.URLField(blank=True, null=True, max_length=2083) check_in = models.DateTimeField(blank=True, null=True) check_out = models.DateTimeField(blank=True, null=True) + timezone = models.CharField(max_length=50, choices=[(tz, tz) for tz in TIMEZONES], null=True, blank=True) reservation_number = models.CharField(max_length=100, blank=True, null=True) price = models.DecimalField(max_digits=9, decimal_places=2, blank=True, null=True) latitude = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True) @@ -363,8 +834,8 @@ class Lodging(models.Model): updated_at = models.DateTimeField(auto_now=True) def clean(self): - if self.date and self.end_date and self.date > self.end_date: - raise ValidationError('The start date must be before the end date. Start date: ' + str(self.date) + ' End date: ' + str(self.end_date)) + if self.check_in and self.check_out and self.check_in > self.check_out: + raise ValidationError('The start date must be before the end date. Start date: ' + str(self.check_in) + ' End date: ' + str(self.check_out)) if self.collection: if self.collection.is_public and not self.is_public: diff --git a/backend/server/adventures/serializers.py b/backend/server/adventures/serializers.py index d69466d..0fe059e 100644 --- a/backend/server/adventures/serializers.py +++ b/backend/server/adventures/serializers.py @@ -72,7 +72,7 @@ class VisitSerializer(serializers.ModelSerializer): class Meta: model = Visit - fields = ['id', 'start_date', 'end_date', 'notes'] + fields = ['id', 'start_date', 'end_date', 'timezone', 'notes'] read_only_fields = ['id'] class AdventureSerializer(CustomModelSerializer): @@ -82,13 +82,14 @@ class AdventureSerializer(CustomModelSerializer): category = CategorySerializer(read_only=False, required=False) is_visited = serializers.SerializerMethodField() user = serializers.SerializerMethodField() + country = serializers.SerializerMethodField() class Meta: model = Adventure fields = [ 'id', 'user_id', 'name', 'description', 'rating', 'activity_types', 'location', 'is_public', 'collection', 'created_at', 'updated_at', 'images', 'link', 'longitude', - 'latitude', 'visits', 'is_visited', 'category', 'attachments', 'user' + 'latitude', 'visits', 'is_visited', 'category', 'attachments', 'user', 'city', 'country', 'region' ] read_only_fields = ['id', 'created_at', 'updated_at', 'user_id', 'is_visited', 'user'] @@ -103,6 +104,9 @@ class AdventureSerializer(CustomModelSerializer): return existing_category category_data['name'] = name return category_data + + def get_country(self, obj): + return obj.country.country_code if obj.country else None def get_or_create_category(self, category_data): user = self.context['request'].user @@ -134,15 +138,7 @@ class AdventureSerializer(CustomModelSerializer): return CustomUserDetailsSerializer(user).data def get_is_visited(self, obj): - current_date = timezone.now().date() - for visit in obj.visits.all(): - start_date = visit.start_date.date() if isinstance(visit.start_date, timezone.datetime) else visit.start_date - end_date = visit.end_date.date() if isinstance(visit.end_date, timezone.datetime) else visit.end_date - if start_date and end_date and (start_date <= current_date): - return True - elif start_date and not end_date and (start_date <= current_date): - return True - return False + return obj.is_visited_status() def create(self, validated_data): visits_data = validated_data.pop('visits', None) @@ -155,7 +151,8 @@ class AdventureSerializer(CustomModelSerializer): if category_data: category = self.get_or_create_category(category_data) adventure.category = category - adventure.save() + + adventure.save() return adventure @@ -192,6 +189,9 @@ class AdventureSerializer(CustomModelSerializer): visits_to_delete = current_visit_ids - updated_visit_ids instance.visits.filter(id__in=visits_to_delete).delete() + # call save on the adventure to update the updated_at field and trigger any geocoding + instance.save() + return instance class TransportationSerializer(CustomModelSerializer): @@ -201,7 +201,7 @@ class TransportationSerializer(CustomModelSerializer): fields = [ 'id', 'user_id', 'type', 'name', 'description', 'rating', 'link', 'date', 'flight_number', 'from_location', 'to_location', - 'is_public', 'collection', 'created_at', 'updated_at', 'end_date', 'origin_latitude', 'origin_longitude', 'destination_latitude', 'destination_longitude' + 'is_public', 'collection', 'created_at', 'updated_at', 'end_date', 'origin_latitude', 'origin_longitude', 'destination_latitude', 'destination_longitude', 'start_timezone', 'end_timezone' ] read_only_fields = ['id', 'created_at', 'updated_at', 'user_id'] @@ -212,7 +212,7 @@ class LodgingSerializer(CustomModelSerializer): fields = [ 'id', 'user_id', 'name', 'description', 'rating', 'link', 'check_in', 'check_out', 'reservation_number', 'price', 'latitude', 'longitude', 'location', 'is_public', - 'collection', 'created_at', 'updated_at', 'type' + 'collection', 'created_at', 'updated_at', 'type', 'timezone' ] read_only_fields = ['id', 'created_at', 'updated_at', 'user_id'] diff --git a/backend/server/adventures/views/adventure_view.py b/backend/server/adventures/views/adventure_view.py index 55beac3..a936f86 100644 --- a/backend/server/adventures/views/adventure_view.py +++ b/backend/server/adventures/views/adventure_view.py @@ -60,14 +60,15 @@ class AdventureViewSet(viewsets.ModelViewSet): """ user = self.request.user + # Actions that allow public access (include 'retrieve' and your custom action) + public_allowed_actions = {'retrieve', 'additional_info'} + if not user.is_authenticated: - # Unauthenticated users can only access public adventures for retrieval - if self.action == 'retrieve': + if self.action in public_allowed_actions: return Adventure.objects.retrieve_adventures(user, include_public=True).order_by('-updated_at') return Adventure.objects.none() - # Authenticated users: Handle retrieval separately - include_public = self.action == 'retrieve' + include_public = self.action in public_allowed_actions return Adventure.objects.retrieve_adventures( user, include_public=include_public, @@ -75,6 +76,7 @@ class AdventureViewSet(viewsets.ModelViewSet): include_shared=True ).order_by('-updated_at') + def perform_update(self, serializer): adventure = serializer.save() if adventure.collection: @@ -175,11 +177,15 @@ class AdventureViewSet(viewsets.ModelViewSet): def additional_info(self, request, pk=None): adventure = self.get_object() - # 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) + user = request.user + + # Allow if public + if not adventure.is_public: + # Only allow owner or shared collection members + if not user.is_authenticated or adventure.user_id != user: + if not (adventure.collection and adventure.collection.shared_with.filter(uuid=user.uuid).exists()): + return Response({"error": "User does not have permission to access this adventure"}, + status=status.HTTP_403_FORBIDDEN) serializer = self.get_serializer(adventure) response_data = serializer.data @@ -202,7 +208,6 @@ class AdventureViewSet(viewsets.ModelViewSet): "sunrise": results.get('sunrise'), "sunset": results.get('sunset') }) - response_data['sun_times'] = sun_times return Response(response_data) \ No newline at end of file diff --git a/backend/server/adventures/views/reverse_geocode_view.py b/backend/server/adventures/views/reverse_geocode_view.py index 4dc1d6b..e7c4562 100644 --- a/backend/server/adventures/views/reverse_geocode_view.py +++ b/backend/server/adventures/views/reverse_geocode_view.py @@ -6,77 +6,26 @@ from worldtravel.models import Region, City, VisitedRegion, VisitedCity from adventures.models import Adventure from adventures.serializers import AdventureSerializer import requests +from adventures.geocoding import reverse_geocode class ReverseGeocodeViewSet(viewsets.ViewSet): permission_classes = [IsAuthenticated] - def extractIsoCode(self, data): - """ - Extract the ISO code from the response data. - Returns a dictionary containing the region name, country name, and ISO code if found. - """ - iso_code = None - town_city_or_county = None - display_name = None - country_code = None - city = None - visited_city = None - location_name = None - - # town = None - # city = None - # county = None - - if 'name' in data.keys(): - location_name = data['name'] - - if 'address' in data.keys(): - keys = data['address'].keys() - for key in keys: - if key.find("ISO") != -1: - iso_code = data['address'][key] - if 'town' in keys: - town_city_or_county = data['address']['town'] - if 'county' in keys: - town_city_or_county = data['address']['county'] - if 'city' in keys: - town_city_or_county = data['address']['city'] - if not iso_code: - return {"error": "No region found"} - - region = Region.objects.filter(id=iso_code).first() - visited_region = VisitedRegion.objects.filter(region=region, user_id=self.request.user).first() - - region_visited = False - city_visited = False - country_code = iso_code[:2] - - if region: - if town_city_or_county: - display_name = f"{town_city_or_county}, {region.name}, {country_code}" - city = City.objects.filter(name__contains=town_city_or_county, region=region).first() - visited_city = VisitedCity.objects.filter(city=city, user_id=self.request.user).first() - - if visited_region: - region_visited = True - if visited_city: - city_visited = True - if region: - return {"region_id": iso_code, "region": region.name, "country": region.country.name, "region_visited": region_visited, "display_name": display_name, "city": city.name if city else None, "city_id": city.id if city else None, "city_visited": city_visited, 'location_name': location_name} - return {"error": "No region found"} - @action(detail=False, methods=['get']) def reverse_geocode(self, request): lat = request.query_params.get('lat', '') lon = request.query_params.get('lon', '') - url = f"https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat={lat}&lon={lon}" - headers = {'User-Agent': 'AdventureLog Server'} - response = requests.get(url, headers=headers) + if not lat or not lon: + return Response({"error": "Latitude and longitude are required"}, status=400) try: - data = response.json() - except requests.exceptions.JSONDecodeError: - return Response({"error": "Invalid response from geocoding service"}, status=400) - return Response(self.extractIsoCode(data)) + lat = float(lat) + lon = float(lon) + except ValueError: + return Response({"error": "Invalid latitude or longitude"}, status=400) + data = reverse_geocode(lat, lon, self.request.user) + if 'error' in data: + return Response(data, status=400) + return Response(data) @action(detail=False, methods=['post']) def mark_visited_region(self, request): diff --git a/backend/server/main/settings.py b/backend/server/main/settings.py index c9af870..bf51406 100644 --- a/backend/server/main/settings.py +++ b/backend/server/main/settings.py @@ -71,6 +71,7 @@ MIDDLEWARE = ( 'whitenoise.middleware.WhiteNoiseMiddleware', 'adventures.middleware.XSessionTokenMiddleware', 'adventures.middleware.DisableCSRFForSessionTokenMiddleware', + 'adventures.middleware.DisableCSRFForMobileLoginSignup', 'corsheaders.middleware.CorsMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', @@ -312,4 +313,4 @@ LOGGING = { # ADVENTURELOG_CDN_URL = getenv('ADVENTURELOG_CDN_URL', 'https://cdn.adventurelog.app') # https://github.com/dr5hn/countries-states-cities-database/tags -COUNTRY_REGION_JSON_VERSION = 'v2.5' +COUNTRY_REGION_JSON_VERSION = 'v2.6' diff --git a/backend/server/requirements.txt b/backend/server/requirements.txt index dcd0125..c43f171 100644 --- a/backend/server/requirements.txt +++ b/backend/server/requirements.txt @@ -1,4 +1,4 @@ -Django==5.0.11 +Django==5.2.1 djangorestframework>=3.15.2 django-allauth==0.63.3 drf-yasg==1.21.4 diff --git a/backend/server/users/views.py b/backend/server/users/views.py index 7c763f5..d5a5a41 100644 --- a/backend/server/users/views.py +++ b/backend/server/users/views.py @@ -165,7 +165,7 @@ class EnabledSocialProvidersView(APIView): providers = [] for provider in social_providers: if provider.provider == 'openid_connect': - new_provider = f'oidc/{provider.client_id}' + new_provider = f'oidc/{provider.provider_id}' else: new_provider = provider.provider providers.append({ @@ -204,4 +204,4 @@ class DisablePasswordAuthenticationView(APIView): user.disable_password = False user.save() return Response({"detail": "Password authentication enabled."}, status=status.HTTP_200_OK) - \ No newline at end of file + diff --git a/backend/server/worldtravel/management/commands/bulk-adventure-geocode.py b/backend/server/worldtravel/management/commands/bulk-adventure-geocode.py new file mode 100644 index 0000000..1a5d7a5 --- /dev/null +++ b/backend/server/worldtravel/management/commands/bulk-adventure-geocode.py @@ -0,0 +1,26 @@ +from django.core.management.base import BaseCommand +from adventures.models import Adventure +import time + +class Command(BaseCommand): + help = 'Bulk geocode all adventures by triggering save on each one' + + def handle(self, *args, **options): + adventures = Adventure.objects.all() + total = adventures.count() + + self.stdout.write(self.style.SUCCESS(f'Starting bulk geocoding of {total} adventures')) + + for i, adventure in enumerate(adventures): + try: + self.stdout.write(f'Processing adventure {i+1}/{total}: {adventure}') + adventure.save() # This should trigger any geocoding in the save method + self.stdout.write(self.style.SUCCESS(f'Successfully processed adventure {i+1}/{total}')) + except Exception as e: + self.stdout.write(self.style.ERROR(f'Error processing adventure {i+1}/{total}: {adventure} - {e}')) + + # Sleep for 2 seconds between each save + if i < total - 1: # Don't sleep after the last one + time.sleep(2) + + self.stdout.write(self.style.SUCCESS('Finished processing all adventures')) diff --git a/documentation/.vitepress/config.mts b/documentation/.vitepress/config.mts index 6e83e9a..3d52727 100644 --- a/documentation/.vitepress/config.mts +++ b/documentation/.vitepress/config.mts @@ -15,6 +15,14 @@ export default defineConfig({ "data-website-id": "a7552764-5a1d-4fe7-80c2-5331e1a53cb6", }, ], + + [ + "link", + { + rel: "me", + href: "https://mastodon.social/@adventurelog", + }, + ], ], ignoreDeadLinks: "localhostLinks", title: "AdventureLog", @@ -25,6 +33,66 @@ export default defineConfig({ hostname: "https://adventurelog.app", }, + transformPageData(pageData) { + if (pageData.relativePath === "index.md") { + const jsonLd = { + "@context": "https://schema.org", + "@type": "SoftwareApplication", + name: "AdventureLog", + url: "https://adventurelog.app", + applicationCategory: "TravelApplication", + operatingSystem: "Web, Docker, Linux", + description: + "AdventureLog is a self-hosted platform for tracking and planning travel experiences. Built for modern explorers, it offers trip planning, journaling, tracking and location mapping in one privacy-respecting package.", + creator: { + "@type": "Person", + name: "Sean Morley", + url: "https://seanmorley.com", + }, + offers: { + "@type": "Offer", + price: "0.00", + priceCurrency: "USD", + description: "Open-source version available for self-hosting.", + }, + softwareVersion: "v0.9.0", + license: + "https://github.com/seanmorley15/adventurelog/blob/main/LICENSE", + screenshot: "https://raw.githubusercontent.com/seanmorley15/AdventureLog/refs/heads/main/brand/screenshots/adventures.png", + downloadUrl: "https://github.com/seanmorley15/adventurelog", + sameAs: ["https://github.com/seanmorley15/adventurelog"], + keywords: [ + "self-hosted travel log", + "open source trip planner", + "travel journaling app", + "docker travel diary", + "map-based travel tracker", + "privacy-focused travel app", + "adventure log software", + "travel experience tracker", + "self-hosted travel app", + "open source travel software", + "trip planning tool", + "travel itinerary manager", + "location-based travel app", + "travel experience sharing", + "travel log application", + ], + }; + + return { + frontmatter: { + ...pageData.frontmatter, + head: [ + ["script", { type: "application/ld+json" }, JSON.stringify(jsonLd)], + ], + }, + }; + } + + return {}; + }, + themeConfig: { // https://vitepress.dev/reference/default-theme-config nav: [ @@ -199,6 +267,7 @@ export default defineConfig({ { icon: "buymeacoffee", link: "https://buymeacoffee.com/seanmorley15" }, { icon: "x", link: "https://x.com/AdventureLogApp" }, { icon: "mastodon", link: "https://mastodon.social/@adventurelog" }, + { icon: "instagram", link: "https://www.instagram.com/adventurelogapp" }, ], }, }); diff --git a/documentation/pnpm-lock.yaml b/documentation/pnpm-lock.yaml index c5fa66d..c22e463 100644 --- a/documentation/pnpm-lock.yaml +++ b/documentation/pnpm-lock.yaml @@ -709,8 +709,8 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - vite@5.4.14: - resolution: {integrity: sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==} + vite@5.4.19: + resolution: {integrity: sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -1105,9 +1105,9 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@vitejs/plugin-vue@5.2.0(vite@5.4.14)(vue@3.5.13)': + '@vitejs/plugin-vue@5.2.0(vite@5.4.19)(vue@3.5.13)': dependencies: - vite: 5.4.14 + vite: 5.4.19 vue: 3.5.13 '@vue/compiler-core@3.5.13': @@ -1475,7 +1475,7 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.2 - vite@5.4.14: + vite@5.4.19: dependencies: esbuild: 0.21.5 postcss: 8.4.49 @@ -1492,7 +1492,7 @@ snapshots: '@shikijs/transformers': 1.23.1 '@shikijs/types': 1.23.1 '@types/markdown-it': 14.1.2 - '@vitejs/plugin-vue': 5.2.0(vite@5.4.14)(vue@3.5.13) + '@vitejs/plugin-vue': 5.2.0(vite@5.4.19)(vue@3.5.13) '@vue/devtools-api': 7.6.4 '@vue/shared': 3.5.13 '@vueuse/core': 11.3.0(vue@3.5.13) @@ -1501,7 +1501,7 @@ snapshots: mark.js: 8.11.1 minisearch: 7.1.1 shiki: 1.23.1 - vite: 5.4.14 + vite: 5.4.19 vue: 3.5.13 optionalDependencies: postcss: 8.4.49 diff --git a/frontend/package.json b/frontend/package.json index 96dd929..c5bf4e4 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", @@ -34,7 +34,7 @@ "tslib": "^2.6.3", "typescript": "^5.5.2", "unplugin-icons": "^0.19.0", - "vite": "^5.4.12" + "vite": "^5.4.19" }, "type": "module", "dependencies": { diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index f084d2a..22170d5 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -56,16 +56,16 @@ importers: version: 1.2.3 '@sveltejs/adapter-node': specifier: ^5.2.0 - version: 5.2.12(@sveltejs/kit@2.20.7(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.18(@types/node@22.15.2)))(svelte@4.2.19)(vite@5.4.18(@types/node@22.15.2))) + version: 5.2.12(@sveltejs/kit@2.20.7(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2)))(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2))) '@sveltejs/adapter-vercel': specifier: ^5.4.1 - version: 5.7.0(@sveltejs/kit@2.20.7(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.18(@types/node@22.15.2)))(svelte@4.2.19)(vite@5.4.18(@types/node@22.15.2)))(rollup@4.40.0) + version: 5.7.0(@sveltejs/kit@2.20.7(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2)))(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2)))(rollup@4.40.2) '@sveltejs/kit': specifier: ^2.8.3 - version: 2.20.7(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.18(@types/node@22.15.2)))(svelte@4.2.19)(vite@5.4.18(@types/node@22.15.2)) + version: 2.20.7(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2)))(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2)) '@sveltejs/vite-plugin-svelte': specifier: ^3.1.1 - version: 3.1.2(svelte@4.2.19)(vite@5.4.18(@types/node@22.15.2)) + version: 3.1.2(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2)) '@tailwindcss/typography': specifier: ^0.5.13 version: 0.5.16(tailwindcss@3.4.17) @@ -109,8 +109,8 @@ importers: specifier: ^0.19.0 version: 0.19.3 vite: - specifier: ^5.4.12 - version: 5.4.18(@types/node@22.15.2) + specifier: ^5.4.19 + version: 5.4.19(@types/node@22.15.2) packages: @@ -716,103 +716,103 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.40.0': - resolution: {integrity: sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==} + '@rollup/rollup-android-arm-eabi@4.40.2': + resolution: {integrity: sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.40.0': - resolution: {integrity: sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==} + '@rollup/rollup-android-arm64@4.40.2': + resolution: {integrity: sha512-13unNoZ8NzUmnndhPTkWPWbX3vtHodYmy+I9kuLxN+F+l+x3LdVF7UCu8TWVMt1POHLh6oDHhnOA04n8oJZhBw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.40.0': - resolution: {integrity: sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==} + '@rollup/rollup-darwin-arm64@4.40.2': + resolution: {integrity: sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.40.0': - resolution: {integrity: sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==} + '@rollup/rollup-darwin-x64@4.40.2': + resolution: {integrity: sha512-47N4hxa01a4x6XnJoskMKTS8XZ0CZMd8YTbINbi+w03A2w4j1RTlnGHOz/P0+Bg1LaVL6ufZyNprSg+fW5nYQQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.40.0': - resolution: {integrity: sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==} + '@rollup/rollup-freebsd-arm64@4.40.2': + resolution: {integrity: sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.40.0': - resolution: {integrity: sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==} + '@rollup/rollup-freebsd-x64@4.40.2': + resolution: {integrity: sha512-C+AyHBzfpsOEYRFjztcYUFsH4S7UsE9cDtHCtma5BK8+ydOZYgMmWg1d/4KBytQspJCld8ZIujFMAdKG1xyr4Q==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.40.0': - resolution: {integrity: sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==} + '@rollup/rollup-linux-arm-gnueabihf@4.40.2': + resolution: {integrity: sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.40.0': - resolution: {integrity: sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==} + '@rollup/rollup-linux-arm-musleabihf@4.40.2': + resolution: {integrity: sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.40.0': - resolution: {integrity: sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==} + '@rollup/rollup-linux-arm64-gnu@4.40.2': + resolution: {integrity: sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.40.0': - resolution: {integrity: sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==} + '@rollup/rollup-linux-arm64-musl@4.40.2': + resolution: {integrity: sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.40.0': - resolution: {integrity: sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==} + '@rollup/rollup-linux-loongarch64-gnu@4.40.2': + resolution: {integrity: sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.40.0': - resolution: {integrity: sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==} + '@rollup/rollup-linux-powerpc64le-gnu@4.40.2': + resolution: {integrity: sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.40.0': - resolution: {integrity: sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==} + '@rollup/rollup-linux-riscv64-gnu@4.40.2': + resolution: {integrity: sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.40.0': - resolution: {integrity: sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==} + '@rollup/rollup-linux-riscv64-musl@4.40.2': + resolution: {integrity: sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.40.0': - resolution: {integrity: sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==} + '@rollup/rollup-linux-s390x-gnu@4.40.2': + resolution: {integrity: sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.40.0': - resolution: {integrity: sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==} + '@rollup/rollup-linux-x64-gnu@4.40.2': + resolution: {integrity: sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.40.0': - resolution: {integrity: sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==} + '@rollup/rollup-linux-x64-musl@4.40.2': + resolution: {integrity: sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.40.0': - resolution: {integrity: sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==} + '@rollup/rollup-win32-arm64-msvc@4.40.2': + resolution: {integrity: sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.40.0': - resolution: {integrity: sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==} + '@rollup/rollup-win32-ia32-msvc@4.40.2': + resolution: {integrity: sha512-dt1llVSGEsGKvzeIO76HToiYPNPYPkmjhMHhP00T9S4rDern8P2ZWvWAQUEJ+R1UdMWJ/42i/QqJ2WV765GZcA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.40.0': - resolution: {integrity: sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==} + '@rollup/rollup-win32-x64-msvc@4.40.2': + resolution: {integrity: sha512-bwspbWB04XJpeElvsp+DCylKfF4trJDa2Y9Go8O6A7YLX2LIKGcNK/CYImJN6ZP4DcuOHB4Utl3iCbnR62DudA==} cpu: [x64] os: [win32] @@ -1799,8 +1799,8 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true - rollup@4.40.0: - resolution: {integrity: sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==} + rollup@4.40.2: + resolution: {integrity: sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -2061,8 +2061,8 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - vite@5.4.18: - resolution: {integrity: sha512-1oDcnEp3lVyHCuQ2YFelM4Alm2o91xNoMncRm1U7S+JdYfYOvbiGZ3/CxGttrOu2M/KcGz7cRC2DoNUA6urmMA==} + vite@5.4.19: + resolution: {integrity: sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -2554,9 +2554,9 @@ snapshots: '@polka/url@1.0.0-next.29': {} - '@rollup/plugin-commonjs@28.0.3(rollup@4.40.0)': + '@rollup/plugin-commonjs@28.0.3(rollup@4.40.2)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.40.0) + '@rollup/pluginutils': 5.1.4(rollup@4.40.2) commondir: 1.0.1 estree-walker: 2.0.2 fdir: 6.4.4(picomatch@4.0.2) @@ -2564,113 +2564,113 @@ snapshots: magic-string: 0.30.17 picomatch: 4.0.2 optionalDependencies: - rollup: 4.40.0 + rollup: 4.40.2 - '@rollup/plugin-json@6.1.0(rollup@4.40.0)': + '@rollup/plugin-json@6.1.0(rollup@4.40.2)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.40.0) + '@rollup/pluginutils': 5.1.4(rollup@4.40.2) optionalDependencies: - rollup: 4.40.0 + rollup: 4.40.2 - '@rollup/plugin-node-resolve@16.0.1(rollup@4.40.0)': + '@rollup/plugin-node-resolve@16.0.1(rollup@4.40.2)': dependencies: - '@rollup/pluginutils': 5.1.4(rollup@4.40.0) + '@rollup/pluginutils': 5.1.4(rollup@4.40.2) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 resolve: 1.22.10 optionalDependencies: - rollup: 4.40.0 + rollup: 4.40.2 - '@rollup/pluginutils@5.1.4(rollup@4.40.0)': + '@rollup/pluginutils@5.1.4(rollup@4.40.2)': dependencies: '@types/estree': 1.0.7 estree-walker: 2.0.2 picomatch: 4.0.2 optionalDependencies: - rollup: 4.40.0 + rollup: 4.40.2 - '@rollup/rollup-android-arm-eabi@4.40.0': + '@rollup/rollup-android-arm-eabi@4.40.2': optional: true - '@rollup/rollup-android-arm64@4.40.0': + '@rollup/rollup-android-arm64@4.40.2': optional: true - '@rollup/rollup-darwin-arm64@4.40.0': + '@rollup/rollup-darwin-arm64@4.40.2': optional: true - '@rollup/rollup-darwin-x64@4.40.0': + '@rollup/rollup-darwin-x64@4.40.2': optional: true - '@rollup/rollup-freebsd-arm64@4.40.0': + '@rollup/rollup-freebsd-arm64@4.40.2': optional: true - '@rollup/rollup-freebsd-x64@4.40.0': + '@rollup/rollup-freebsd-x64@4.40.2': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.40.0': + '@rollup/rollup-linux-arm-gnueabihf@4.40.2': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.40.0': + '@rollup/rollup-linux-arm-musleabihf@4.40.2': optional: true - '@rollup/rollup-linux-arm64-gnu@4.40.0': + '@rollup/rollup-linux-arm64-gnu@4.40.2': optional: true - '@rollup/rollup-linux-arm64-musl@4.40.0': + '@rollup/rollup-linux-arm64-musl@4.40.2': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.40.0': + '@rollup/rollup-linux-loongarch64-gnu@4.40.2': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.40.0': + '@rollup/rollup-linux-powerpc64le-gnu@4.40.2': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.40.0': + '@rollup/rollup-linux-riscv64-gnu@4.40.2': optional: true - '@rollup/rollup-linux-riscv64-musl@4.40.0': + '@rollup/rollup-linux-riscv64-musl@4.40.2': optional: true - '@rollup/rollup-linux-s390x-gnu@4.40.0': + '@rollup/rollup-linux-s390x-gnu@4.40.2': optional: true - '@rollup/rollup-linux-x64-gnu@4.40.0': + '@rollup/rollup-linux-x64-gnu@4.40.2': optional: true - '@rollup/rollup-linux-x64-musl@4.40.0': + '@rollup/rollup-linux-x64-musl@4.40.2': optional: true - '@rollup/rollup-win32-arm64-msvc@4.40.0': + '@rollup/rollup-win32-arm64-msvc@4.40.2': optional: true - '@rollup/rollup-win32-ia32-msvc@4.40.0': + '@rollup/rollup-win32-ia32-msvc@4.40.2': optional: true - '@rollup/rollup-win32-x64-msvc@4.40.0': + '@rollup/rollup-win32-x64-msvc@4.40.2': optional: true - '@sveltejs/adapter-node@5.2.12(@sveltejs/kit@2.20.7(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.18(@types/node@22.15.2)))(svelte@4.2.19)(vite@5.4.18(@types/node@22.15.2)))': + '@sveltejs/adapter-node@5.2.12(@sveltejs/kit@2.20.7(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2)))(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2)))': dependencies: - '@rollup/plugin-commonjs': 28.0.3(rollup@4.40.0) - '@rollup/plugin-json': 6.1.0(rollup@4.40.0) - '@rollup/plugin-node-resolve': 16.0.1(rollup@4.40.0) - '@sveltejs/kit': 2.20.7(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.18(@types/node@22.15.2)))(svelte@4.2.19)(vite@5.4.18(@types/node@22.15.2)) - rollup: 4.40.0 + '@rollup/plugin-commonjs': 28.0.3(rollup@4.40.2) + '@rollup/plugin-json': 6.1.0(rollup@4.40.2) + '@rollup/plugin-node-resolve': 16.0.1(rollup@4.40.2) + '@sveltejs/kit': 2.20.7(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2)))(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2)) + rollup: 4.40.2 - '@sveltejs/adapter-vercel@5.7.0(@sveltejs/kit@2.20.7(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.18(@types/node@22.15.2)))(svelte@4.2.19)(vite@5.4.18(@types/node@22.15.2)))(rollup@4.40.0)': + '@sveltejs/adapter-vercel@5.7.0(@sveltejs/kit@2.20.7(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2)))(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2)))(rollup@4.40.2)': dependencies: - '@sveltejs/kit': 2.20.7(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.18(@types/node@22.15.2)))(svelte@4.2.19)(vite@5.4.18(@types/node@22.15.2)) - '@vercel/nft': 0.29.2(rollup@4.40.0) + '@sveltejs/kit': 2.20.7(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2)))(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2)) + '@vercel/nft': 0.29.2(rollup@4.40.2) esbuild: 0.24.2 transitivePeerDependencies: - encoding - rollup - supports-color - '@sveltejs/kit@2.20.7(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.18(@types/node@22.15.2)))(svelte@4.2.19)(vite@5.4.18(@types/node@22.15.2))': + '@sveltejs/kit@2.20.7(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2)))(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2))': dependencies: - '@sveltejs/vite-plugin-svelte': 3.1.2(svelte@4.2.19)(vite@5.4.18(@types/node@22.15.2)) + '@sveltejs/vite-plugin-svelte': 3.1.2(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2)) '@types/cookie': 0.6.0 cookie: 0.6.0 devalue: 5.1.1 @@ -2683,28 +2683,28 @@ snapshots: set-cookie-parser: 2.7.1 sirv: 3.0.1 svelte: 4.2.19 - vite: 5.4.18(@types/node@22.15.2) + vite: 5.4.19(@types/node@22.15.2) - '@sveltejs/vite-plugin-svelte-inspector@2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.18(@types/node@22.15.2)))(svelte@4.2.19)(vite@5.4.18(@types/node@22.15.2))': + '@sveltejs/vite-plugin-svelte-inspector@2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2)))(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2))': dependencies: - '@sveltejs/vite-plugin-svelte': 3.1.2(svelte@4.2.19)(vite@5.4.18(@types/node@22.15.2)) + '@sveltejs/vite-plugin-svelte': 3.1.2(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2)) debug: 4.4.0 svelte: 4.2.19 - vite: 5.4.18(@types/node@22.15.2) + vite: 5.4.19(@types/node@22.15.2) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.18(@types/node@22.15.2))': + '@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.18(@types/node@22.15.2)))(svelte@4.2.19)(vite@5.4.18(@types/node@22.15.2)) + '@sveltejs/vite-plugin-svelte-inspector': 2.1.0(@sveltejs/vite-plugin-svelte@3.1.2(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2)))(svelte@4.2.19)(vite@5.4.19(@types/node@22.15.2)) debug: 4.4.0 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.17 svelte: 4.2.19 svelte-hmr: 0.16.0(svelte@4.2.19) - vite: 5.4.18(@types/node@22.15.2) - vitefu: 0.2.5(vite@5.4.18(@types/node@22.15.2)) + vite: 5.4.19(@types/node@22.15.2) + vitefu: 0.2.5(vite@5.4.19(@types/node@22.15.2)) transitivePeerDependencies: - supports-color @@ -2759,10 +2759,10 @@ snapshots: '@types/trusted-types@2.0.7': optional: true - '@vercel/nft@0.29.2(rollup@4.40.0)': + '@vercel/nft@0.29.2(rollup@4.40.2)': dependencies: '@mapbox/node-pre-gyp': 2.0.0 - '@rollup/pluginutils': 5.1.4(rollup@4.40.0) + '@rollup/pluginutils': 5.1.4(rollup@4.40.2) acorn: 8.14.1 acorn-import-attributes: 1.9.5(acorn@8.14.1) async-sema: 3.1.1 @@ -3670,30 +3670,30 @@ snapshots: dependencies: glob: 7.2.3 - rollup@4.40.0: + rollup@4.40.2: dependencies: '@types/estree': 1.0.7 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.40.0 - '@rollup/rollup-android-arm64': 4.40.0 - '@rollup/rollup-darwin-arm64': 4.40.0 - '@rollup/rollup-darwin-x64': 4.40.0 - '@rollup/rollup-freebsd-arm64': 4.40.0 - '@rollup/rollup-freebsd-x64': 4.40.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.40.0 - '@rollup/rollup-linux-arm-musleabihf': 4.40.0 - '@rollup/rollup-linux-arm64-gnu': 4.40.0 - '@rollup/rollup-linux-arm64-musl': 4.40.0 - '@rollup/rollup-linux-loongarch64-gnu': 4.40.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.40.0 - '@rollup/rollup-linux-riscv64-gnu': 4.40.0 - '@rollup/rollup-linux-riscv64-musl': 4.40.0 - '@rollup/rollup-linux-s390x-gnu': 4.40.0 - '@rollup/rollup-linux-x64-gnu': 4.40.0 - '@rollup/rollup-linux-x64-musl': 4.40.0 - '@rollup/rollup-win32-arm64-msvc': 4.40.0 - '@rollup/rollup-win32-ia32-msvc': 4.40.0 - '@rollup/rollup-win32-x64-msvc': 4.40.0 + '@rollup/rollup-android-arm-eabi': 4.40.2 + '@rollup/rollup-android-arm64': 4.40.2 + '@rollup/rollup-darwin-arm64': 4.40.2 + '@rollup/rollup-darwin-x64': 4.40.2 + '@rollup/rollup-freebsd-arm64': 4.40.2 + '@rollup/rollup-freebsd-x64': 4.40.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.40.2 + '@rollup/rollup-linux-arm-musleabihf': 4.40.2 + '@rollup/rollup-linux-arm64-gnu': 4.40.2 + '@rollup/rollup-linux-arm64-musl': 4.40.2 + '@rollup/rollup-linux-loongarch64-gnu': 4.40.2 + '@rollup/rollup-linux-powerpc64le-gnu': 4.40.2 + '@rollup/rollup-linux-riscv64-gnu': 4.40.2 + '@rollup/rollup-linux-riscv64-musl': 4.40.2 + '@rollup/rollup-linux-s390x-gnu': 4.40.2 + '@rollup/rollup-linux-x64-gnu': 4.40.2 + '@rollup/rollup-linux-x64-musl': 4.40.2 + '@rollup/rollup-win32-arm64-msvc': 4.40.2 + '@rollup/rollup-win32-ia32-msvc': 4.40.2 + '@rollup/rollup-win32-x64-msvc': 4.40.2 fsevents: 2.3.3 run-parallel@1.2.0: @@ -3968,18 +3968,18 @@ snapshots: util-deprecate@1.0.2: {} - vite@5.4.18(@types/node@22.15.2): + vite@5.4.19(@types/node@22.15.2): dependencies: esbuild: 0.21.5 postcss: 8.5.3 - rollup: 4.40.0 + rollup: 4.40.2 optionalDependencies: '@types/node': 22.15.2 fsevents: 2.3.3 - vitefu@0.2.5(vite@5.4.18(@types/node@22.15.2)): + vitefu@0.2.5(vite@5.4.19(@types/node@22.15.2)): optionalDependencies: - vite: 5.4.18(@types/node@22.15.2) + vite: 5.4.19(@types/node@22.15.2) vt-pbf@3.1.3: dependencies: diff --git a/frontend/src/lib/components/AdventureModal.svelte b/frontend/src/lib/components/AdventureModal.svelte index 20dd39f..8a93284 100644 --- a/frontend/src/lib/components/AdventureModal.svelte +++ b/frontend/src/lib/components/AdventureModal.svelte @@ -92,6 +92,7 @@ import Crown from '~icons/mdi/crown'; import AttachmentCard from './AttachmentCard.svelte'; import LocationDropdown from './LocationDropdown.svelte'; + import DateRangeCollapse from './DateRangeCollapse.svelte'; let modal: HTMLDialogElement; let wikiError: string = ''; @@ -389,54 +390,6 @@ } } - let new_start_date: string = ''; - let new_end_date: string = ''; - let new_notes: string = ''; - - // Function to add a new visit. - function addNewVisit() { - // If an end date isn’t provided, assume it’s the same as start. - if (new_start_date && !new_end_date) { - new_end_date = new_start_date; - } - if (new_start_date > new_end_date) { - addToast('error', $t('adventures.start_before_end_error')); - return; - } - if (new_end_date && !new_start_date) { - addToast('error', $t('adventures.no_start_date')); - return; - } - // Convert input to UTC if not already. - if (new_start_date && !new_start_date.includes('Z')) { - new_start_date = new Date(new_start_date).toISOString(); - } - if (new_end_date && !new_end_date.includes('Z')) { - new_end_date = new Date(new_end_date).toISOString(); - } - - // If the visit is all day, force the times to midnight. - if (allDay) { - new_start_date = new_start_date.split('T')[0] + 'T00:00:00.000Z'; - new_end_date = new_end_date.split('T')[0] + 'T00:00:00.000Z'; - } - - adventure.visits = [ - ...adventure.visits, - { - start_date: new_start_date, - end_date: new_end_date, - notes: new_notes, - id: '' // or generate an id as needed - } - ]; - - // Clear the input fields. - new_start_date = ''; - new_end_date = ''; - new_notes = ''; - } - function close() { dispatch('close'); } @@ -462,6 +415,13 @@ event.preventDefault(); triggerMarkVisted = true; + // if category icon is empty, set it to the default icon + if (adventure.category?.icon == '' || adventure.category?.icon == null) { + if (adventure.category) { + adventure.category.icon = '🌍'; + } + } + if (adventure.id === '') { if (adventure.category?.display_name == '') { if (categories.some((category) => category.name === 'general')) { @@ -478,6 +438,7 @@ }; } } + let res = await fetch('/api/adventures', { method: 'POST', headers: { @@ -646,7 +607,7 @@

{wikiError}

- {#if !collection?.id} + {#if !adventure?.collection}
-
- -
- {$t('adventures.visits')} ({adventure.visits.length}) -
-
- -
- {#if !allDay} - { - if (e.key === 'Enter') { - e.preventDefault(); - addNewVisit(); - } - }} - /> - { - if (e.key === 'Enter') { - e.preventDefault(); - addNewVisit(); - } - }} - /> - {:else} - { - if (e.key === 'Enter') { - e.preventDefault(); - addNewVisit(); - } - }} - /> - { - if (e.key === 'Enter') { - e.preventDefault(); - addNewVisit(); - } - }} - /> - {/if} -
-
- - -
- {#if !allDay} - - {/if} -
- -
- - {#if adventure.visits.length > 0} -

{$t('adventures.my_visits')}

- {#each adventure.visits as visit} -
-
-

- {#if isAllDay(visit.start_date)} - - {new Date(visit.start_date).toLocaleDateString(undefined, { - timeZone: 'UTC' - })} - {:else} - - {new Date(visit.start_date).toLocaleDateString()} ({new Date( - visit.start_date - ).toLocaleTimeString()}) - {/if} -

- {#if visit.end_date && visit.end_date !== visit.start_date} -

- {#if isAllDay(visit.end_date)} - - {new Date(visit.end_date).toLocaleDateString(undefined, { - timeZone: 'UTC' - })} - {:else} - - {new Date(visit.end_date).toLocaleDateString()} ({new Date( - visit.end_date - ).toLocaleTimeString()}) - {/if} -

- {/if} -
- - -
-
-

{visit.notes}

-
- {/each} - {/if} -
-
+
@@ -936,8 +668,10 @@ {$t('adventures.warning')}: {warningMessage}
{/if} - - +
+ + +
diff --git a/frontend/src/lib/components/CollectionCard.svelte b/frontend/src/lib/components/CollectionCard.svelte index 78b511f..c897a54 100644 --- a/frontend/src/lib/components/CollectionCard.svelte +++ b/frontend/src/lib/components/CollectionCard.svelte @@ -87,7 +87,7 @@ {/if}
@@ -137,7 +137,7 @@
+ + + {#if !validateDateRange(utcStartDate ?? '', utcEndDate ?? '').valid} + + {/if} + + {#if type === 'adventure'} +
+

+ {$t('adventures.visits')} +

+ + + {#if visits && visits.length === 0} +

+ {$t('adventures.no_visits')} +

+ {/if} +
+ + {#if visits && visits.length > 0} +
+ {#each visits as visit} +
+

+ {#if isAllDay(visit.start_date)} + {$t('adventures.all_day')} + {visit.start_date ? visit.start_date.split('T')[0] : ''} – {visit.end_date + ? visit.end_date.split('T')[0] + : ''} + {:else if 'start_timezone' in visit} + {formatDateInTimezone(visit.start_date, visit.start_timezone)} – {formatDateInTimezone( + visit.end_date, + visit.end_timezone + )} + {:else if visit.timezone} + {formatDateInTimezone(visit.start_date, visit.timezone)} – {formatDateInTimezone( + visit.end_date, + visit.timezone + )} + {:else} + {new Date(visit.start_date).toLocaleString()} – {new Date( + visit.end_date + ).toLocaleString()} + + {/if} + {#if 'timezone' in visit && visit.timezone} + {visit.timezone} + {/if} +

+ + + + + {#if 'start_timezone' in visit && 'end_timezone' in visit && visit.start_timezone !== visit.end_timezone} +

+ {visit.start_timezone} → {visit.end_timezone} +

+ {/if} + + {#if visit.notes} +

+ "{visit.notes}" +

+ {/if} + +
+ + +
+
+ {/each} +
+ {/if} + {/if} +
+ diff --git a/frontend/src/lib/components/LocationDropdown.svelte b/frontend/src/lib/components/LocationDropdown.svelte index 1ca4b12..b39628f 100644 --- a/frontend/src/lib/components/LocationDropdown.svelte +++ b/frontend/src/lib/components/LocationDropdown.svelte @@ -55,7 +55,7 @@ } $: if (triggerMarkVisted && willBeMarkedVisited) { - markVisited(); + displaySuccessToast(); // since the server will trigger the geocode automatically, we just need to show the toast and let the server handle the rest. It's kinda a placebo effect... triggerMarkVisted = false; } @@ -102,6 +102,17 @@ } } + function displaySuccessToast() { + if (reverseGeocodePlace) { + if (reverseGeocodePlace.region) { + addToast('success', `Visit to ${reverseGeocodePlace.region} marked`); + } + if (reverseGeocodePlace.city) { + addToast('success', `Visit to ${reverseGeocodePlace.city} marked`); + } + } + } + async function markVisited() { console.log(reverseGeocodePlace); if (reverseGeocodePlace) { diff --git a/frontend/src/lib/components/LodgingCard.svelte b/frontend/src/lib/components/LodgingCard.svelte index 2fe8b0e..7b7395c 100644 --- a/frontend/src/lib/components/LodgingCard.svelte +++ b/frontend/src/lib/components/LodgingCard.svelte @@ -18,6 +18,23 @@ } } + function formatDateInTimezone(utcDate: string, timezone?: string): string { + if (!utcDate) return ''; + try { + return new Intl.DateTimeFormat(undefined, { + timeZone: timezone, + year: 'numeric', + month: 'short', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', + hour12: true + }).format(new Date(utcDate)); + } catch { + return new Date(utcDate).toLocaleString(); + } + } + export let lodging: Lodging; export let user: User | null = null; export let collection: Collection | null = null; @@ -119,21 +136,11 @@
{$t('adventures.dates')}:

- {new Date(lodging.check_in).toLocaleString(undefined, { - month: 'short', - day: 'numeric', - year: 'numeric', - hour: 'numeric', - minute: 'numeric' - })} - - - {new Date(lodging.check_out).toLocaleString(undefined, { - month: 'short', - day: 'numeric', - year: 'numeric', - hour: 'numeric', - minute: 'numeric' - })} + {formatDateInTimezone(lodging.check_in ?? '', lodging.timezone ?? undefined)} – + {formatDateInTimezone(lodging.check_out ?? '', lodging.timezone ?? undefined)} + {#if lodging.timezone} + ({lodging.timezone}) + {/if}

{/if} diff --git a/frontend/src/lib/components/LodgingModal.svelte b/frontend/src/lib/components/LodgingModal.svelte index d91155c..239211b 100644 --- a/frontend/src/lib/components/LodgingModal.svelte +++ b/frontend/src/lib/components/LodgingModal.svelte @@ -5,6 +5,7 @@ import MarkdownEditor from './MarkdownEditor.svelte'; import type { Collection, Lodging } from '$lib/types'; import LocationDropdown from './LocationDropdown.svelte'; + import DateRangeCollapse from './DateRangeCollapse.svelte'; const dispatch = createEventDispatcher(); @@ -12,22 +13,10 @@ export let lodgingToEdit: Lodging | null = null; let modal: HTMLDialogElement; - let constrainDates: boolean = false; let lodging: Lodging = { ...initializeLodging(lodgingToEdit) }; let fullStartDate: string = ''; let fullEndDate: string = ''; - // Format date as local datetime - // Convert an ISO date to a datetime-local value in local time. - function toLocalDatetime(value: string | null): string { - if (!value) return ''; - const date = new Date(value); - // Adjust the time by subtracting the timezone offset. - date.setMinutes(date.getMinutes() - date.getTimezoneOffset()); - // Return format YYYY-MM-DDTHH:mm - return date.toISOString().slice(0, 16); - } - type LodgingType = { value: string; label: string; @@ -47,27 +36,28 @@ { value: 'other', label: 'Other' } ]; - // Initialize hotel with values from hotelToEdit or default values - function initializeLodging(hotelToEdit: Lodging | null): Lodging { + // Initialize hotel with values from lodgingToEdit or default values + function initializeLodging(lodgingToEdit: Lodging | null): Lodging { return { - id: hotelToEdit?.id || '', - user_id: hotelToEdit?.user_id || '', - name: hotelToEdit?.name || '', - type: hotelToEdit?.type || 'other', - description: hotelToEdit?.description || '', - rating: hotelToEdit?.rating || NaN, - link: hotelToEdit?.link || '', - check_in: hotelToEdit?.check_in ? toLocalDatetime(hotelToEdit.check_in) : null, - check_out: hotelToEdit?.check_out ? toLocalDatetime(hotelToEdit.check_out) : null, - reservation_number: hotelToEdit?.reservation_number || '', - price: hotelToEdit?.price || null, - latitude: hotelToEdit?.latitude || null, - longitude: hotelToEdit?.longitude || null, - location: hotelToEdit?.location || '', - is_public: hotelToEdit?.is_public || false, - collection: hotelToEdit?.collection || collection.id, - created_at: hotelToEdit?.created_at || '', - updated_at: hotelToEdit?.updated_at || '' + id: lodgingToEdit?.id || '', + user_id: lodgingToEdit?.user_id || '', + name: lodgingToEdit?.name || '', + type: lodgingToEdit?.type || 'other', + description: lodgingToEdit?.description || '', + rating: lodgingToEdit?.rating || NaN, + link: lodgingToEdit?.link || '', + check_in: lodgingToEdit?.check_in || null, + check_out: lodgingToEdit?.check_out || null, + reservation_number: lodgingToEdit?.reservation_number || '', + price: lodgingToEdit?.price || null, + latitude: lodgingToEdit?.latitude || null, + longitude: lodgingToEdit?.longitude || null, + location: lodgingToEdit?.location || '', + is_public: lodgingToEdit?.is_public || false, + collection: lodgingToEdit?.collection || collection.id, + created_at: lodgingToEdit?.created_at || '', + updated_at: lodgingToEdit?.updated_at || '', + timezone: lodgingToEdit?.timezone || '' }; } @@ -104,27 +94,6 @@ async function handleSubmit(event: Event) { event.preventDefault(); - if (lodging.check_in && !lodging.check_out) { - const checkInDate = new Date(lodging.check_in); - checkInDate.setDate(checkInDate.getDate() + 1); - lodging.check_out = checkInDate.toISOString(); - } - - if (lodging.check_in && lodging.check_out && lodging.check_in > lodging.check_out) { - addToast('error', $t('adventures.start_before_end_error')); - return; - } - - // Only convert to UTC if the time is still in local format. - if (lodging.check_in && !lodging.check_in.includes('Z')) { - // new Date(lodging.check_in) interprets the input as local time. - lodging.check_in = new Date(lodging.check_in).toISOString(); - } - if (lodging.check_out && !lodging.check_out.includes('Z')) { - lodging.check_out = new Date(lodging.check_out).toISOString(); - } - console.log(lodging.check_in, lodging.check_out); - // Create or update lodging... const url = lodging.id === '' ? '/api/lodging' : `/api/lodging/${lodging.id}`; const method = lodging.id === '' ? 'POST' : 'PATCH'; @@ -323,6 +292,7 @@ id="price" name="price" bind:value={lodging.price} + step="0.01" class="input input-bordered w-full max-w-xs mt-1" /> @@ -330,85 +300,12 @@ -
- -
- {$t('adventures.date_information')} -
-
- -
- - - {#if collection && collection.start_date && collection.end_date} - {/if} -
- -
-
- -
- -
- -
-
- -
-
+ diff --git a/frontend/src/lib/components/Navbar.svelte b/frontend/src/lib/components/Navbar.svelte index efa0fa6..7fc134d 100644 --- a/frontend/src/lib/components/Navbar.svelte +++ b/frontend/src/lib/components/Navbar.svelte @@ -20,6 +20,8 @@ import { onMount } from 'svelte'; let inputElement: HTMLInputElement | null = null; + let theme = ''; + // Event listener for focusing input function handleKeydown(event: KeyboardEvent) { // Ignore any keypresses in an input/textarea field, so we don't interfere with typing. @@ -38,6 +40,8 @@ // Attach event listener on component mount document.addEventListener('keydown', handleKeydown); + theme = document.documentElement.getAttribute('data-theme'); + // Cleanup event listener on component destruction return () => { document.removeEventListener('keydown', handleKeydown); @@ -69,9 +73,14 @@ locale.set(newLocale); window.location.reload(); }; + const submitThemeChange = (event: Event) => { + const theme = event.target.value; + const themeForm = event.target.parentNode; + themeForm.action = `/?/setTheme&theme=${theme}`; + themeForm.submit(); + }; const submitUpdateTheme: SubmitFunction = ({ action }) => { const theme = action.searchParams.get('theme'); - console.log('theme', theme); if (theme) { document.documentElement.setAttribute('data-theme', theme); } @@ -117,11 +126,10 @@ - + Map Logo @@ -304,13 +312,15 @@

{$t('navbar.theme_selection')}

- {#each themes as theme} -
  • - -
  • - {/each} +
    diff --git a/frontend/src/lib/components/NoteCard.svelte b/frontend/src/lib/components/NoteCard.svelte index 9b6ba62..d7b35f1 100644 --- a/frontend/src/lib/components/NoteCard.svelte +++ b/frontend/src/lib/components/NoteCard.svelte @@ -5,6 +5,12 @@ import { createEventDispatcher } from 'svelte'; const dispatch = createEventDispatcher(); + import { marked } from 'marked'; // Import the markdown parser + + const renderMarkdown = (markdown: string) => { + return marked(markdown); + }; + import Launch from '~icons/mdi/launch'; import TrashCan from '~icons/mdi/trash-can'; import Calendar from '~icons/mdi/calendar'; @@ -71,11 +77,30 @@ {#if unlinked}
    {$t('adventures.out_of_range')}
    {/if} + {#if note.content && note.content.length > 0} +
    + {@html renderMarkdown(note.content || '')} +
    + {/if} {#if note.links && note.links.length > 0}

    {note.links.length} {note.links.length > 1 ? $t('adventures.links') : $t('adventures.link')}

    + {/if} {#if note.date && note.date !== ''}
    diff --git a/frontend/src/lib/components/TimezoneSelector.svelte b/frontend/src/lib/components/TimezoneSelector.svelte index c0738b8..5603555 100644 --- a/frontend/src/lib/components/TimezoneSelector.svelte +++ b/frontend/src/lib/components/TimezoneSelector.svelte @@ -3,9 +3,13 @@ import { onMount } from 'svelte'; export let selectedTimezone: string = Intl.DateTimeFormat().resolvedOptions().timeZone; + // Generate a unique ID for this component instance + const uniqueId = Date.now().toString(36) + Math.random().toString(36).substring(2); + const instanceId = `tz-selector-${uniqueId}`; let dropdownOpen = false; let searchQuery = ''; + let searchInput: HTMLInputElement | null = null; const timezones = Intl.supportedValuesOf('timeZone'); // Filter timezones based on search query @@ -19,10 +23,29 @@ searchQuery = ''; } + // Focus search input when dropdown opens - with proper null check + $: if (dropdownOpen && searchInput) { + // Use setTimeout to delay focus until after the element is rendered + setTimeout(() => { + if (searchInput) searchInput.focus(); + }, 0); + } + + function handleKeydown(event: KeyboardEvent, tz?: string) { + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault(); + if (tz) selectTimezone(tz); + else dropdownOpen = !dropdownOpen; + } else if (event.key === 'Escape') { + event.preventDefault(); + dropdownOpen = false; + } + } + // Close dropdown if clicked outside onMount(() => { const handleClickOutside = (e: MouseEvent) => { - const dropdown = document.getElementById('tz-selector'); + const dropdown = document.getElementById(instanceId); if (dropdown && !dropdown.contains(e.target as Node)) dropdownOpen = false; }; document.addEventListener('click', handleClickOutside); @@ -30,17 +53,21 @@ }); -
    -
    -
    - -
    - {$t('adventures.date_information')} -
    -
    - - -
    - - {#if collection && collection.start_date && collection.end_date} - {/if} -
    - -
    -
    - - {#if localStartDate} -
    - -
    - -
    -
    - {/if} - - {#if utcStartDate} -
    - UTC Time: {formatUTCDate(utcStartDate)} - {#if utcEndDate && utcEndDate !== utcStartDate} - to {formatUTCDate(utcEndDate)} - {/if} -
    - {/if} -
    -
    + + -
    diff --git a/frontend/src/lib/dateUtils.ts b/frontend/src/lib/dateUtils.ts index d47f119..3eccaa5 100644 --- a/frontend/src/lib/dateUtils.ts +++ b/frontend/src/lib/dateUtils.ts @@ -12,10 +12,16 @@ export function toLocalDatetime( timezone: string = Intl.DateTimeFormat().resolvedOptions().timeZone ): string { if (!utcDate) return ''; - return DateTime.fromISO(utcDate, { zone: 'UTC' }) - .setZone(timezone) - .toISO({ suppressSeconds: true, includeOffset: false }) - .slice(0, 16); + + const dt = DateTime.fromISO(utcDate, { zone: 'UTC' }); + if (!dt.isValid) return ''; + + const isoString = dt.setZone(timezone).toISO({ + suppressSeconds: true, + includeOffset: false + }); + + return isoString ? isoString.slice(0, 16) : ''; } /** @@ -26,10 +32,22 @@ export function toLocalDatetime( */ export function toUTCDatetime( localDate: string, - timezone: string = Intl.DateTimeFormat().resolvedOptions().timeZone + timezone: string = Intl.DateTimeFormat().resolvedOptions().timeZone, + allDay: boolean = false ): string | null { if (!localDate) return null; - return DateTime.fromISO(localDate, { zone: timezone }).toUTC().toISO(); + + if (allDay) { + // Treat input as date-only, set UTC midnight manually + return DateTime.fromISO(localDate, { zone: 'UTC' }) + .startOf('day') + .toISO({ suppressMilliseconds: true }); + } + + // Normal timezone conversion for datetime-local input + return DateTime.fromISO(localDate, { zone: timezone }) + .toUTC() + .toISO({ suppressMilliseconds: true }); } /** @@ -54,17 +72,25 @@ export function updateLocalDate({ * @param params Object containing local date and timezone * @returns Object with updated UTC datetime string */ -export function updateUTCDate({ localDate, timezone }: { localDate: string; timezone: string }) { +export function updateUTCDate({ + localDate, + timezone, + allDay = false +}: { + localDate: string; + timezone: string; + allDay?: boolean; +}) { return { - utcDate: toUTCDatetime(localDate, timezone) + utcDate: toUTCDatetime(localDate, timezone, allDay) }; } /** - * Validate date ranges - * @param startDate - Start date string - * @param endDate - End date string - * @returns Object with validation result and error message + * Validate date ranges using UTC comparison + * @param startDate - Start date string in UTC (ISO format) + * @param endDate - End date string in UTC (ISO format) + * @returns Object with validation result and optional error message */ export function validateDateRange( startDate: string, @@ -80,11 +106,12 @@ export function validateDateRange( if ( startDate && endDate && - DateTime.fromISO(startDate).toMillis() > DateTime.fromISO(endDate).toMillis() + DateTime.fromISO(startDate, { zone: 'utc' }).toMillis() > + DateTime.fromISO(endDate, { zone: 'utc' }).toMillis() ) { return { valid: false, - error: 'Start date must be before end date' + error: 'Start date must be before end date (based on UTC)' }; } @@ -98,5 +125,427 @@ export function validateDateRange( */ export function formatUTCDate(utcDate: string | null): string { if (!utcDate) return ''; - return DateTime.fromISO(utcDate).toISO().slice(0, 16).replace('T', ' '); + const dateTime = DateTime.fromISO(utcDate); + if (!dateTime.isValid) return ''; + return dateTime.toISO()?.slice(0, 16).replace('T', ' ') || ''; } + +export const VALID_TIMEZONES = [ + 'Africa/Abidjan', + 'Africa/Accra', + 'Africa/Addis_Ababa', + 'Africa/Algiers', + 'Africa/Asmera', + 'Africa/Bamako', + 'Africa/Bangui', + 'Africa/Banjul', + 'Africa/Bissau', + 'Africa/Blantyre', + 'Africa/Brazzaville', + 'Africa/Bujumbura', + 'Africa/Cairo', + 'Africa/Casablanca', + 'Africa/Ceuta', + 'Africa/Conakry', + 'Africa/Dakar', + 'Africa/Dar_es_Salaam', + 'Africa/Djibouti', + 'Africa/Douala', + 'Africa/El_Aaiun', + 'Africa/Freetown', + 'Africa/Gaborone', + 'Africa/Harare', + 'Africa/Johannesburg', + 'Africa/Juba', + 'Africa/Kampala', + 'Africa/Khartoum', + 'Africa/Kigali', + 'Africa/Kinshasa', + 'Africa/Lagos', + 'Africa/Libreville', + 'Africa/Lome', + 'Africa/Luanda', + 'Africa/Lubumbashi', + 'Africa/Lusaka', + 'Africa/Malabo', + 'Africa/Maputo', + 'Africa/Maseru', + 'Africa/Mbabane', + 'Africa/Mogadishu', + 'Africa/Monrovia', + 'Africa/Nairobi', + 'Africa/Ndjamena', + 'Africa/Niamey', + 'Africa/Nouakchott', + 'Africa/Ouagadougou', + 'Africa/Porto-Novo', + 'Africa/Sao_Tome', + 'Africa/Tripoli', + 'Africa/Tunis', + 'Africa/Windhoek', + 'America/Adak', + 'America/Anchorage', + 'America/Anguilla', + 'America/Antigua', + 'America/Araguaina', + 'America/Argentina/La_Rioja', + 'America/Argentina/Rio_Gallegos', + 'America/Argentina/Salta', + 'America/Argentina/San_Juan', + 'America/Argentina/San_Luis', + 'America/Argentina/Tucuman', + 'America/Argentina/Ushuaia', + 'America/Aruba', + 'America/Asuncion', + 'America/Bahia', + 'America/Bahia_Banderas', + 'America/Barbados', + 'America/Belem', + 'America/Belize', + 'America/Blanc-Sablon', + 'America/Boa_Vista', + 'America/Bogota', + 'America/Boise', + 'America/Buenos_Aires', + 'America/Cambridge_Bay', + 'America/Campo_Grande', + 'America/Cancun', + 'America/Caracas', + 'America/Catamarca', + 'America/Cayenne', + 'America/Cayman', + 'America/Chicago', + 'America/Chihuahua', + 'America/Ciudad_Juarez', + 'America/Coral_Harbour', + 'America/Cordoba', + 'America/Costa_Rica', + 'America/Creston', + 'America/Cuiaba', + 'America/Curacao', + 'America/Danmarkshavn', + 'America/Dawson', + 'America/Dawson_Creek', + 'America/Denver', + 'America/Detroit', + 'America/Dominica', + 'America/Edmonton', + 'America/Eirunepe', + 'America/El_Salvador', + 'America/Fort_Nelson', + 'America/Fortaleza', + 'America/Glace_Bay', + 'America/Godthab', + 'America/Goose_Bay', + 'America/Grand_Turk', + 'America/Grenada', + 'America/Guadeloupe', + 'America/Guatemala', + 'America/Guayaquil', + 'America/Guyana', + 'America/Halifax', + 'America/Havana', + 'America/Hermosillo', + 'America/Indiana/Knox', + 'America/Indiana/Marengo', + 'America/Indiana/Petersburg', + 'America/Indiana/Tell_City', + 'America/Indiana/Vevay', + 'America/Indiana/Vincennes', + 'America/Indiana/Winamac', + 'America/Indianapolis', + 'America/Inuvik', + 'America/Iqaluit', + 'America/Jamaica', + 'America/Jujuy', + 'America/Juneau', + 'America/Kentucky/Monticello', + 'America/Kralendijk', + 'America/La_Paz', + 'America/Lima', + 'America/Los_Angeles', + 'America/Louisville', + 'America/Lower_Princes', + 'America/Maceio', + 'America/Managua', + 'America/Manaus', + 'America/Marigot', + 'America/Martinique', + 'America/Matamoros', + 'America/Mazatlan', + 'America/Mendoza', + 'America/Menominee', + 'America/Merida', + 'America/Metlakatla', + 'America/Mexico_City', + 'America/Miquelon', + 'America/Moncton', + 'America/Monterrey', + 'America/Montevideo', + 'America/Montserrat', + 'America/Nassau', + 'America/New_York', + 'America/Nome', + 'America/Noronha', + 'America/North_Dakota/Beulah', + 'America/North_Dakota/Center', + 'America/North_Dakota/New_Salem', + 'America/Ojinaga', + 'America/Panama', + 'America/Paramaribo', + 'America/Phoenix', + 'America/Port-au-Prince', + 'America/Port_of_Spain', + 'America/Porto_Velho', + 'America/Puerto_Rico', + 'America/Punta_Arenas', + 'America/Rankin_Inlet', + 'America/Recife', + 'America/Regina', + 'America/Resolute', + 'America/Rio_Branco', + 'America/Santarem', + 'America/Santiago', + 'America/Santo_Domingo', + 'America/Sao_Paulo', + 'America/Scoresbysund', + 'America/Sitka', + 'America/St_Barthelemy', + 'America/St_Johns', + 'America/St_Kitts', + 'America/St_Lucia', + 'America/St_Thomas', + 'America/St_Vincent', + 'America/Swift_Current', + 'America/Tegucigalpa', + 'America/Thule', + 'America/Tijuana', + 'America/Toronto', + 'America/Tortola', + 'America/Vancouver', + 'America/Whitehorse', + 'America/Winnipeg', + 'America/Yakutat', + 'Antarctica/Casey', + 'Antarctica/Davis', + 'Antarctica/DumontDUrville', + 'Antarctica/Macquarie', + 'Antarctica/Mawson', + 'Antarctica/McMurdo', + 'Antarctica/Palmer', + 'Antarctica/Rothera', + 'Antarctica/Syowa', + 'Antarctica/Troll', + 'Antarctica/Vostok', + 'Arctic/Longyearbyen', + 'Asia/Aden', + 'Asia/Almaty', + 'Asia/Amman', + 'Asia/Anadyr', + 'Asia/Aqtau', + 'Asia/Aqtobe', + 'Asia/Ashgabat', + 'Asia/Atyrau', + 'Asia/Baghdad', + 'Asia/Bahrain', + 'Asia/Baku', + 'Asia/Bangkok', + 'Asia/Barnaul', + 'Asia/Beirut', + 'Asia/Bishkek', + 'Asia/Brunei', + 'Asia/Calcutta', + 'Asia/Chita', + 'Asia/Colombo', + 'Asia/Damascus', + 'Asia/Dhaka', + 'Asia/Dili', + 'Asia/Dubai', + 'Asia/Dushanbe', + 'Asia/Famagusta', + 'Asia/Gaza', + 'Asia/Hebron', + 'Asia/Hong_Kong', + 'Asia/Hovd', + 'Asia/Irkutsk', + 'Asia/Jakarta', + 'Asia/Jayapura', + 'Asia/Jerusalem', + 'Asia/Kabul', + 'Asia/Kamchatka', + 'Asia/Karachi', + 'Asia/Katmandu', + 'Asia/Khandyga', + 'Asia/Krasnoyarsk', + 'Asia/Kuala_Lumpur', + 'Asia/Kuching', + 'Asia/Kuwait', + 'Asia/Macau', + 'Asia/Magadan', + 'Asia/Makassar', + 'Asia/Manila', + 'Asia/Muscat', + 'Asia/Nicosia', + 'Asia/Novokuznetsk', + 'Asia/Novosibirsk', + 'Asia/Omsk', + 'Asia/Oral', + 'Asia/Phnom_Penh', + 'Asia/Pontianak', + 'Asia/Pyongyang', + 'Asia/Qatar', + 'Asia/Qostanay', + 'Asia/Qyzylorda', + 'Asia/Rangoon', + 'Asia/Riyadh', + 'Asia/Saigon', + 'Asia/Sakhalin', + 'Asia/Samarkand', + 'Asia/Seoul', + 'Asia/Shanghai', + 'Asia/Singapore', + 'Asia/Srednekolymsk', + 'Asia/Taipei', + 'Asia/Tashkent', + 'Asia/Tbilisi', + 'Asia/Tehran', + 'Asia/Thimphu', + 'Asia/Tokyo', + 'Asia/Tomsk', + 'Asia/Ulaanbaatar', + 'Asia/Urumqi', + 'Asia/Ust-Nera', + 'Asia/Vientiane', + 'Asia/Vladivostok', + 'Asia/Yakutsk', + 'Asia/Yekaterinburg', + 'Asia/Yerevan', + 'Atlantic/Azores', + 'Atlantic/Bermuda', + 'Atlantic/Canary', + 'Atlantic/Cape_Verde', + 'Atlantic/Faeroe', + 'Atlantic/Madeira', + 'Atlantic/Reykjavik', + 'Atlantic/South_Georgia', + 'Atlantic/St_Helena', + 'Atlantic/Stanley', + 'Australia/Adelaide', + 'Australia/Brisbane', + 'Australia/Broken_Hill', + 'Australia/Darwin', + 'Australia/Eucla', + 'Australia/Hobart', + 'Australia/Lindeman', + 'Australia/Lord_Howe', + 'Australia/Melbourne', + 'Australia/Perth', + 'Australia/Sydney', + 'Europe/Amsterdam', + 'Europe/Andorra', + 'Europe/Astrakhan', + 'Europe/Athens', + 'Europe/Belgrade', + 'Europe/Berlin', + 'Europe/Bratislava', + 'Europe/Brussels', + 'Europe/Bucharest', + 'Europe/Budapest', + 'Europe/Busingen', + 'Europe/Chisinau', + 'Europe/Copenhagen', + 'Europe/Dublin', + 'Europe/Gibraltar', + 'Europe/Guernsey', + 'Europe/Helsinki', + 'Europe/Isle_of_Man', + 'Europe/Istanbul', + 'Europe/Jersey', + 'Europe/Kaliningrad', + 'Europe/Kiev', + 'Europe/Kirov', + 'Europe/Lisbon', + 'Europe/Ljubljana', + 'Europe/London', + 'Europe/Luxembourg', + 'Europe/Madrid', + 'Europe/Malta', + 'Europe/Mariehamn', + 'Europe/Minsk', + 'Europe/Monaco', + 'Europe/Moscow', + 'Europe/Oslo', + 'Europe/Paris', + 'Europe/Podgorica', + 'Europe/Prague', + 'Europe/Riga', + 'Europe/Rome', + 'Europe/Samara', + 'Europe/San_Marino', + 'Europe/Sarajevo', + 'Europe/Saratov', + 'Europe/Simferopol', + 'Europe/Skopje', + 'Europe/Sofia', + 'Europe/Stockholm', + 'Europe/Tallinn', + 'Europe/Tirane', + 'Europe/Ulyanovsk', + 'Europe/Vaduz', + 'Europe/Vatican', + 'Europe/Vienna', + 'Europe/Vilnius', + 'Europe/Volgograd', + 'Europe/Warsaw', + 'Europe/Zagreb', + 'Europe/Zurich', + 'Indian/Antananarivo', + 'Indian/Chagos', + 'Indian/Christmas', + 'Indian/Cocos', + 'Indian/Comoro', + 'Indian/Kerguelen', + 'Indian/Mahe', + 'Indian/Maldives', + 'Indian/Mauritius', + 'Indian/Mayotte', + 'Indian/Reunion', + 'Pacific/Apia', + 'Pacific/Auckland', + 'Pacific/Bougainville', + 'Pacific/Chatham', + 'Pacific/Easter', + 'Pacific/Efate', + 'Pacific/Enderbury', + 'Pacific/Fakaofo', + 'Pacific/Fiji', + 'Pacific/Funafuti', + 'Pacific/Galapagos', + 'Pacific/Gambier', + 'Pacific/Guadalcanal', + 'Pacific/Guam', + 'Pacific/Honolulu', + 'Pacific/Kiritimati', + 'Pacific/Kosrae', + 'Pacific/Kwajalein', + 'Pacific/Majuro', + 'Pacific/Marquesas', + 'Pacific/Midway', + 'Pacific/Nauru', + 'Pacific/Niue', + 'Pacific/Norfolk', + 'Pacific/Noumea', + 'Pacific/Pago_Pago', + 'Pacific/Palau', + 'Pacific/Pitcairn', + 'Pacific/Ponape', + 'Pacific/Port_Moresby', + 'Pacific/Rarotonga', + 'Pacific/Saipan', + 'Pacific/Tahiti', + 'Pacific/Tarawa', + 'Pacific/Tongatapu', + 'Pacific/Truk', + 'Pacific/Wake', + 'Pacific/Wallis' +]; diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index 69735a3..f47d9ea 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -1,3 +1,5 @@ +import { VALID_TIMEZONES } from './dateUtils'; + export type User = { pk: number; username: string; @@ -30,6 +32,7 @@ export type Adventure = { id: string; start_date: string; end_date: string; + timezone: string | null; notes: string; }[]; collection?: string | null; @@ -42,6 +45,9 @@ export type Adventure = { category: Category | null; attachments: Attachment[]; user?: User | null; + city?: string | null; + region?: string | null; + country?: string | null; }; export type AdditionalAdventure = Adventure & { @@ -160,6 +166,8 @@ export type Transportation = { link: string | null; date: string | null; // ISO 8601 date string end_date: string | null; // ISO 8601 date string + start_timezone: string | null; + end_timezone: string | null; flight_number: string | null; from_location: string | null; to_location: string | null; @@ -288,6 +296,7 @@ export type Lodging = { link: string | null; check_in: string | null; // ISO 8601 date string check_out: string | null; // ISO 8601 date string + timezone: string | null; reservation_number: string | null; price: number | null; latitude: number | null; diff --git a/frontend/src/locales/de.json b/frontend/src/locales/de.json index 13a02ad..10d58c2 100644 --- a/frontend/src/locales/de.json +++ b/frontend/src/locales/de.json @@ -252,7 +252,16 @@ "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" + "ordered_itinerary": "Reiseroute bestellt", + "additional_info": "Weitere Informationen", + "invalid_date_range": "Ungültiger Datumsbereich", + "sunrise_sunset": "Sonnenaufgang", + "timezone": "Zeitzone", + "no_visits": "Keine Besuche", + "arrival_timezone": "Ankunftszeitzone", + "departure_timezone": "Abfahrtszeit", + "arrival_date": "Ankunftsdatum", + "departure_date": "Abflugdatum" }, "home": { "desc_1": "Entdecken, planen und erkunden Sie mühelos", diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json index 65c7288..024d499 100644 --- a/frontend/src/locales/en.json +++ b/frontend/src/locales/en.json @@ -62,6 +62,13 @@ "collection_remove_success": "Adventure removed from collection successfully!", "collection_remove_error": "Error removing adventure from collection", "collection_link_success": "Adventure linked to collection successfully!", + "invalid_date_range": "Invalid date range", + "timezone": "Timezone", + "no_visits": "No visits", + "departure_timezone": "Departure Timezone", + "arrival_timezone": "Arrival Timezone", + "departure_date": "Departure Date", + "arrival_date": "Arrival Date", "no_image_found": "No image found", "collection_link_error": "Error linking adventure to collection", "adventure_delete_confirm": "Are you sure you want to delete this adventure? This action cannot be undone.", @@ -132,7 +139,7 @@ "location": "Location", "search_for_location": "Search for a location", "clear_map": "Clear map", - "search_results": "Searh results", + "search_results": "Search results", "collection_no_start_end_date": "Adding a start and end date to the collection will unlock itinerary planning features in the collection page.", "no_results": "No results found", "wiki_desc": "Pulls excerpt from Wikipedia article matching the name of the adventure.", diff --git a/frontend/src/locales/es.json b/frontend/src/locales/es.json index c82df3f..a41d186 100644 --- a/frontend/src/locales/es.json +++ b/frontend/src/locales/es.json @@ -300,7 +300,16 @@ "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" + "ordered_itinerary": "Itinerario ordenado", + "additional_info": "información adicional", + "invalid_date_range": "Rango de fechas no válido", + "sunrise_sunset": "Amanecer", + "timezone": "Zona horaria", + "no_visits": "No hay visitas", + "arrival_timezone": "Zona horaria de llegada", + "departure_timezone": "Zona horaria de salida", + "arrival_date": "Fecha de llegada", + "departure_date": "Fecha de salida" }, "worldtravel": { "all": "Todo", diff --git a/frontend/src/locales/fr.json b/frontend/src/locales/fr.json index 249243d..fce6ede 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,36 @@ "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é", + "additional_info": "Informations Complémentaires", + "invalid_date_range": "Plage de dates non valide", + "sunrise_sunset": "Lever du soleil", + "timezone": "Fuseau horaire", + "no_visits": "Pas de visites", + "arrival_timezone": "Fuseau horaire d'arrivée", + "departure_timezone": "Fuseau horaire de départ", + "arrival_date": "Date d'arrivée", + "departure_date": "Date de départ" }, "home": { "desc_1": "Découvrez, planifiez et explorez en toute simplicité", @@ -272,7 +281,7 @@ "about": "À propos de AdventureLog", "adventures": "Aventures", "collections": "Collections", - "discord": "Discorde", + "discord": "Discord", "documentation": "Documentation", "greeting": "Salut", "logout": "Déconnexion", @@ -285,12 +294,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 +312,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 +326,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 +366,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 +394,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 +482,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 +494,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 +508,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 +542,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 +572,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 +580,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 +601,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 +613,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" } diff --git a/frontend/src/locales/it.json b/frontend/src/locales/it.json index 1a77839..60f15d4 100644 --- a/frontend/src/locales/it.json +++ b/frontend/src/locales/it.json @@ -252,7 +252,16 @@ "collection_no_start_end_date": "L'aggiunta di una data di inizio e fine alla collezione sbloccherà le funzionalità di pianificazione dell'itinerario nella pagina della collezione.", "date_itinerary": "Data dell'itinerario", "no_ordered_items": "Aggiungi elementi con date alla collezione per vederli qui.", - "ordered_itinerary": "Itinerario ordinato" + "ordered_itinerary": "Itinerario ordinato", + "additional_info": "Ulteriori informazioni", + "invalid_date_range": "Intervallo di date non valido", + "sunrise_sunset": "Alba", + "timezone": "Fuso orario", + "no_visits": "Nessuna visita", + "arrival_timezone": "Fuso orario di arrivo", + "departure_timezone": "Fuso orario di partenza", + "arrival_date": "Data di arrivo", + "departure_date": "Data di partenza" }, "home": { "desc_1": "Scopri, pianifica ed esplora con facilità", @@ -632,4 +641,4 @@ "motel": "Motel", "current_timezone": "Fuso orario attuale" } -} \ No newline at end of file +} diff --git a/frontend/src/locales/ko.json b/frontend/src/locales/ko.json index dda6962..409c3dd 100644 --- a/frontend/src/locales/ko.json +++ b/frontend/src/locales/ko.json @@ -252,7 +252,16 @@ "collection_no_start_end_date": "컬렉션에 시작 및 종료 날짜를 추가하면 컬렉션 페이지에서 여정 계획 기능이 잠금 해제됩니다.", "date_itinerary": "날짜 일정", "no_ordered_items": "컬렉션에 날짜가있는 항목을 추가하여 여기에서 확인하십시오.", - "ordered_itinerary": "주문한 여정" + "ordered_itinerary": "주문한 여정", + "additional_info": "추가 정보", + "invalid_date_range": "잘못된 날짜 범위", + "sunrise_sunset": "해돋이", + "timezone": "시간대", + "no_visits": "방문 없음", + "arrival_timezone": "도착 시간대", + "departure_timezone": "출발 시간대", + "arrival_date": "도착 날짜", + "departure_date": "출발 날짜" }, "auth": { "both_passwords_required": "두 암호 모두 필요합니다", diff --git a/frontend/src/locales/nl.json b/frontend/src/locales/nl.json index 7cb51dc..2795271 100644 --- a/frontend/src/locales/nl.json +++ b/frontend/src/locales/nl.json @@ -252,7 +252,16 @@ "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" + "ordered_itinerary": "Besteld reisschema", + "additional_info": "Aanvullende informatie", + "invalid_date_range": "Ongeldige datumbereik", + "sunrise_sunset": "Zonsopgang", + "timezone": "Tijdzone", + "no_visits": "Geen bezoeken", + "arrival_timezone": "Aankomsttijdzone", + "departure_timezone": "Vertrektijdzone", + "arrival_date": "Aankomstdatum", + "departure_date": "Vertrekdatum" }, "home": { "desc_1": "Ontdek, plan en verken met gemak", diff --git a/frontend/src/locales/no.json b/frontend/src/locales/no.json index 8b49218..0239283 100644 --- a/frontend/src/locales/no.json +++ b/frontend/src/locales/no.json @@ -1,631 +1,644 @@ { - "navbar": { - "adventures": "Eventyr", - "collections": "Samlinger", - "worldtravel": "Verdensreiser", - "map": "Kart", - "users": "Brukere", - "search": "Søk", - "profile": "Profil", - "greeting": "Hei", - "my_adventures": "Mine Eventyr", - "my_tags": "Mine Tags", - "tag": "Tag", - "shared_with_me": "Delt med meg", - "settings": "Innstillinger", - "logout": "Logg ut", - "about": "Om AdventureLog", - "documentation": "Dokumentasjon", - "discord": "Discord", - "language_selection": "Språk", - "support": "Støtte", - "calendar": "Kalender", - "theme_selection": "Tema-valg", - "admin_panel": "Admin Panel", - "themes": { - "light": "Lyst", - "dark": "Mørkt", - "night": "Natt", - "forest": "Skog", - "aestheticLight": "Estetisk Lyst", - "aestheticDark": "Estetisk Mørkt", - "aqua": "Aqua", - "northernLights": "Nordlys" - } - }, - "about": { - "about": "Om", - "license": "Lisensiert under GPL-3.0-lisensen.", - "source_code": "Kildekode", - "message": "Laget med ❤️ i USA.", - "oss_attributions": "Open Source-attribusjoner", - "nominatim_1": "Stedsøk og geokoding leveres av", - "nominatim_2": "Deres data er lisensiert under ODbL-lisensen.", - "other_attributions": "Ytterligere attribusjoner finnes i README-filen.", - "close": "Lukk" - }, - "home": { - "hero_1": "Oppdag verdens mest spennende eventyr", - "hero_2": "Oppdag og planlegg ditt neste eventyr med AdventureLog. Utforsk fantastiske destinasjoner, lag tilpassede reiseplaner, og hold kontakten på farten.", - "go_to": "Gå til AdventureLog", - "key_features": "Nøkkelfunksjoner", - "desc_1": "Oppdag, planlegg og utforsk med letthet", - "desc_2": "AdventureLog er designet for å forenkle reisen din, og gir deg verktøy og ressurser til å planlegge, pakke og navigere ditt neste uforglemmelige eventyr.", - "feature_1": "Reiselogg", - "feature_1_desc": "Før en personlig reiselogg for eventyrene dine og del opplevelsene dine med venner og familie.", - "feature_2": "Reiseplanlegging", - "feature_2_desc": "Lag enkelt tilpassede reiseplaner og få en dag-for-dag oversikt over turen din.", - "feature_3": "Reisekart", - "feature_3_desc": "Se reisene dine over hele verden med et interaktivt kart og utforsk nye destinasjoner." - }, - "adventures": { - "collection_remove_success": "Eventyret ble fjernet fra samlingen!", - "collection_remove_error": "Feil ved fjerning av eventyr fra samling", - "collection_link_success": "Eventyret ble lagt til samlingen!", - "no_image_found": "Ingen bilde funnet", - "collection_link_error": "Feil ved lenking av eventyr til samling", - "adventure_delete_confirm": "Er du sikker på at du vil slette dette eventyret? Denne handlingen kan ikke angres.", - "checklist_delete_confirm": "Er du sikker på at du vil slette denne sjekklisten? Denne handlingen kan ikke angres.", - "note_delete_confirm": "Er du sikker på at du vil slette dette notatet? Denne handlingen kan ikke angres.", - "transportation_delete_confirm": "Er du sikker på at du vil slette dette transportmiddelet? Denne handlingen kan ikke angres.", - "lodging_delete_confirm": "Er du sikker på at du vil slette dette overnattingsstedet? Denne handlingen kan ikke angres.", - "delete_checklist": "Slett sjekkliste", - "delete_note": "Slett notat", - "delete_transportation": "Slett transport", - "delete_lodging": "Slett overnatting", - "open_details": "Åpne detaljer", - "edit_adventure": "Rediger eventyr", - "remove_from_collection": "Fjern fra samling", - "add_to_collection": "Legg til i samling", - "delete": "Slett", - "not_found": "Fant ikke eventyret", - "not_found_desc": "Eventyret du leter etter, ble ikke funnet. Vennligst prøv et annet eventyr eller kom tilbake senere.", - "homepage": "Hjemmeside", - "adventure_details": "Eventyrdetaljer", - "collection": "Samling", - "adventure_type": "Eventyrtype", - "longitude": "Lengdegrad", - "latitude": "Breddegrad", - "visit": "Besøk", - "visits": "Besøk", - "create_new": "Opprett nytt...", - "adventure": "Eventyr", - "count_txt": "resultater som samsvarer med søket ditt", - "sort": "Sorter", - "order_by": "Sorter etter", - "order_direction": "Sorteringsretning", - "ascending": "Stigende", - "descending": "Synkende", - "updated": "Oppdatert", - "name": "Navn", - "date": "Dato", - "activity_types": "Aktivitetstyper", - "tags": "Tags", - "add_a_tag": "Legg til en tag", - "date_constrain": "Begrens til samlingsdatoer", - "rating": "Vurdering", - "my_images": "Mine bilder", - "add_an_activity": "Legg til en aktivitet", - "show_region_labels": "Vis regionetiketter", - "no_images": "Ingen bilder", - "upload_images_here": "Last opp bilder her", - "share_adventure": "Del dette eventyret!", - "copy_link": "Kopier lenke", - "image": "Bilde", - "upload_image": "Last opp bilde", - "open_in_maps": "Åpne i kart", - "url": "URL", - "fetch_image": "Hent bilde", - "wikipedia": "Wikipedia", - "add_notes": "Legg til notater", - "warning": "Advarsel", - "my_adventures": "Mine eventyr", - "no_linkable_adventures": "Ingen eventyr funnet som kan legges til denne samlingen.", - "add": "Legg til", - "save_next": "Lagre og fortsett", - "end_date": "Sluttdato", - "my_visits": "Mine besøk", - "start_date": "Startdato", - "remove": "Fjern", - "location": "Plassering", - "search_for_location": "Søk etter sted", - "clear_map": "Tøm kart", - "search_results": "Søkeresultater", - "no_results": "Ingen resultater funnet", - "wiki_desc": "Henter utdrag fra Wikipedia-artikkelen som samsvarer med navnet på eventyret.", - "attachments": "Vedlegg", - "attachment": "Vedlegg", - "images": "Bilder", - "primary": "Primær", - "view_attachment": "Vis vedlegg", - "generate_desc": "Generer beskrivelse", - "public_adventure": "Offentlig eventyr", - "location_information": "Plasseringsinformasjon", - "link": "Lenke", - "links": "Lenker", - "description": "Beskrivelse", - "sources": "Kilder", - "collection_adventures": "Inkluder eventyr i samlinger", - "filter": "Filter", - "category_filter": "Kategorifilter", - "category": "Kategori", - "select_adventure_category": "Velg eventyrkategori", - "clear": "Tøm", - "my_collections": "Mine samlinger", - "open_filters": "Åpne filtre", - "close_filters": "Lukk filtre", - "archived_collections": "Arkiverte samlinger", - "share": "Del", - "private": "Privat", - "public": "Offentlig", - "archived": "Arkivert", - "edit_collection": "Rediger samling", - "unarchive": "Fjern fra arkiv", - "archive": "Arkiver", - "no_collections_found": "Ingen samlinger funnet for å legge dette eventyret til.", - "not_visited": "Ikke besøkt", - "archived_collection_message": "Samlingen ble arkivert!", - "unarchived_collection_message": "Samlingen ble fjernet fra arkivet!", - "delete_collection_success": "Samlingen ble slettet!", - "delete_collection_warning": "Er du sikker på at du vil slette denne samlingen? Dette vil også slette alle lenkede eventyr. Denne handlingen kan ikke angres.", - "cancel": "Avbryt", - "of": "av", - "delete_collection": "Slett samling", - "delete_adventure": "Slett eventyr", - "adventure_delete_success": "Eventyret ble slettet!", - "visited": "Besøkt", - "planned": "Planlagt", - "duration": "Varighet", - "all": "Alle", - "image_removed_success": "Bilde ble fjernet!", - "image_removed_error": "Feil ved fjerning av bilde", - "no_image_url": "Finner ikke bilde på den oppgitte URL-en.", - "image_upload_success": "Bilde opplastet!", - "image_upload_error": "Feil ved opplasting av bilde", - "dates": "Datoer", - "wiki_image_error": "Feil ved henting av bilde fra Wikipedia", - "start_before_end_error": "Startdato må være før sluttdato", - "activity": "Aktivitet", - "actions": "Handlinger", - "no_end_date": "Vennligst angi en sluttdato", - "see_adventures": "Se eventyr", - "image_fetch_failed": "Kunne ikke hente bilde", - "no_location": "Vennligst angi et sted", - "no_start_date": "Vennligst angi en startdato", - "no_description_found": "Fant ingen beskrivelse", - "adventure_created": "Eventyr opprettet", - "adventure_create_error": "Kunne ikke opprette eventyr", - "lodging": "Overnatting", - "create_adventure": "Opprett eventyr", - "adventure_updated": "Eventyr oppdatert", - "adventure_update_error": "Kunne ikke oppdatere eventyr", - "set_to_pin": "Fest", - "category_fetch_error": "Feil ved henting av kategorier", - "new_adventure": "Nytt eventyr", - "basic_information": "Grunnleggende informasjon", - "no_adventures_to_recommendations": "Ingen eventyr funnet. Legg til minst ett eventyr for å få anbefalinger.", - "display_name": "Visningsnavn", - "adventure_not_found": "Det finnes ingen eventyr å vise. Legg til noen ved å trykke på plusstegnet nederst til høyre, eller prøv å endre filtre!", - "no_adventures_found": "Ingen eventyr funnet", - "mark_region_as_visited": "Merk regionen {region}, {country} som besøkt?", - "mark_visited": "Merk som besøkt", - "error_updating_regions": "Feil ved oppdatering av regioner", - "regions_updated": "regioner oppdatert", - "cities_updated": "byer oppdatert", - "visited_region_check": "Sjekk besøkte regioner", - "visited_region_check_desc": "Ved å markere denne, vil serveren sjekke alle dine besøkte eventyr og markere regionene de befinner seg i som besøkt i verdensreiser.", - "update_visited_regions": "Oppdater besøkte regioner", - "update_visited_regions_disclaimer": "Dette kan ta litt tid avhengig av hvor mange eventyr du har besøkt.", - "link_new": "Lenk ny...", - "add_new": "Legg til ny...", - "transportation": "Transport", - "note": "Notat", - "checklist": "Sjekkliste", - "collection_archived": "Denne samlingen er arkivert.", - "visit_link": "Besøk lenke", - "collection_completed": "Du har fullført denne samlingen!", - "collection_stats": "Samlingsstatistikk", - "keep_exploring": "Fortsett å utforske!", - "linked_adventures": "Lenkede eventyr", - "notes": "Notater", - "checklists": "Sjekklister", - "transportations": "Transportmidler", - "adventure_calendar": "Eventyrkalender", - "day": "Dag", - "itineary_by_date": "Reiseplan etter dato", - "nothing_planned": "Ingenting planlagt denne dagen. Nyt reisen!", - "copied_to_clipboard": "Kopiert til utklippstavlen!", - "copy_failed": "Kopiering mislyktes", - "show": "Vis", - "hide": "Skjul", - "clear_location": "Fjern sted", - "starting_airport": "Avreiseflyplass", - "ending_airport": "Ankomsflyplass", - "no_location_found": "Ingen sted funnet", - "from": "Fra", - "to": "Til", - "will_be_marked": "vil bli markert som besøkt når eventyret er lagret.", - "start": "Start", - "end": "Slutt", - "show_map": "Vis kart", - "emoji_picker": "Emoji-velger", - "download_calendar": "Last ned kalender", - "date_information": "Dato-informasjon", - "flight_information": "Flyinformasjon", - "out_of_range": "Ikke i reiseplandatoer", - "preview": "Forhåndsvisning", - "finding_recommendations": "Oppdager skjulte perler for ditt neste eventyr", - "location_details": "Stedsdetaljer", - "city": "By", - "region": "Region", - "md_instructions": "Skriv markdown her...", - "days": "dager", - "attachment_upload_success": "Vedlegg lastet opp!", - "attachment_upload_error": "Feil ved opplasting av vedlegg", - "upload": "Last opp", - "attachment_delete_success": "Vedlegg slettet!", - "attachment_update_success": "Vedlegg oppdatert!", - "attachment_name": "Vedleggsnavn", - "gpx_tip": "Last opp GPX-filer i vedlegg for å se dem på kartet!", - "welcome_map_info": "Offentlige eventyr på denne serveren", - "attachment_update_error": "Feil ved oppdatering av vedlegg", - "activities": { - "general": "Generelt 🌍", - "outdoor": "Utendørs 🏞️", - "lodging": "Overnatting 🛌", - "dining": "Servering 🍽️", - "activity": "Aktivitet 🏄", - "attraction": "Attraksjon 🎢", - "shopping": "Shopping 🛍️", - "nightlife": "Uteliv 🌃", - "event": "Arrangement 🎉", - "transportation": "Transport 🚗", - "culture": "Kultur 🎭", - "water_sports": "Vannsport 🚤", - "hiking": "Fotturer 🥾", - "wildlife": "Dyreliv 🦒", - "historical_sites": "Historiske steder 🏛️", - "music_concerts": "Musikk og konserter 🎶", - "fitness": "Trening 🏋️", - "art_museums": "Kunst og museer 🎨", - "festivals": "Festivaler 🎪", - "spiritual_journeys": "Spirituelle reiser 🧘‍♀️", - "volunteer_work": "Frivillig arbeid 🤝", - "other": "Annet" - }, - "lodging_information": "Overnattingsinformasjon", - "price": "Pris", - "reservation_number": "Reservasjonsnummer" - }, - "worldtravel": { - "country_list": "Liste over land", - "num_countries": "land funnet", - "all": "Alle", - "partially_visited": "Delvis besøkt", - "not_visited": "Ikke besøkt", - "completely_visited": "Fullstendig besøkt", - "all_subregions": "Alle underregioner", - "clear_search": "Tøm søk", - "no_countries_found": "Ingen land funnet", - "view_cities": "Vis byer", - "no_cities_found": "Ingen byer funnet", - "visit_to": "Besøk i", - "region_failed_visited": "Kunne ikke markere region som besøkt", - "failed_to_mark_visit": "Kunne ikke markere besøk i", - "visit_remove_failed": "Kunne ikke fjerne besøk", - "removed": "fjernet", - "failed_to_remove_visit": "Kunne ikke fjerne besøk i", - "marked_visited": "markert som besøkt", - "regions_in": "Regioner i", - "region_stats": "Regionstatistikk", - "all_visited": "Du har besøkt alle regionene i", - "cities": "byer" - }, - "auth": { - "username": "Brukernavn", - "password": "Passord", - "forgot_password": "Glemt passord?", - "signup": "Registrer deg", - "login_error": "Kan ikke logge inn med oppgitte legitimasjon.", - "login": "Logg inn", - "email": "E-post", - "first_name": "Fornavn", - "last_name": "Etternavn", - "confirm_password": "Bekreft passord", - "registration_disabled": "Registrering er for øyeblikket deaktivert.", - "profile_picture": "Profilbilde", - "public_profile": "Offentlig profil", - "public_tooltip": "Med en offentlig profil kan brukere dele samlinger med deg og se profilen din på brukersiden.", - "email_required": "E-post kreves", - "new_password": "Nytt passord (6+ tegn)", - "both_passwords_required": "Begge passord er påkrevd", - "reset_failed": "Kunne ikke tilbakestille passord", - "or_3rd_party": "Eller logg inn med en tredjepartstjeneste", - "no_public_adventures": "Ingen offentlige eventyr funnet", - "no_public_collections": "Ingen offentlige samlinger funnet", - "user_adventures": "Brukerens eventyr", - "user_collections": "Brukerens samlinger" - }, - "users": { - "no_users_found": "Ingen brukere med offentlig profil funnet." - }, - "settings": { - "update_error": "Feil ved oppdatering av innstillinger", - "update_success": "Innstillinger oppdatert!", - "settings_page": "Innstillingsside", - "account_settings": "Brukerkontoinnstillinger", - "update": "Oppdater", - "no_verified_email_warning": "Du må ha en verifisert e-postadresse for å aktivere tofaktorautentisering.", - "password_change": "Bytt passord", - "new_password": "Nytt passord", - "confirm_new_password": "Bekreft nytt passord", - "email_change": "Bytt e-post", - "current_email": "Nåværende e-post", - "no_email_set": "Ingen e-post angitt", - "new_email": "Ny e-post", - "change_password": "Bytt passord", - "login_redir": "Du blir da omdirigert til innloggingssiden.", - "token_required": "Token og UID kreves for tilbakestilling av passord.", - "reset_password": "Tilbakestill passord", - "possible_reset": "Hvis e-postadressen du oppga er knyttet til en konto, vil du motta en e-post med instruksjoner om å tilbakestille passordet ditt!", - "missing_email": "Vennligst skriv inn en e-postadresse", - "submit": "Send inn", - "password_does_not_match": "Passordene samsvarer ikke", - "password_is_required": "Passord er påkrevd", - "invalid_token": "Token er ugyldig eller utløpt", - "about_this_background": "Om denne bakgrunnen", - "photo_by": "Foto av", - "join_discord": "Bli med på Discord", - "join_discord_desc": "for å dele dine egne bilder. Legg dem ut i #travel-share-kanalen.", - "current_password": "Nåværende passord", - "change_password_error": "Kan ikke endre passord. Ugyldig nåværende passord eller ugyldig nytt passord.", - "password_change_lopout_warning": "Du vil bli logget ut etter å ha endret passordet.", - "generic_error": "En feil oppsto under behandlingen av forespørselen din.", - "email_removed": "E-post fjernet!", - "email_removed_error": "Feil ved fjerning av e-post", - "verify_email_success": "E-postbekreftelse sendt!", - "verify_email_error": "Feil ved e-postbekreftelse. Prøv igjen om noen minutter.", - "email_added": "E-post lagt til!", - "email_added_error": "Feil ved legging til e-post", - "email_set_primary": "E-post satt som primær!", - "email_set_primary_error": "Feil ved innstilling av primær e-post", - "verified": "Verifisert", - "primary": "Primær", - "not_verified": "Ikke verifisert", - "make_primary": "Gjør til primær", - "verify": "Verifiser", - "no_emai_set": "Ingen e-post angitt", - "error_change_password": "Feil ved endring av passord. Sjekk ditt nåværende passord og prøv igjen.", - "mfa_disabled": "Tofaktorautentisering er deaktivert!", - "mfa_page_title": "Tofaktorautentisering", - "enable_mfa": "Aktiver MFA", - "disable_mfa": "Deaktiver MFA", - "mfa_not_enabled": "MFA er ikke aktivert", - "mfa_enabled": "Tofaktorautentisering er aktivert!", - "copy": "Kopier", - "recovery_codes": "Gjenopprettingskoder", - "recovery_codes_desc": "Dette er dine gjenopprettingskoder. Oppbevar dem trygt. Du vil ikke kunne se dem igjen.", - "reset_session_error": "Logg ut og logg inn igjen for å oppdatere økten din, og prøv igjen.", - "authenticator_code": "Autentiseringskode", - "email_verified": "E-post verifisert!", - "email_verified_success": "E-posten din er verifisert. Du kan nå logge inn.", - "email_verified_error": "Feil ved verifisering av e-post", - "email_verified_erorr_desc": "E-posten din kunne ikke verifiseres. Vennligst prøv igjen.", - "invalid_code": "Ugyldig MFA-kode", - "invalid_credentials": "Ugyldig brukernavn eller passord", - "mfa_required": "Tofaktorautentisering er påkrevd", - "required": "Dette feltet er påkrevd", - "add_email_blocked": "Du kan ikke legge til en e-postadresse på en konto som er beskyttet av tofaktorautentisering.", - "duplicate_email": "Denne e-postadressen er allerede i bruk.", - "csrf_failed": "Kunne ikke hente CSRF-token", - "email_taken": "Denne e-postadressen er allerede i bruk.", - "username_taken": "Dette brukernavnet er allerede i bruk.", - "administration_settings": "Administrasjonsinnstillinger", - "launch_administration_panel": "Åpne administrasjonspanelet", - "social_oidc_auth": "Social og OIDC-autentisering", - "social_auth_desc": "Aktiver eller deaktiver sosiale og OIDC-autentiseringsleverandører for kontoen din. Disse koblingene lar deg logge inn med selvhostede autentiseringstjenester som Authentik eller tredjepartsleverandører som GitHub.", - "social_auth_desc_2": "Disse innstillingene administreres på AdventureLog-serveren og må aktiveres manuelt av administratoren.", - "documentation_link": "Dokumentasjonslenke", - "launch_account_connections": "Åpne kontotilkoblinger", - "password_too_short": "Passordet må være minst 6 tegn", - "add_email": "Legg til e-post", - "password_disable": "Deaktiver passordautentisering", - "password_disable_desc": "Å deaktivere passordautentisering vil hindre deg fra å logge inn med et passord. Du må bruke en sosial eller OIDC-leverandør for å logge inn. Skulle leverandøren din fjernes, vil passordautentisering automatisk bli gjenaktivert, selv om denne innstillingen er deaktivert.", - "disable_password": "Deaktiver passord", - "password_enabled": "Passordautentisering er aktivert", - "password_disabled": "Passordautentisering er deaktivert", - "password_disable_warning": "Akkurat nå er passordautentisering deaktivert. Innlogging via en sosial eller OIDC-leverandør er påkrevd.", - "password_disabled_error": "Feil ved deaktivering av passordautentisering. Sørg for at en sosial eller OIDC-leverandør er koblet til kontoen din.", - "password_enabled_error": "Feil ved aktivering av passordautentisering." - }, - "collection": { - "collection_created": "Samling opprettet!", - "error_creating_collection": "Feil ved oppretting av samling", - "new_collection": "Ny samling", - "create": "Opprett", - "collection_edit_success": "Samling redigert!", - "error_editing_collection": "Feil ved redigering av samling", - "edit_collection": "Rediger samling", - "public_collection": "Offentlig samling" - }, - "notes": { - "note_deleted": "Notat slettet!", - "note_delete_error": "Feil ved sletting av notat", - "open": "Åpne", - "failed_to_save": "Kunne ikke lagre notat", - "note_editor": "Notatredigerer", - "note_viewer": "Notatviser", - "editing_note": "Redigerer notat", - "content": "Innhold", - "save": "Lagre", - "note_public": "Dette notatet er offentlig fordi det er i en offentlig samling.", - "add_a_link": "Legg til en lenke", - "invalid_url": "Ugyldig URL" - }, - "checklist": { - "checklist_deleted": "Sjekkliste slettet!", - "checklist_delete_error": "Feil ved sletting av sjekkliste", - "failed_to_save": "Kunne ikke lagre sjekkliste", - "checklist_editor": "Sjekklisteredigerer", - "checklist_viewer": "Sjekklisteviser", - "editing_checklist": "Redigerer sjekkliste", - "new_checklist": "Ny sjekkliste", - "item": "Punkt", - "items": "Punkter", - "add_item": "Legg til punkt", - "new_item": "Nytt punkt", - "save": "Lagre", - "checklist_public": "Denne sjekklisten er offentlig fordi den er i en offentlig samling.", - "item_cannot_be_empty": "Punktet kan ikke være tomt", - "item_already_exists": "Punktet finnes allerede" - }, - "transportation": { - "transportation_deleted": "Transport slettet!", - "transportation_delete_error": "Feil ved sletting av transport", - "provide_start_date": "Vennligst angi en startdato", - "transport_type": "Transporttype", - "type": "Type", - "transportation_added": "Transport lagt til!", - "error_editing_transportation": "Feil ved redigering av transport", - "new_transportation": "Ny transport", - "date_time": "Startdato og -tid", - "end_date_time": "Sluttdato og -tid", - "flight_number": "Flynummer", - "from_location": "Fra sted", - "to_location": "Til sted", - "fetch_location_information": "Hent stedsinformasjon", - "starting_airport_desc": "Skriv inn avreiseflyplasskode (f.eks. JFK)", - "ending_airport_desc": "Skriv inn ankomsflyplasskode (f.eks. LAX)", - "edit": "Rediger", - "modes": { - "car": "Bil", - "plane": "Fly", - "train": "Tog", - "bus": "Buss", - "boat": "Båt", - "bike": "Sykkel", - "walking": "Går", - "other": "Annet" - }, - "transportation_edit_success": "Transport redigert!", - "edit_transportation": "Rediger transport", - "start": "Start", - "date_and_time": "Dato og tid" - }, - "lodging": { - "lodging_deleted": "Overnatting slettet!", - "lodging_delete_error": "Feil ved sletting av overnatting", - "provide_start_date": "Vennligst angi en startdato", - "lodging_type": "Overnattingstype", - "type": "Type", - "lodging_added": "Overnatting lagt til!", - "error_editing_lodging": "Feil ved redigering av overnatting", - "new_lodging": "Ny overnatting", - "check_in": "Innsjekking", - "check_out": "Utsjekking", - "edit": "Rediger", - "lodging_edit_success": "Overnatting redigert!", - "edit_lodging": "Rediger overnatting", - "start": "Start", - "date_and_time": "Dato og tid", - "hotel": "Hotell", - "hostel": "Hostell", - "resort": "Resort", - "bnb": "Bed & Breakfast", - "campground": "Campingplass", - "cabin": "Hytte", - "apartment": "Leilighet", - "house": "Hus", - "villa": "Villa", - "motel": "Motell", - "other": "Annet", - "reservation_number": "Reservasjonsnummer", - "current_timezone": "Gjeldende tidssone" - }, - "search": { - "adventurelog_results": "AdventureLog-resultater", - "public_adventures": "Offentlige eventyr", - "online_results": "Nettresultater" - }, - "map": { - "view_details": "Vis detaljer", - "adventure_map": "Eventyrkart", - "map_options": "Kartalternativer", - "show_visited_regions": "Vis besøkte regioner", - "add_adventure_at_marker": "Legg til nytt eventyr ved markøren", - "clear_marker": "Fjern markør", - "add_adventure": "Legg til nytt eventyr" - }, - "share": { - "shared": "Delt", - "with": "med", - "unshared": "Udelt", - "share_desc": "Del denne samlingen med andre brukere.", - "shared_with": "Delt med", - "no_users_shared": "Ingen brukere delt med", - "not_shared_with": "Ikke delt med", - "no_shared_found": "Ingen samlinger funnet som er delt med deg.", - "set_public": "For å la brukere dele med deg, må profilen din være offentlig.", - "go_to_settings": "Gå til innstillinger" - }, - "languages": {}, - "profile": { - "member_since": "Medlem siden", - "user_stats": "Brukerstatistikk", - "visited_countries": "Besøkte land", - "visited_regions": "Besøkte regioner", - "visited_cities": "Besøkte byer" - }, - "categories": { - "manage_categories": "Administrer kategorier", - "no_categories_found": "Ingen kategorier funnet.", - "edit_category": "Rediger kategori", - "icon": "Ikon", - "update_after_refresh": "Eventyrkortene vil oppdateres når du oppdaterer siden.", - "select_category": "Velg kategori", - "category_name": "Kategorinavn" - }, - "dashboard": { - "welcome_back": "Velkommen tilbake", - "countries_visited": "Land besøkt", - "total_adventures": "Totalt antall eventyr", - "total_visited_regions": "Totalt antall besøkte regioner", - "total_visited_cities": "Totalt antall besøkte byer", - "recent_adventures": "Nylige eventyr", - "no_recent_adventures": "Ingen nylige eventyr?", - "add_some": "Hvorfor ikke begynne å planlegge ditt neste eventyr? Du kan legge til et nytt eventyr ved å klikke på knappen nedenfor." - }, - "immich": { - "immich": "Immich", - "integration_fetch_error": "Feil ved henting av data fra Immich-integrasjonen", - "integration_missing": "Immich-integrasjonen mangler på backend", - "query_required": "Forespørsel er påkrevd", - "server_down": "Immich-serveren er nede eller utilgjengelig", - "no_items_found": "Ingen elementer funnet", - "imageid_required": "Bilde-ID er påkrevd", - "load_more": "Last mer", - "immich_updated": "Immich-innstillinger oppdatert!", - "immich_enabled": "Immich-integrasjon aktivert!", - "immich_error": "Feil ved oppdatering av Immich-integrasjon", - "immich_disabled": "Immich-integrasjon deaktivert!", - "immich_desc": "Integrer Immich-kontoen din med AdventureLog for å søke i bildebiblioteket ditt og importere bilder til eventyrene dine.", - "integration_enabled": "Integrasjon aktivert", - "disable": "Deaktiver", - "server_url": "Immich-server-URL", - "api_note": "Merk: dette må være URL-en til Immich API-serveren, så den slutter sannsynligvis med /api, med mindre du har en tilpasset konfig.", - "api_key": "Immich API-nøkkel", - "enable_immich": "Aktiver Immich", - "update_integration": "Oppdater integrasjon", - "immich_integration": "Immich-integrasjon", - "localhost_note": "Merk: localhost vil sannsynligvis ikke fungere med mindre du har satt opp docker-nettverk. Det anbefales å bruke serverens IP-adresse eller domenenavn.", - "documentation": "Immich-integrasjonsdokumentasjon" - }, - "recomendations": { - "address": "Adresse", - "phone": "Telefon", - "contact": "Kontakt", - "website": "Nettsted", - "recommendation": "Anbefaling" - } + "navbar": { + "adventures": "Eventyr", + "collections": "Samlinger", + "worldtravel": "Verdensreiser", + "map": "Kart", + "users": "Brukere", + "search": "Søk", + "profile": "Profil", + "greeting": "Hei", + "my_adventures": "Mine Eventyr", + "my_tags": "Mine Tags", + "tag": "Tag", + "shared_with_me": "Delt med meg", + "settings": "Innstillinger", + "logout": "Logg ut", + "about": "Om AdventureLog", + "documentation": "Dokumentasjon", + "discord": "Discord", + "language_selection": "Språk", + "support": "Støtte", + "calendar": "Kalender", + "theme_selection": "Tema-valg", + "admin_panel": "Admin Panel", + "themes": { + "light": "Lyst", + "dark": "Mørkt", + "night": "Natt", + "forest": "Skog", + "aestheticLight": "Estetisk Lyst", + "aestheticDark": "Estetisk Mørkt", + "aqua": "Aqua", + "northernLights": "Nordlys" + } + }, + "about": { + "about": "Om", + "license": "Lisensiert under GPL-3.0-lisensen.", + "source_code": "Kildekode", + "message": "Laget med ❤️ i USA.", + "oss_attributions": "Open Source-attribusjoner", + "nominatim_1": "Stedsøk og geokoding leveres av", + "nominatim_2": "Deres data er lisensiert under ODbL-lisensen.", + "other_attributions": "Ytterligere attribusjoner finnes i README-filen.", + "close": "Lukk" + }, + "home": { + "hero_1": "Oppdag verdens mest spennende eventyr", + "hero_2": "Oppdag og planlegg ditt neste eventyr med AdventureLog. Utforsk fantastiske destinasjoner, lag tilpassede reiseplaner, og hold kontakten på farten.", + "go_to": "Gå til AdventureLog", + "key_features": "Nøkkelfunksjoner", + "desc_1": "Oppdag, planlegg og utforsk med letthet", + "desc_2": "AdventureLog er designet for å forenkle reisen din, og gir deg verktøy og ressurser til å planlegge, pakke og navigere ditt neste uforglemmelige eventyr.", + "feature_1": "Reiselogg", + "feature_1_desc": "Før en personlig reiselogg for eventyrene dine og del opplevelsene dine med venner og familie.", + "feature_2": "Reiseplanlegging", + "feature_2_desc": "Lag enkelt tilpassede reiseplaner og få en dag-for-dag oversikt over turen din.", + "feature_3": "Reisekart", + "feature_3_desc": "Se reisene dine over hele verden med et interaktivt kart og utforsk nye destinasjoner." + }, + "adventures": { + "collection_remove_success": "Eventyret ble fjernet fra samlingen!", + "collection_remove_error": "Feil ved fjerning av eventyr fra samling", + "collection_link_success": "Eventyret ble lagt til samlingen!", + "no_image_found": "Ingen bilde funnet", + "collection_link_error": "Feil ved lenking av eventyr til samling", + "adventure_delete_confirm": "Er du sikker på at du vil slette dette eventyret? Denne handlingen kan ikke angres.", + "checklist_delete_confirm": "Er du sikker på at du vil slette denne sjekklisten? Denne handlingen kan ikke angres.", + "note_delete_confirm": "Er du sikker på at du vil slette dette notatet? Denne handlingen kan ikke angres.", + "transportation_delete_confirm": "Er du sikker på at du vil slette dette transportmiddelet? Denne handlingen kan ikke angres.", + "lodging_delete_confirm": "Er du sikker på at du vil slette dette overnattingsstedet? Denne handlingen kan ikke angres.", + "delete_checklist": "Slett sjekkliste", + "delete_note": "Slett notat", + "delete_transportation": "Slett transport", + "delete_lodging": "Slett overnatting", + "open_details": "Åpne detaljer", + "edit_adventure": "Rediger eventyr", + "remove_from_collection": "Fjern fra samling", + "add_to_collection": "Legg til i samling", + "delete": "Slett", + "not_found": "Fant ikke eventyret", + "not_found_desc": "Eventyret du leter etter, ble ikke funnet. Vennligst prøv et annet eventyr eller kom tilbake senere.", + "homepage": "Hjemmeside", + "adventure_details": "Eventyrdetaljer", + "collection": "Samling", + "adventure_type": "Eventyrtype", + "longitude": "Lengdegrad", + "latitude": "Breddegrad", + "visit": "Besøk", + "visits": "Besøk", + "create_new": "Opprett nytt...", + "adventure": "Eventyr", + "count_txt": "resultater som samsvarer med søket ditt", + "sort": "Sorter", + "order_by": "Sorter etter", + "order_direction": "Sorteringsretning", + "ascending": "Stigende", + "descending": "Synkende", + "updated": "Oppdatert", + "name": "Navn", + "date": "Dato", + "activity_types": "Aktivitetstyper", + "tags": "Tags", + "add_a_tag": "Legg til en tag", + "date_constrain": "Begrens til samlingsdatoer", + "rating": "Vurdering", + "my_images": "Mine bilder", + "add_an_activity": "Legg til en aktivitet", + "show_region_labels": "Vis regionetiketter", + "no_images": "Ingen bilder", + "upload_images_here": "Last opp bilder her", + "share_adventure": "Del dette eventyret!", + "copy_link": "Kopier lenke", + "image": "Bilde", + "upload_image": "Last opp bilde", + "open_in_maps": "Åpne i kart", + "url": "URL", + "fetch_image": "Hent bilde", + "wikipedia": "Wikipedia", + "add_notes": "Legg til notater", + "warning": "Advarsel", + "my_adventures": "Mine eventyr", + "no_linkable_adventures": "Ingen eventyr funnet som kan legges til denne samlingen.", + "add": "Legg til", + "save_next": "Lagre og fortsett", + "end_date": "Sluttdato", + "my_visits": "Mine besøk", + "start_date": "Startdato", + "remove": "Fjern", + "location": "Plassering", + "search_for_location": "Søk etter sted", + "clear_map": "Tøm kart", + "search_results": "Søkeresultater", + "no_results": "Ingen resultater funnet", + "wiki_desc": "Henter utdrag fra Wikipedia-artikkelen som samsvarer med navnet på eventyret.", + "attachments": "Vedlegg", + "attachment": "Vedlegg", + "images": "Bilder", + "primary": "Primær", + "view_attachment": "Vis vedlegg", + "generate_desc": "Generer beskrivelse", + "public_adventure": "Offentlig eventyr", + "location_information": "Plasseringsinformasjon", + "link": "Lenke", + "links": "Lenker", + "description": "Beskrivelse", + "sources": "Kilder", + "collection_adventures": "Inkluder eventyr i samlinger", + "filter": "Filter", + "category_filter": "Kategorifilter", + "category": "Kategori", + "select_adventure_category": "Velg eventyrkategori", + "clear": "Tøm", + "my_collections": "Mine samlinger", + "open_filters": "Åpne filtre", + "close_filters": "Lukk filtre", + "archived_collections": "Arkiverte samlinger", + "share": "Del", + "private": "Privat", + "public": "Offentlig", + "archived": "Arkivert", + "edit_collection": "Rediger samling", + "unarchive": "Fjern fra arkiv", + "archive": "Arkiver", + "no_collections_found": "Ingen samlinger funnet for å legge dette eventyret til.", + "not_visited": "Ikke besøkt", + "archived_collection_message": "Samlingen ble arkivert!", + "unarchived_collection_message": "Samlingen ble fjernet fra arkivet!", + "delete_collection_success": "Samlingen ble slettet!", + "delete_collection_warning": "Er du sikker på at du vil slette denne samlingen? Dette vil også slette alle lenkede eventyr. Denne handlingen kan ikke angres.", + "cancel": "Avbryt", + "of": "av", + "delete_collection": "Slett samling", + "delete_adventure": "Slett eventyr", + "adventure_delete_success": "Eventyret ble slettet!", + "visited": "Besøkt", + "planned": "Planlagt", + "duration": "Varighet", + "all": "Alle", + "image_removed_success": "Bilde ble fjernet!", + "image_removed_error": "Feil ved fjerning av bilde", + "no_image_url": "Finner ikke bilde på den oppgitte URL-en.", + "image_upload_success": "Bilde opplastet!", + "image_upload_error": "Feil ved opplasting av bilde", + "dates": "Datoer", + "wiki_image_error": "Feil ved henting av bilde fra Wikipedia", + "start_before_end_error": "Startdato må være før sluttdato", + "activity": "Aktivitet", + "actions": "Handlinger", + "no_end_date": "Vennligst angi en sluttdato", + "see_adventures": "Se eventyr", + "image_fetch_failed": "Kunne ikke hente bilde", + "no_location": "Vennligst angi et sted", + "no_start_date": "Vennligst angi en startdato", + "no_description_found": "Fant ingen beskrivelse", + "adventure_created": "Eventyr opprettet", + "adventure_create_error": "Kunne ikke opprette eventyr", + "lodging": "Overnatting", + "create_adventure": "Opprett eventyr", + "adventure_updated": "Eventyr oppdatert", + "adventure_update_error": "Kunne ikke oppdatere eventyr", + "set_to_pin": "Fest", + "category_fetch_error": "Feil ved henting av kategorier", + "new_adventure": "Nytt eventyr", + "basic_information": "Grunnleggende informasjon", + "no_adventures_to_recommendations": "Ingen eventyr funnet. Legg til minst ett eventyr for å få anbefalinger.", + "display_name": "Visningsnavn", + "adventure_not_found": "Det finnes ingen eventyr å vise. Legg til noen ved å trykke på plusstegnet nederst til høyre, eller prøv å endre filtre!", + "no_adventures_found": "Ingen eventyr funnet", + "mark_region_as_visited": "Merk regionen {region}, {country} som besøkt?", + "mark_visited": "Merk som besøkt", + "error_updating_regions": "Feil ved oppdatering av regioner", + "regions_updated": "regioner oppdatert", + "cities_updated": "byer oppdatert", + "visited_region_check": "Sjekk besøkte regioner", + "visited_region_check_desc": "Ved å markere denne, vil serveren sjekke alle dine besøkte eventyr og markere regionene de befinner seg i som besøkt i verdensreiser.", + "update_visited_regions": "Oppdater besøkte regioner", + "update_visited_regions_disclaimer": "Dette kan ta litt tid avhengig av hvor mange eventyr du har besøkt.", + "link_new": "Lenk ny...", + "add_new": "Legg til ny...", + "transportation": "Transport", + "note": "Notat", + "checklist": "Sjekkliste", + "collection_archived": "Denne samlingen er arkivert.", + "visit_link": "Besøk lenke", + "collection_completed": "Du har fullført denne samlingen!", + "collection_stats": "Samlingsstatistikk", + "keep_exploring": "Fortsett å utforske!", + "linked_adventures": "Lenkede eventyr", + "notes": "Notater", + "checklists": "Sjekklister", + "transportations": "Transportmidler", + "adventure_calendar": "Eventyrkalender", + "day": "Dag", + "itineary_by_date": "Reiseplan etter dato", + "nothing_planned": "Ingenting planlagt denne dagen. Nyt reisen!", + "copied_to_clipboard": "Kopiert til utklippstavlen!", + "copy_failed": "Kopiering mislyktes", + "show": "Vis", + "hide": "Skjul", + "clear_location": "Fjern sted", + "starting_airport": "Avreiseflyplass", + "ending_airport": "Ankomsflyplass", + "no_location_found": "Ingen sted funnet", + "from": "Fra", + "to": "Til", + "will_be_marked": "vil bli markert som besøkt når eventyret er lagret.", + "start": "Start", + "end": "Slutt", + "show_map": "Vis kart", + "emoji_picker": "Emoji-velger", + "download_calendar": "Last ned kalender", + "date_information": "Dato-informasjon", + "flight_information": "Flyinformasjon", + "out_of_range": "Ikke i reiseplandatoer", + "preview": "Forhåndsvisning", + "finding_recommendations": "Oppdager skjulte perler for ditt neste eventyr", + "location_details": "Stedsdetaljer", + "city": "By", + "region": "Region", + "md_instructions": "Skriv markdown her...", + "days": "dager", + "attachment_upload_success": "Vedlegg lastet opp!", + "attachment_upload_error": "Feil ved opplasting av vedlegg", + "upload": "Last opp", + "attachment_delete_success": "Vedlegg slettet!", + "attachment_update_success": "Vedlegg oppdatert!", + "attachment_name": "Vedleggsnavn", + "gpx_tip": "Last opp GPX-filer i vedlegg for å se dem på kartet!", + "welcome_map_info": "Offentlige eventyr på denne serveren", + "attachment_update_error": "Feil ved oppdatering av vedlegg", + "activities": { + "general": "Generelt 🌍", + "outdoor": "Utendørs 🏞️", + "lodging": "Overnatting 🛌", + "dining": "Servering 🍽️", + "activity": "Aktivitet 🏄", + "attraction": "Attraksjon 🎢", + "shopping": "Shopping 🛍️", + "nightlife": "Uteliv 🌃", + "event": "Arrangement 🎉", + "transportation": "Transport 🚗", + "culture": "Kultur 🎭", + "water_sports": "Vannsport 🚤", + "hiking": "Fotturer 🥾", + "wildlife": "Dyreliv 🦒", + "historical_sites": "Historiske steder 🏛️", + "music_concerts": "Musikk og konserter 🎶", + "fitness": "Trening 🏋️", + "art_museums": "Kunst og museer 🎨", + "festivals": "Festivaler 🎪", + "spiritual_journeys": "Spirituelle reiser 🧘‍♀️", + "volunteer_work": "Frivillig arbeid 🤝", + "other": "Annet" + }, + "lodging_information": "Overnattingsinformasjon", + "price": "Pris", + "reservation_number": "Reservasjonsnummer", + "additional_info": "Ytterligere informasjon", + "all_day": "Hele dagen", + "collection_no_start_end_date": "Å legge til en start- og sluttdato til samlingen vil låse opp reiseruteplanleggingsfunksjoner på innsamlingssiden.", + "date_itinerary": "Dato reiserute", + "invalid_date_range": "Ugyldig datoområde", + "no_ordered_items": "Legg til varer med datoer i samlingen for å se dem her.", + "ordered_itinerary": "Bestilt reiserute", + "sunrise_sunset": "Soloppgang", + "timezone": "Tidssone", + "no_visits": "Ingen besøk", + "arrival_timezone": "Ankomst tidssone", + "departure_timezone": "Avgangstidssone", + "arrival_date": "Ankomstdato", + "departure_date": "Avgangsdato" + }, + "worldtravel": { + "country_list": "Liste over land", + "num_countries": "land funnet", + "all": "Alle", + "partially_visited": "Delvis besøkt", + "not_visited": "Ikke besøkt", + "completely_visited": "Fullstendig besøkt", + "all_subregions": "Alle underregioner", + "clear_search": "Tøm søk", + "no_countries_found": "Ingen land funnet", + "view_cities": "Vis byer", + "no_cities_found": "Ingen byer funnet", + "visit_to": "Besøk i", + "region_failed_visited": "Kunne ikke markere region som besøkt", + "failed_to_mark_visit": "Kunne ikke markere besøk i", + "visit_remove_failed": "Kunne ikke fjerne besøk", + "removed": "fjernet", + "failed_to_remove_visit": "Kunne ikke fjerne besøk i", + "marked_visited": "markert som besøkt", + "regions_in": "Regioner i", + "region_stats": "Regionstatistikk", + "all_visited": "Du har besøkt alle regionene i", + "cities": "byer" + }, + "auth": { + "username": "Brukernavn", + "password": "Passord", + "forgot_password": "Glemt passord?", + "signup": "Registrer deg", + "login_error": "Kan ikke logge inn med oppgitte legitimasjon.", + "login": "Logg inn", + "email": "E-post", + "first_name": "Fornavn", + "last_name": "Etternavn", + "confirm_password": "Bekreft passord", + "registration_disabled": "Registrering er for øyeblikket deaktivert.", + "profile_picture": "Profilbilde", + "public_profile": "Offentlig profil", + "public_tooltip": "Med en offentlig profil kan brukere dele samlinger med deg og se profilen din på brukersiden.", + "email_required": "E-post kreves", + "new_password": "Nytt passord (6+ tegn)", + "both_passwords_required": "Begge passord er påkrevd", + "reset_failed": "Kunne ikke tilbakestille passord", + "or_3rd_party": "Eller logg inn med en tredjepartstjeneste", + "no_public_adventures": "Ingen offentlige eventyr funnet", + "no_public_collections": "Ingen offentlige samlinger funnet", + "user_adventures": "Brukerens eventyr", + "user_collections": "Brukerens samlinger" + }, + "users": { + "no_users_found": "Ingen brukere med offentlig profil funnet." + }, + "settings": { + "update_error": "Feil ved oppdatering av innstillinger", + "update_success": "Innstillinger oppdatert!", + "settings_page": "Innstillingsside", + "account_settings": "Brukerkontoinnstillinger", + "update": "Oppdater", + "no_verified_email_warning": "Du må ha en verifisert e-postadresse for å aktivere tofaktorautentisering.", + "password_change": "Bytt passord", + "new_password": "Nytt passord", + "confirm_new_password": "Bekreft nytt passord", + "email_change": "Bytt e-post", + "current_email": "Nåværende e-post", + "no_email_set": "Ingen e-post angitt", + "new_email": "Ny e-post", + "change_password": "Bytt passord", + "login_redir": "Du blir da omdirigert til innloggingssiden.", + "token_required": "Token og UID kreves for tilbakestilling av passord.", + "reset_password": "Tilbakestill passord", + "possible_reset": "Hvis e-postadressen du oppga er knyttet til en konto, vil du motta en e-post med instruksjoner om å tilbakestille passordet ditt!", + "missing_email": "Vennligst skriv inn en e-postadresse", + "submit": "Send inn", + "password_does_not_match": "Passordene samsvarer ikke", + "password_is_required": "Passord er påkrevd", + "invalid_token": "Token er ugyldig eller utløpt", + "about_this_background": "Om denne bakgrunnen", + "photo_by": "Foto av", + "join_discord": "Bli med på Discord", + "join_discord_desc": "for å dele dine egne bilder. Legg dem ut i #travel-share-kanalen.", + "current_password": "Nåværende passord", + "change_password_error": "Kan ikke endre passord. Ugyldig nåværende passord eller ugyldig nytt passord.", + "password_change_lopout_warning": "Du vil bli logget ut etter å ha endret passordet.", + "generic_error": "En feil oppsto under behandlingen av forespørselen din.", + "email_removed": "E-post fjernet!", + "email_removed_error": "Feil ved fjerning av e-post", + "verify_email_success": "E-postbekreftelse sendt!", + "verify_email_error": "Feil ved e-postbekreftelse. Prøv igjen om noen minutter.", + "email_added": "E-post lagt til!", + "email_added_error": "Feil ved legging til e-post", + "email_set_primary": "E-post satt som primær!", + "email_set_primary_error": "Feil ved innstilling av primær e-post", + "verified": "Verifisert", + "primary": "Primær", + "not_verified": "Ikke verifisert", + "make_primary": "Gjør til primær", + "verify": "Verifiser", + "no_emai_set": "Ingen e-post angitt", + "error_change_password": "Feil ved endring av passord. Sjekk ditt nåværende passord og prøv igjen.", + "mfa_disabled": "Tofaktorautentisering er deaktivert!", + "mfa_page_title": "Tofaktorautentisering", + "enable_mfa": "Aktiver MFA", + "disable_mfa": "Deaktiver MFA", + "mfa_not_enabled": "MFA er ikke aktivert", + "mfa_enabled": "Tofaktorautentisering er aktivert!", + "copy": "Kopier", + "recovery_codes": "Gjenopprettingskoder", + "recovery_codes_desc": "Dette er dine gjenopprettingskoder. Oppbevar dem trygt. Du vil ikke kunne se dem igjen.", + "reset_session_error": "Logg ut og logg inn igjen for å oppdatere økten din, og prøv igjen.", + "authenticator_code": "Autentiseringskode", + "email_verified": "E-post verifisert!", + "email_verified_success": "E-posten din er verifisert. Du kan nå logge inn.", + "email_verified_error": "Feil ved verifisering av e-post", + "email_verified_erorr_desc": "E-posten din kunne ikke verifiseres. Vennligst prøv igjen.", + "invalid_code": "Ugyldig MFA-kode", + "invalid_credentials": "Ugyldig brukernavn eller passord", + "mfa_required": "Tofaktorautentisering er påkrevd", + "required": "Dette feltet er påkrevd", + "add_email_blocked": "Du kan ikke legge til en e-postadresse på en konto som er beskyttet av tofaktorautentisering.", + "duplicate_email": "Denne e-postadressen er allerede i bruk.", + "csrf_failed": "Kunne ikke hente CSRF-token", + "email_taken": "Denne e-postadressen er allerede i bruk.", + "username_taken": "Dette brukernavnet er allerede i bruk.", + "administration_settings": "Administrasjonsinnstillinger", + "launch_administration_panel": "Åpne administrasjonspanelet", + "social_oidc_auth": "Social og OIDC-autentisering", + "social_auth_desc": "Aktiver eller deaktiver sosiale og OIDC-autentiseringsleverandører for kontoen din. Disse koblingene lar deg logge inn med selvhostede autentiseringstjenester som Authentik eller tredjepartsleverandører som GitHub.", + "social_auth_desc_2": "Disse innstillingene administreres på AdventureLog-serveren og må aktiveres manuelt av administratoren.", + "documentation_link": "Dokumentasjonslenke", + "launch_account_connections": "Åpne kontotilkoblinger", + "password_too_short": "Passordet må være minst 6 tegn", + "add_email": "Legg til e-post", + "password_disable": "Deaktiver passordautentisering", + "password_disable_desc": "Å deaktivere passordautentisering vil hindre deg fra å logge inn med et passord. Du må bruke en sosial eller OIDC-leverandør for å logge inn. Skulle leverandøren din fjernes, vil passordautentisering automatisk bli gjenaktivert, selv om denne innstillingen er deaktivert.", + "disable_password": "Deaktiver passord", + "password_enabled": "Passordautentisering er aktivert", + "password_disabled": "Passordautentisering er deaktivert", + "password_disable_warning": "Akkurat nå er passordautentisering deaktivert. Innlogging via en sosial eller OIDC-leverandør er påkrevd.", + "password_disabled_error": "Feil ved deaktivering av passordautentisering. Sørg for at en sosial eller OIDC-leverandør er koblet til kontoen din.", + "password_enabled_error": "Feil ved aktivering av passordautentisering." + }, + "collection": { + "collection_created": "Samling opprettet!", + "error_creating_collection": "Feil ved oppretting av samling", + "new_collection": "Ny samling", + "create": "Opprett", + "collection_edit_success": "Samling redigert!", + "error_editing_collection": "Feil ved redigering av samling", + "edit_collection": "Rediger samling", + "public_collection": "Offentlig samling" + }, + "notes": { + "note_deleted": "Notat slettet!", + "note_delete_error": "Feil ved sletting av notat", + "open": "Åpne", + "failed_to_save": "Kunne ikke lagre notat", + "note_editor": "Notatredigerer", + "note_viewer": "Notatviser", + "editing_note": "Redigerer notat", + "content": "Innhold", + "save": "Lagre", + "note_public": "Dette notatet er offentlig fordi det er i en offentlig samling.", + "add_a_link": "Legg til en lenke", + "invalid_url": "Ugyldig URL" + }, + "checklist": { + "checklist_deleted": "Sjekkliste slettet!", + "checklist_delete_error": "Feil ved sletting av sjekkliste", + "failed_to_save": "Kunne ikke lagre sjekkliste", + "checklist_editor": "Sjekklisteredigerer", + "checklist_viewer": "Sjekklisteviser", + "editing_checklist": "Redigerer sjekkliste", + "new_checklist": "Ny sjekkliste", + "item": "Punkt", + "items": "Punkter", + "add_item": "Legg til punkt", + "new_item": "Nytt punkt", + "save": "Lagre", + "checklist_public": "Denne sjekklisten er offentlig fordi den er i en offentlig samling.", + "item_cannot_be_empty": "Punktet kan ikke være tomt", + "item_already_exists": "Punktet finnes allerede" + }, + "transportation": { + "transportation_deleted": "Transport slettet!", + "transportation_delete_error": "Feil ved sletting av transport", + "provide_start_date": "Vennligst angi en startdato", + "transport_type": "Transporttype", + "type": "Type", + "transportation_added": "Transport lagt til!", + "error_editing_transportation": "Feil ved redigering av transport", + "new_transportation": "Ny transport", + "date_time": "Startdato og -tid", + "end_date_time": "Sluttdato og -tid", + "flight_number": "Flynummer", + "from_location": "Fra sted", + "to_location": "Til sted", + "fetch_location_information": "Hent stedsinformasjon", + "starting_airport_desc": "Skriv inn avreiseflyplasskode (f.eks. JFK)", + "ending_airport_desc": "Skriv inn ankomsflyplasskode (f.eks. LAX)", + "edit": "Rediger", + "modes": { + "car": "Bil", + "plane": "Fly", + "train": "Tog", + "bus": "Buss", + "boat": "Båt", + "bike": "Sykkel", + "walking": "Går", + "other": "Annet" + }, + "transportation_edit_success": "Transport redigert!", + "edit_transportation": "Rediger transport", + "start": "Start", + "date_and_time": "Dato og tid" + }, + "lodging": { + "lodging_deleted": "Overnatting slettet!", + "lodging_delete_error": "Feil ved sletting av overnatting", + "provide_start_date": "Vennligst angi en startdato", + "lodging_type": "Overnattingstype", + "type": "Type", + "lodging_added": "Overnatting lagt til!", + "error_editing_lodging": "Feil ved redigering av overnatting", + "new_lodging": "Ny overnatting", + "check_in": "Innsjekking", + "check_out": "Utsjekking", + "edit": "Rediger", + "lodging_edit_success": "Overnatting redigert!", + "edit_lodging": "Rediger overnatting", + "start": "Start", + "date_and_time": "Dato og tid", + "hotel": "Hotell", + "hostel": "Hostell", + "resort": "Resort", + "bnb": "Bed & Breakfast", + "campground": "Campingplass", + "cabin": "Hytte", + "apartment": "Leilighet", + "house": "Hus", + "villa": "Villa", + "motel": "Motell", + "other": "Annet", + "reservation_number": "Reservasjonsnummer", + "current_timezone": "Gjeldende tidssone" + }, + "search": { + "adventurelog_results": "AdventureLog-resultater", + "public_adventures": "Offentlige eventyr", + "online_results": "Nettresultater" + }, + "map": { + "view_details": "Vis detaljer", + "adventure_map": "Eventyrkart", + "map_options": "Kartalternativer", + "show_visited_regions": "Vis besøkte regioner", + "add_adventure_at_marker": "Legg til nytt eventyr ved markøren", + "clear_marker": "Fjern markør", + "add_adventure": "Legg til nytt eventyr" + }, + "share": { + "shared": "Delt", + "with": "med", + "unshared": "Udelt", + "share_desc": "Del denne samlingen med andre brukere.", + "shared_with": "Delt med", + "no_users_shared": "Ingen brukere delt med", + "not_shared_with": "Ikke delt med", + "no_shared_found": "Ingen samlinger funnet som er delt med deg.", + "set_public": "For å la brukere dele med deg, må profilen din være offentlig.", + "go_to_settings": "Gå til innstillinger" + }, + "languages": {}, + "profile": { + "member_since": "Medlem siden", + "user_stats": "Brukerstatistikk", + "visited_countries": "Besøkte land", + "visited_regions": "Besøkte regioner", + "visited_cities": "Besøkte byer" + }, + "categories": { + "manage_categories": "Administrer kategorier", + "no_categories_found": "Ingen kategorier funnet.", + "edit_category": "Rediger kategori", + "icon": "Ikon", + "update_after_refresh": "Eventyrkortene vil oppdateres når du oppdaterer siden.", + "select_category": "Velg kategori", + "category_name": "Kategorinavn" + }, + "dashboard": { + "welcome_back": "Velkommen tilbake", + "countries_visited": "Land besøkt", + "total_adventures": "Totalt antall eventyr", + "total_visited_regions": "Totalt antall besøkte regioner", + "total_visited_cities": "Totalt antall besøkte byer", + "recent_adventures": "Nylige eventyr", + "no_recent_adventures": "Ingen nylige eventyr?", + "add_some": "Hvorfor ikke begynne å planlegge ditt neste eventyr? Du kan legge til et nytt eventyr ved å klikke på knappen nedenfor." + }, + "immich": { + "immich": "Immich", + "integration_fetch_error": "Feil ved henting av data fra Immich-integrasjonen", + "integration_missing": "Immich-integrasjonen mangler på backend", + "query_required": "Forespørsel er påkrevd", + "server_down": "Immich-serveren er nede eller utilgjengelig", + "no_items_found": "Ingen elementer funnet", + "imageid_required": "Bilde-ID er påkrevd", + "load_more": "Last mer", + "immich_updated": "Immich-innstillinger oppdatert!", + "immich_enabled": "Immich-integrasjon aktivert!", + "immich_error": "Feil ved oppdatering av Immich-integrasjon", + "immich_disabled": "Immich-integrasjon deaktivert!", + "immich_desc": "Integrer Immich-kontoen din med AdventureLog for å søke i bildebiblioteket ditt og importere bilder til eventyrene dine.", + "integration_enabled": "Integrasjon aktivert", + "disable": "Deaktiver", + "server_url": "Immich-server-URL", + "api_note": "Merk: dette må være URL-en til Immich API-serveren, så den slutter sannsynligvis med /api, med mindre du har en tilpasset konfig.", + "api_key": "Immich API-nøkkel", + "enable_immich": "Aktiver Immich", + "update_integration": "Oppdater integrasjon", + "immich_integration": "Immich-integrasjon", + "localhost_note": "Merk: localhost vil sannsynligvis ikke fungere med mindre du har satt opp docker-nettverk. Det anbefales å bruke serverens IP-adresse eller domenenavn.", + "documentation": "Immich-integrasjonsdokumentasjon" + }, + "recomendations": { + "address": "Adresse", + "phone": "Telefon", + "contact": "Kontakt", + "website": "Nettsted", + "recommendation": "Anbefaling" } - \ No newline at end of file +} diff --git a/frontend/src/locales/pl.json b/frontend/src/locales/pl.json index c4cd914..eabae73 100644 --- a/frontend/src/locales/pl.json +++ b/frontend/src/locales/pl.json @@ -300,7 +300,16 @@ "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ę" + "ordered_itinerary": "Zamówiono trasę", + "additional_info": "Dodatkowe informacje", + "invalid_date_range": "Niepoprawny zakres dat", + "sunrise_sunset": "Wschód słońca", + "timezone": "Strefa czasowa", + "no_visits": "Brak wizyt", + "arrival_timezone": "Strefa czasowa przyjazdu", + "departure_timezone": "Strefa czasowa odlotu", + "arrival_date": "Data przyjazdu", + "departure_date": "Data wyjazdu" }, "worldtravel": { "country_list": "Lista krajów", diff --git a/frontend/src/locales/sv.json b/frontend/src/locales/sv.json index 5efbf63..b2f3b8b 100644 --- a/frontend/src/locales/sv.json +++ b/frontend/src/locales/sv.json @@ -252,7 +252,16 @@ "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" + "ordered_itinerary": "Beställd resplan", + "additional_info": "Ytterligare information", + "invalid_date_range": "Ogiltigt datumintervall", + "sunrise_sunset": "Soluppgång", + "timezone": "Tidszon", + "no_visits": "Inga besök", + "arrival_timezone": "Ankomsttidszon", + "departure_timezone": "Avgångstidszon", + "arrival_date": "Ankomstdatum", + "departure_date": "Avgångsdatum" }, "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 84caea8..fcf2fc5 100644 --- a/frontend/src/locales/zh.json +++ b/frontend/src/locales/zh.json @@ -300,7 +300,16 @@ "collection_no_start_end_date": "在集合页面中添加开始日期和结束日期将在“收集”页面中解锁行程计划功能。", "date_itinerary": "日期行程", "no_ordered_items": "将带有日期的项目添加到集合中,以便在此处查看它们。", - "ordered_itinerary": "订购了行程" + "ordered_itinerary": "订购了行程", + "additional_info": "附加信息", + "invalid_date_range": "无效的日期范围", + "sunrise_sunset": "日出", + "timezone": "时区", + "no_visits": "没有访问", + "arrival_timezone": "到达时区", + "departure_timezone": "离开时区", + "arrival_date": "到达日期", + "departure_date": "出发日期" }, "auth": { "forgot_password": "忘记密码?", diff --git a/frontend/src/routes/adventures/[id]/+page.svelte b/frontend/src/routes/adventures/[id]/+page.svelte index f11f450..4d5d335 100644 --- a/frontend/src/routes/adventures/[id]/+page.svelte +++ b/frontend/src/routes/adventures/[id]/+page.svelte @@ -10,6 +10,8 @@ import DOMPurify from 'dompurify'; // @ts-ignore import toGeoJSON from '@mapbox/togeojson'; + // @ts-ignore + import { DateTime } from 'luxon'; import LightbulbOn from '~icons/mdi/lightbulb-on'; import WeatherSunset from '~icons/mdi/weather-sunset'; @@ -412,33 +414,41 @@

    {#each adventure.visits as visit} -
    -
    -

    - {#if isAllDay(visit.start_date)} - - {new Date(visit.start_date).toLocaleDateString(undefined, { - timeZone: 'UTC' - })} +

    + {#if isAllDay(visit.start_date)} +

    + All Day + {visit.start_date.split('T')[0]} – {visit.end_date.split('T')[0]} +

    + {:else} +

    + {#if visit.timezone} + + 🕓 {visit.timezone}
    + {DateTime.fromISO(visit.start_date, { zone: 'utc' }) + .setZone(visit.timezone) + .toLocaleString(DateTime.DATETIME_MED)} – + {DateTime.fromISO(visit.end_date, { zone: 'utc' }) + .setZone(visit.timezone) + .toLocaleString(DateTime.DATETIME_MED)} {:else} - - {new Date(visit.start_date).toLocaleDateString()} ({new Date( - visit.start_date - ).toLocaleTimeString()}) + + 🕓 Local Time
    + {DateTime.fromISO(visit.start_date).toLocaleString( + DateTime.DATETIME_MED + )} – + {DateTime.fromISO(visit.end_date).toLocaleString( + DateTime.DATETIME_MED + )} {/if}

    - {#if visit.end_date && visit.end_date !== visit.start_date} -

    - - {new Date(visit.end_date).toLocaleDateString(undefined, { - timeZone: 'UTC' - })} - {#if !isAllDay(visit.end_date)} - ({new Date(visit.end_date).toLocaleTimeString()}) - {/if} -

    - {/if} -
    -

    {visit.notes}

    + {/if} + + {#if visit.notes} +

    "{visit.notes}"

    + {/if}
    {/each}
    @@ -458,16 +468,33 @@
    {/if} {#if adventure.longitude && adventure.latitude} - {$t('adventures.open_in_maps')} +
    +

    {$t('adventures.open_in_maps')}:

    +
    + Apple + Google + OSM +
    +
    {/if} {#if adventure.visits.length > 0} -

    +

    {#each adventure.visits as visit} - {visit.start_date - ? new Date(visit.start_date).toLocaleDateString(undefined, { - timeZone: 'UTC' - }) - : ''} - {visit.end_date && - visit.end_date !== '' && - visit.end_date !== visit.start_date - ? ' - ' + - new Date(visit.end_date).toLocaleDateString(undefined, { - timeZone: 'UTC' - }) - : ''} -
    +

    +

    + {#if isAllDay(visit.start_date)} + All Day + {visit.start_date.split('T')[0]} – {visit.end_date.split( + 'T' + )[0]} + {:else} + + Local: + {DateTime.fromISO(visit.start_date).toLocaleString( + DateTime.DATETIME_MED + )} – + {DateTime.fromISO(visit.end_date).toLocaleString( + DateTime.DATETIME_MED + )} + + {/if} +

    + + {#if !isAllDay(visit.start_date) && visit.timezone} +

    + {visit.timezone}: + {DateTime.fromISO(visit.start_date, { zone: 'utc' }) + .setZone(visit.timezone) + .toLocaleString(DateTime.DATETIME_MED)} – + {DateTime.fromISO(visit.end_date, { zone: 'utc' }) + .setZone(visit.timezone) + .toLocaleString(DateTime.DATETIME_MED)} +

    + {/if} + + {#if visit.notes} +

    + "{visit.notes}" +

    + {/if} +
    {/each}

    {/if} diff --git a/frontend/src/routes/collections/[id]/+page.svelte b/frontend/src/routes/collections/[id]/+page.svelte index ab12175..63ee0ce 100644 --- a/frontend/src/routes/collections/[id]/+page.svelte +++ b/frontend/src/routes/collections/[id]/+page.svelte @@ -300,7 +300,7 @@ function handleHashChange() { const hash = window.location.hash.replace('#', ''); if (hash) { - currentView = hash + currentView = hash; } else if (!collection.start_date) { currentView = 'all'; } else { @@ -308,6 +308,10 @@ } } + function changeHash(event: any) { + window.location.hash = '#' + event.target.value; + } + onMount(() => { if (data.props.adventure) { collection = data.props.adventure; @@ -772,7 +776,18 @@ {/if} {#if collection.id} -
    + +