diff --git a/compass_app/app/lib/config/dependencies.dart b/compass_app/app/lib/config/dependencies.dart index fb6743d9f..76938b8a0 100644 --- a/compass_app/app/lib/config/dependencies.dart +++ b/compass_app/app/lib/config/dependencies.dart @@ -3,12 +3,34 @@ import 'package:provider/provider.dart'; import '../data/repositories/continent/continent_repository.dart'; import '../data/repositories/continent/continent_repository_local.dart'; +import '../data/repositories/continent/continent_repository_remote.dart'; import '../data/repositories/destination/destination_repository.dart'; import '../data/repositories/destination/destination_repository_local.dart'; +import '../data/repositories/destination/destination_repository_remote.dart'; +import '../data/services/api_client.dart'; -/// Configure dependencies as a list of Providers -List get providers { - // List of Providers +/// Configure dependencies for remote data. +/// This dependency list uses repositories that connect to a remote server. +List get providersRemote { + final apiClient = ApiClient(); + + return [ + Provider.value( + value: DestinationRepositoryRemote( + apiClient: apiClient, + ) as DestinationRepository, + ), + Provider.value( + value: ContinentRepositoryRemote( + apiClient: apiClient, + ) as ContinentRepository, + ), + ]; +} + +/// Configure dependencies for local data. +/// This dependency list uses repositories that provide local data. +List get providersLocal { return [ Provider.value( value: DestinationRepositoryLocal() as DestinationRepository, diff --git a/compass_app/app/lib/data/models/continent.dart b/compass_app/app/lib/data/models/continent.dart deleted file mode 100644 index b9114ce22..000000000 --- a/compass_app/app/lib/data/models/continent.dart +++ /dev/null @@ -1,12 +0,0 @@ -class Continent { - /// e.g. 'Europe' - final String name; - - /// e.g. 'https://rstr.in/google/tripedia/TmR12QdlVTT' - final String imageUrl; - - Continent({ - required this.name, - required this.imageUrl, - }); -} diff --git a/compass_app/app/lib/data/models/destination.dart b/compass_app/app/lib/data/models/destination.dart deleted file mode 100644 index d22b560c5..000000000 --- a/compass_app/app/lib/data/models/destination.dart +++ /dev/null @@ -1,50 +0,0 @@ -/// Model class for Destination data -class Destination { - Destination({ - required this.ref, - required this.name, - required this.country, - required this.continent, - required this.knownFor, - required this.tags, - required this.imageUrl, - }); - - /// e.g. 'alaska' - final String ref; - - /// e.g. 'Alaska' - final String name; - - /// e.g. 'United States' - final String country; - - /// e.g. 'North America' - final String continent; - - /// e.g. 'Alaska is a haven for outdoor enthusiasts ...' - final String knownFor; - - /// e.g. ['Mountain', 'Off-the-beaten-path', 'Wildlife watching'] - final List tags; - - /// e.g. 'https://storage.googleapis.com/tripedia-images/destinations/alaska.jpg' - final String imageUrl; - - @override - String toString() { - return 'Destination{ref: $ref, name: $name, country: $country, continent: $continent, knownFor: $knownFor, tags: $tags, imageUrl: $imageUrl}'; - } - - factory Destination.fromJson(Map json) { - return Destination( - ref: json['ref'] as String, - name: json['name'] as String, - country: json['country'] as String, - continent: json['continent'] as String, - knownFor: json['knownFor'] as String, - tags: (json['tags'] as List).map((e) => e as String).toList(), - imageUrl: json['imageUrl'] as String, - ); - } -} diff --git a/compass_app/app/lib/data/repositories/continent/continent_repository.dart b/compass_app/app/lib/data/repositories/continent/continent_repository.dart index b9155166b..9429684e7 100644 --- a/compass_app/app/lib/data/repositories/continent/continent_repository.dart +++ b/compass_app/app/lib/data/repositories/continent/continent_repository.dart @@ -1,5 +1,6 @@ +import 'package:compass_model/model.dart'; + import '../../../utils/result.dart'; -import '../../models/continent.dart'; /// Data source with all possible continents. abstract class ContinentRepository { diff --git a/compass_app/app/lib/data/repositories/continent/continent_repository_local.dart b/compass_app/app/lib/data/repositories/continent/continent_repository_local.dart index 8215b53d2..c7812a035 100644 --- a/compass_app/app/lib/data/repositories/continent/continent_repository_local.dart +++ b/compass_app/app/lib/data/repositories/continent/continent_repository_local.dart @@ -1,39 +1,40 @@ +import 'package:compass_model/model.dart'; + import '../../../utils/result.dart'; -import '../../models/continent.dart'; import 'continent_repository.dart'; -/// Local data source with all possible regions. +/// Local data source with all possible continents. class ContinentRepositoryLocal implements ContinentRepository { @override Future>> getContinents() { return Future.value( Result.ok( [ - Continent( + const Continent( name: 'Europe', imageUrl: 'https://rstr.in/google/tripedia/TmR12QdlVTT', ), - Continent( + const Continent( name: 'Asia', imageUrl: 'https://rstr.in/google/tripedia/VJ8BXlQg8O1', ), - Continent( + const Continent( name: 'South America', imageUrl: 'https://rstr.in/google/tripedia/flm_-o1aI8e', ), - Continent( + const Continent( name: 'Africa', imageUrl: 'https://rstr.in/google/tripedia/-nzi8yFOBpF', ), - Continent( + const Continent( name: 'North America', imageUrl: 'https://rstr.in/google/tripedia/jlbgFDrSUVE', ), - Continent( + const Continent( name: 'Oceania', imageUrl: 'https://rstr.in/google/tripedia/vxyrDE-fZVL', ), - Continent( + const Continent( name: 'Australia', imageUrl: 'https://rstr.in/google/tripedia/z6vy6HeRyvZ', ), diff --git a/compass_app/app/lib/data/repositories/continent/continent_repository_remote.dart b/compass_app/app/lib/data/repositories/continent/continent_repository_remote.dart new file mode 100644 index 000000000..1e6968d88 --- /dev/null +++ b/compass_app/app/lib/data/repositories/continent/continent_repository_remote.dart @@ -0,0 +1,34 @@ +import 'package:compass_model/model.dart'; + +import '../../../utils/result.dart'; +import '../../services/api_client.dart'; +import 'continent_repository.dart'; + +/// Remote data source for [Continent]. +/// Implements basic local caching. +/// See: https://docs.flutter.dev/get-started/fwe/local-caching +class ContinentRepositoryRemote implements ContinentRepository { + ContinentRepositoryRemote({ + required ApiClient apiClient, + }) : _apiClient = apiClient; + + final ApiClient _apiClient; + + List? _cachedData; + + @override + Future>> getContinents() async { + if (_cachedData == null) { + // No cached data, request continents + final result = await _apiClient.getContinents(); + if (result is Ok) { + // Store value if result Ok + _cachedData = result.asOk.value; + } + return result; + } else { + // Return cached data if available + return Result.ok(_cachedData!); + } + } +} diff --git a/compass_app/app/lib/data/repositories/destination/destination_repository.dart b/compass_app/app/lib/data/repositories/destination/destination_repository.dart index 01bba9181..8675c2314 100644 --- a/compass_app/app/lib/data/repositories/destination/destination_repository.dart +++ b/compass_app/app/lib/data/repositories/destination/destination_repository.dart @@ -1,5 +1,6 @@ +import 'package:compass_model/model.dart'; + import '../../../utils/result.dart'; -import '../../models/destination.dart'; /// Data source with all possible destinations abstract class DestinationRepository { diff --git a/compass_app/app/lib/data/repositories/destination/destination_repository_local.dart b/compass_app/app/lib/data/repositories/destination/destination_repository_local.dart index 5d587c982..d3cd0e55c 100644 --- a/compass_app/app/lib/data/repositories/destination/destination_repository_local.dart +++ b/compass_app/app/lib/data/repositories/destination/destination_repository_local.dart @@ -1,11 +1,11 @@ import 'dart:convert'; +import 'package:compass_model/model.dart'; +import 'package:flutter/services.dart' show rootBundle; + import '../../../utils/result.dart'; -import '../../models/destination.dart'; import 'destination_repository.dart'; -import 'package:flutter/services.dart' show rootBundle; - /// Local implementation of DestinationRepository /// Uses data from assets folder class DestinationRepositoryLocal implements DestinationRepository { diff --git a/compass_app/app/lib/data/repositories/destination/destination_repository_remote.dart b/compass_app/app/lib/data/repositories/destination/destination_repository_remote.dart new file mode 100644 index 000000000..fd9be3296 --- /dev/null +++ b/compass_app/app/lib/data/repositories/destination/destination_repository_remote.dart @@ -0,0 +1,34 @@ +import 'package:compass_model/model.dart'; + +import '../../../utils/result.dart'; +import '../../services/api_client.dart'; +import 'destination_repository.dart'; + +/// Remote data source for [Destination]. +/// Implements basic local caching. +/// See: https://docs.flutter.dev/get-started/fwe/local-caching +class DestinationRepositoryRemote implements DestinationRepository { + DestinationRepositoryRemote({ + required ApiClient apiClient, + }) : _apiClient = apiClient; + + final ApiClient _apiClient; + + List? _cachedData; + + @override + Future>> getDestinations() async { + if (_cachedData == null) { + // No cached data, request destinations + final result = await _apiClient.getDestinations(); + if (result is Ok) { + // Store value if result Ok + _cachedData = result.asOk.value; + } + return result; + } else { + // Return cached data if available + return Result.ok(_cachedData!); + } + } +} diff --git a/compass_app/app/lib/data/services/api_client.dart b/compass_app/app/lib/data/services/api_client.dart new file mode 100644 index 000000000..98d91ca6b --- /dev/null +++ b/compass_app/app/lib/data/services/api_client.dart @@ -0,0 +1,49 @@ +import 'dart:convert'; +import 'dart:io'; +import 'package:compass_model/model.dart'; + +import '../../utils/result.dart'; + +// TODO: Basic auth request +// TODO: Configurable baseurl/host/port +class ApiClient { + Future>> getContinents() async { + final client = HttpClient(); + try { + final request = await client.get('localhost', 8080, '/continent'); + final response = await request.close(); + if (response.statusCode == 200) { + final stringData = await response.transform(utf8.decoder).join(); + final json = jsonDecode(stringData) as List; + return Result.ok( + json.map((element) => Continent.fromJson(element)).toList()); + } else { + return Result.error(const HttpException("Invalid response")); + } + } on Exception catch (error) { + return Result.error(error); + } finally { + client.close(); + } + } + + Future>> getDestinations() async { + final client = HttpClient(); + try { + final request = await client.get('localhost', 8080, '/destination'); + final response = await request.close(); + if (response.statusCode == 200) { + final stringData = await response.transform(utf8.decoder).join(); + final json = jsonDecode(stringData) as List; + return Result.ok( + json.map((element) => Destination.fromJson(element)).toList()); + } else { + return Result.error(const HttpException("Invalid response")); + } + } on Exception catch (error) { + return Result.error(error); + } finally { + client.close(); + } + } +} diff --git a/compass_app/app/lib/main.dart b/compass_app/app/lib/main.dart index a9660d195..c55835c8e 100644 --- a/compass_app/app/lib/main.dart +++ b/compass_app/app/lib/main.dart @@ -1,20 +1,14 @@ -import 'config/dependencies.dart'; import 'ui/core/themes/theme.dart'; import 'routing/router.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; import 'ui/core/ui/scroll_behavior.dart'; +import 'main_development.dart' as development; +/// Default main method void main() { - runApp( - MultiProvider( - // Loading the default providers - // NOTE: We can load different configurations e.g. fakes - providers: providers, - child: const MainApp(), - ), - ); + // Launch development config by default + development.main(); } class MainApp extends StatelessWidget { diff --git a/compass_app/app/lib/main_development.dart b/compass_app/app/lib/main_development.dart new file mode 100644 index 000000000..73ef5073a --- /dev/null +++ b/compass_app/app/lib/main_development.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import 'config/dependencies.dart'; +import 'main.dart'; + +/// Development config entry point. +/// Launch with `flutter run --target lib/main_development.dart`. +/// Uses local data. +void main() { + runApp( + MultiProvider( + providers: providersLocal, + child: const MainApp(), + ), + ); +} diff --git a/compass_app/app/lib/main_staging.dart b/compass_app/app/lib/main_staging.dart new file mode 100644 index 000000000..bcc9f6800 --- /dev/null +++ b/compass_app/app/lib/main_staging.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import 'config/dependencies.dart'; +import 'main.dart'; + +/// Staging config entry point. +/// Launch with `flutter run --target lib/main_staging.dart`. +/// Uses remote data from a server. +void main() { + runApp( + MultiProvider( + providers: providersRemote, + child: const MainApp(), + ), + ); +} diff --git a/compass_app/app/lib/routing/router.dart b/compass_app/app/lib/routing/router.dart index 306cae2d4..f1083afd2 100644 --- a/compass_app/app/lib/routing/router.dart +++ b/compass_app/app/lib/routing/router.dart @@ -13,7 +13,8 @@ final router = GoRouter( GoRoute( path: '/', builder: (context, state) { - final viewModel = SearchFormViewModel(continentRepository: context.read()); + final viewModel = + SearchFormViewModel(continentRepository: context.read()); return SearchFormScreen(viewModel: viewModel); }, routes: [ diff --git a/compass_app/app/lib/ui/core/themes/colors.dart b/compass_app/app/lib/ui/core/themes/colors.dart index 046fe8377..b2a488873 100644 --- a/compass_app/app/lib/ui/core/themes/colors.dart +++ b/compass_app/app/lib/ui/core/themes/colors.dart @@ -8,8 +8,7 @@ class AppColors { static const grey3 = Color(0xFFA4A4A4); static const whiteTransparent = Color(0x4DFFFFFF); // Figma rgba(255, 255, 255, 0.3) - static const blackTransparent = - Color(0x4D000000); + static const blackTransparent = Color(0x4D000000); static const lightColorScheme = ColorScheme( brightness: Brightness.light, diff --git a/compass_app/app/lib/ui/core/ui/scroll_behavior.dart b/compass_app/app/lib/ui/core/ui/scroll_behavior.dart index 73b2be6df..460d5abf1 100644 --- a/compass_app/app/lib/ui/core/ui/scroll_behavior.dart +++ b/compass_app/app/lib/ui/core/ui/scroll_behavior.dart @@ -6,8 +6,8 @@ import 'package:flutter/material.dart'; class AppCustomScrollBehavior extends MaterialScrollBehavior { @override Set get dragDevices => { - PointerDeviceKind.touch, - // Allow to drag with mouse on Regions carousel - PointerDeviceKind.mouse, - }; + PointerDeviceKind.touch, + // Allow to drag with mouse on Regions carousel + PointerDeviceKind.mouse, + }; } diff --git a/compass_app/app/lib/ui/results/view_models/results_viewmodel.dart b/compass_app/app/lib/ui/results/view_models/results_viewmodel.dart index 58643a031..880c667d9 100644 --- a/compass_app/app/lib/ui/results/view_models/results_viewmodel.dart +++ b/compass_app/app/lib/ui/results/view_models/results_viewmodel.dart @@ -1,6 +1,7 @@ +import 'package:compass_model/model.dart'; + import '../../../data/repositories/destination/destination_repository.dart'; import '../../../utils/result.dart'; -import '../../../data/models/destination.dart'; import 'package:flutter/cupertino.dart'; /// Results screen view model diff --git a/compass_app/app/lib/ui/results/widgets/result_card.dart b/compass_app/app/lib/ui/results/widgets/result_card.dart index 98f7a571a..bd63d90b1 100644 --- a/compass_app/app/lib/ui/results/widgets/result_card.dart +++ b/compass_app/app/lib/ui/results/widgets/result_card.dart @@ -1,8 +1,9 @@ +import 'package:compass_model/model.dart'; + import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import '../../core/themes/text_styles.dart'; import '../../core/ui/tag_chip.dart'; -import '../../../data/models/destination.dart'; class ResultCard extends StatelessWidget { const ResultCard({ diff --git a/compass_app/app/lib/ui/search_form/view_models/search_form_viewmodel.dart b/compass_app/app/lib/ui/search_form/view_models/search_form_viewmodel.dart index 44f875532..ec0d6eae7 100644 --- a/compass_app/app/lib/ui/search_form/view_models/search_form_viewmodel.dart +++ b/compass_app/app/lib/ui/search_form/view_models/search_form_viewmodel.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; +import 'package:compass_model/model.dart'; -import '../../../data/models/continent.dart'; import '../../../data/repositories/continent/continent_repository.dart'; import '../../../utils/result.dart'; @@ -33,7 +33,8 @@ class SearchFormViewModel extends ChangeNotifier { /// Must be called only if [valid] is true get searchQuery { assert(valid, "Called searchQuery when the form is not valid"); - assert(_selectedContinent != null, "Called searchQuery without a continent"); + assert( + _selectedContinent != null, "Called searchQuery without a continent"); assert(_dateRange != null, "Called searchQuery without a date range"); assert(_guests > 0, "Called searchQuery without guests"); final startDate = _dateRange!.start; diff --git a/compass_app/app/lib/ui/search_form/widgets/search_form_continent.dart b/compass_app/app/lib/ui/search_form/widgets/search_form_continent.dart index 4960145e4..1bf02a2de 100644 --- a/compass_app/app/lib/ui/search_form/widgets/search_form_continent.dart +++ b/compass_app/app/lib/ui/search_form/widgets/search_form_continent.dart @@ -1,8 +1,8 @@ import 'package:cached_network_image/cached_network_image.dart'; +import 'package:compass_model/model.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; -import '../../../data/models/continent.dart'; import '../../core/themes/colors.dart'; import '../view_models/search_form_viewmodel.dart'; diff --git a/compass_app/app/pubspec.yaml b/compass_app/app/pubspec.yaml index bf95a64b1..bc52ed0bd 100644 --- a/compass_app/app/pubspec.yaml +++ b/compass_app/app/pubspec.yaml @@ -8,6 +8,8 @@ environment: dependencies: cached_network_image: ^3.3.1 + compass_model: + path: ../model flutter: sdk: flutter go_router: ^14.2.0 diff --git a/compass_app/app/test/data/repositories/continent/continent_repository_remote_test.dart b/compass_app/app/test/data/repositories/continent/continent_repository_remote_test.dart new file mode 100644 index 000000000..047c388aa --- /dev/null +++ b/compass_app/app/test/data/repositories/continent/continent_repository_remote_test.dart @@ -0,0 +1,45 @@ +import 'package:compass_app/data/repositories/continent/continent_repository.dart'; +import 'package:compass_app/data/repositories/continent/continent_repository_remote.dart'; +import 'package:compass_app/utils/result.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../../util/fakes/services/fake_api_client.dart'; + +void main() { + group('ContinentRepositoryRemote tests', () { + late FakeApiClient apiClient; + late ContinentRepository repository; + + setUp(() { + apiClient = FakeApiClient(); + repository = ContinentRepositoryRemote(apiClient: apiClient); + }); + + test('should get continents', () async { + final result = await repository.getContinents(); + expect(result, isA()); + + final list = result.asOk.value; + expect(list.length, 3); + + final destination = list.first; + expect(destination.name, 'CONTINENT'); + + // Only one request happened + expect(apiClient.requestCount, 1); + }); + + test('should get continents from cache', () async { + // Request continents once + var result = await repository.getContinents(); + expect(result, isA()); + + // Request continents another time + result = await repository.getContinents(); + expect(result, isA()); + + // Only one request happened + expect(apiClient.requestCount, 1); + }); + }); +} diff --git a/compass_app/app/test/data/repositories/destination/destination_repository_remote_test.dart b/compass_app/app/test/data/repositories/destination/destination_repository_remote_test.dart new file mode 100644 index 000000000..f630da4ec --- /dev/null +++ b/compass_app/app/test/data/repositories/destination/destination_repository_remote_test.dart @@ -0,0 +1,45 @@ +import 'package:compass_app/data/repositories/destination/destination_repository.dart'; +import 'package:compass_app/data/repositories/destination/destination_repository_remote.dart'; +import 'package:compass_app/utils/result.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../../util/fakes/services/fake_api_client.dart'; + +void main() { + group('DestinationRepositoryRemote tests', () { + late FakeApiClient apiClient; + late DestinationRepository repository; + + setUp(() { + apiClient = FakeApiClient(); + repository = DestinationRepositoryRemote(apiClient: apiClient); + }); + + test('should get destinations', () async { + final result = await repository.getDestinations(); + expect(result, isA()); + + final list = result.asOk.value; + expect(list.length, 2); + + final destination = list.first; + expect(destination.name, 'name1'); + + // Only one request happened + expect(apiClient.requestCount, 1); + }); + + test('should get destinations from cache', () async { + // Request destination once + var result = await repository.getDestinations(); + expect(result, isA()); + + // Request destination another time + result = await repository.getDestinations(); + expect(result, isA()); + + // Only one request happened + expect(apiClient.requestCount, 1); + }); + }); +} diff --git a/compass_app/app/test/ui/search_form/view_models/search_form_viewmodel_test.dart b/compass_app/app/test/ui/search_form/view_models/search_form_viewmodel_test.dart index e4338e5ab..16b20ef75 100644 --- a/compass_app/app/test/ui/search_form/view_models/search_form_viewmodel_test.dart +++ b/compass_app/app/test/ui/search_form/view_models/search_form_viewmodel_test.dart @@ -9,7 +9,8 @@ void main() { late SearchFormViewModel viewModel; setUp(() { - viewModel = SearchFormViewModel(continentRepository: FakeContinentRepository()); + viewModel = + SearchFormViewModel(continentRepository: FakeContinentRepository()); }); test('Initial values are correct', () { @@ -58,7 +59,8 @@ void main() { viewModel.dateRange = newDateRange; expect(viewModel.valid, true); - expect(viewModel.searchQuery, 'continent=CONTINENT&checkIn=2024-01-01&checkOut=2024-01-31&guests=2'); + expect(viewModel.searchQuery, + 'continent=CONTINENT&checkIn=2024-01-01&checkOut=2024-01-31&guests=2'); }); }); } diff --git a/compass_app/app/test/ui/search_form/widgets/search_form_continent_test.dart b/compass_app/app/test/ui/search_form/widgets/search_form_continent_test.dart index c6e0f36a2..05be5d34b 100644 --- a/compass_app/app/test/ui/search_form/widgets/search_form_continent_test.dart +++ b/compass_app/app/test/ui/search_form/widgets/search_form_continent_test.dart @@ -30,7 +30,8 @@ void main() { }); } - testWidgets('Should load and select continent', (WidgetTester tester) async { + testWidgets('Should load and select continent', + (WidgetTester tester) async { await loadWidget(tester); expect(find.byType(SearchFormContinent), findsOneWidget); diff --git a/compass_app/app/test/ui/search_form/widgets/search_form_date_test.dart b/compass_app/app/test/ui/search_form/widgets/search_form_date_test.dart index 3a0c12b77..1a83d857c 100644 --- a/compass_app/app/test/ui/search_form/widgets/search_form_date_test.dart +++ b/compass_app/app/test/ui/search_form/widgets/search_form_date_test.dart @@ -27,7 +27,8 @@ void main() { ); } - testWidgets('should display date in different month', (WidgetTester tester) async { + testWidgets('should display date in different month', + (WidgetTester tester) async { await loadWidget(tester); expect(find.byType(SearchFormDate), findsOneWidget); @@ -35,13 +36,15 @@ void main() { expect(find.text('Add Dates'), findsOneWidget); // Simulate date picker input: - viewModel.dateRange = DateTimeRange(start: DateTime(2024, 6, 12), end: DateTime(2024, 7, 23)); + viewModel.dateRange = DateTimeRange( + start: DateTime(2024, 6, 12), end: DateTime(2024, 7, 23)); await tester.pumpAndSettle(); expect(find.text('12 Jun - 23 Jul'), findsOneWidget); }); - testWidgets('should display date in same month', (WidgetTester tester) async { + testWidgets('should display date in same month', + (WidgetTester tester) async { await loadWidget(tester); expect(find.byType(SearchFormDate), findsOneWidget); @@ -49,7 +52,8 @@ void main() { expect(find.text('Add Dates'), findsOneWidget); // Simulate date picker input: - viewModel.dateRange = DateTimeRange(start: DateTime(2024, 6, 12), end: DateTime(2024, 6, 23)); + viewModel.dateRange = DateTimeRange( + start: DateTime(2024, 6, 12), end: DateTime(2024, 6, 23)); await tester.pumpAndSettle(); expect(find.text('12 - 23 Jun'), findsOneWidget); diff --git a/compass_app/app/test/ui/search_form/widgets/search_form_screen_test.dart b/compass_app/app/test/ui/search_form/widgets/search_form_screen_test.dart index 6634963df..51bb00b78 100644 --- a/compass_app/app/test/ui/search_form/widgets/search_form_screen_test.dart +++ b/compass_app/app/test/ui/search_form/widgets/search_form_screen_test.dart @@ -38,7 +38,8 @@ void main() { }); } - testWidgets('Should fill form and perform search', (WidgetTester tester) async { + testWidgets('Should fill form and perform search', + (WidgetTester tester) async { await loadWidget(tester); expect(find.byType(SearchFormScreen), findsOneWidget); @@ -46,7 +47,8 @@ void main() { await tester.tap(find.text('CONTINENT'), warnIfMissed: false); // Select date - viewModel.dateRange = DateTimeRange(start: DateTime(2024, 6, 12), end: DateTime(2024, 7, 23)); + viewModel.dateRange = DateTimeRange( + start: DateTime(2024, 6, 12), end: DateTime(2024, 7, 23)); // Select guests await tester.tap(find.byKey(const ValueKey('add_guests'))); @@ -58,7 +60,9 @@ void main() { await tester.tap(find.byKey(const ValueKey('submit_button'))); // Should navigate to results screen - verify(() => goRouter.go('/results?continent=CONTINENT&checkIn=2024-06-12&checkOut=2024-07-23&guests=1')).called(1); + verify(() => goRouter.go( + '/results?continent=CONTINENT&checkIn=2024-06-12&checkOut=2024-07-23&guests=1')) + .called(1); }); }); } diff --git a/compass_app/app/test/util/fakes/repositories/fake_continent_repository.dart b/compass_app/app/test/util/fakes/repositories/fake_continent_repository.dart index f08f84b7a..d971ebe03 100644 --- a/compass_app/app/test/util/fakes/repositories/fake_continent_repository.dart +++ b/compass_app/app/test/util/fakes/repositories/fake_continent_repository.dart @@ -1,4 +1,4 @@ -import 'package:compass_app/data/models/continent.dart'; +import 'package:compass_model/model.dart'; import 'package:compass_app/data/repositories/continent/continent_repository.dart'; import 'package:compass_app/utils/result.dart'; @@ -6,9 +6,9 @@ class FakeContinentRepository implements ContinentRepository { @override Future>> getContinents() async { return Result.ok([ - Continent(name: 'CONTINENT', imageUrl: 'URL'), - Continent(name: 'CONTINENT2', imageUrl: 'URL'), - Continent(name: 'CONTINENT3', imageUrl: 'URL'), + const Continent(name: 'CONTINENT', imageUrl: 'URL'), + const Continent(name: 'CONTINENT2', imageUrl: 'URL'), + const Continent(name: 'CONTINENT3', imageUrl: 'URL'), ]); } } diff --git a/compass_app/app/test/util/fakes/repositories/fake_destination_repository.dart b/compass_app/app/test/util/fakes/repositories/fake_destination_repository.dart index d5c914857..c6d57e3de 100644 --- a/compass_app/app/test/util/fakes/repositories/fake_destination_repository.dart +++ b/compass_app/app/test/util/fakes/repositories/fake_destination_repository.dart @@ -1,4 +1,4 @@ -import 'package:compass_app/data/models/destination.dart'; +import 'package:compass_model/model.dart'; import 'package:compass_app/data/repositories/destination/destination_repository.dart'; import 'package:compass_app/utils/result.dart'; @@ -9,7 +9,7 @@ class FakeDestinationRepository implements DestinationRepository { return Future.value( Result.ok( [ - Destination( + const Destination( ref: 'ref1', name: 'name1', country: 'country1', @@ -18,7 +18,7 @@ class FakeDestinationRepository implements DestinationRepository { tags: ['tags1'], imageUrl: 'imageUrl1', ), - Destination( + const Destination( ref: 'ref2', name: 'name2', country: 'country2', diff --git a/compass_app/app/test/util/fakes/services/fake_api_client.dart b/compass_app/app/test/util/fakes/services/fake_api_client.dart new file mode 100644 index 000000000..884a2f060 --- /dev/null +++ b/compass_app/app/test/util/fakes/services/fake_api_client.dart @@ -0,0 +1,45 @@ +import 'package:compass_app/data/services/api_client.dart'; +import 'package:compass_app/utils/result.dart'; +import 'package:compass_model/model.dart'; + +class FakeApiClient implements ApiClient { + // Should not increase when using cached data + int requestCount = 0; + + @override + Future>> getContinents() async { + requestCount++; + return Result.ok([ + const Continent(name: 'CONTINENT', imageUrl: 'URL'), + const Continent(name: 'CONTINENT2', imageUrl: 'URL'), + const Continent(name: 'CONTINENT3', imageUrl: 'URL'), + ]); + } + + @override + Future>> getDestinations() async { + requestCount++; + return Result.ok( + [ + const Destination( + ref: 'ref1', + name: 'name1', + country: 'country1', + continent: 'Europe', + knownFor: 'knownFor1', + tags: ['tags1'], + imageUrl: 'imageUrl1', + ), + const Destination( + ref: 'ref2', + name: 'name2', + country: 'country2', + continent: 'Europe', + knownFor: 'knownFor2', + tags: ['tags2'], + imageUrl: 'imageUrl2', + ), + ], + ); + } +} diff --git a/compass_app/model/.gitignore b/compass_app/model/.gitignore new file mode 100644 index 000000000..3cceda557 --- /dev/null +++ b/compass_app/model/.gitignore @@ -0,0 +1,7 @@ +# https://dart.dev/guides/libraries/private-files +# Created by `dart pub` +.dart_tool/ + +# Avoid committing pubspec.lock for library packages; see +# https://dart.dev/guides/libraries/private-files#pubspeclock. +pubspec.lock diff --git a/compass_app/model/README.md b/compass_app/model/README.md new file mode 100644 index 000000000..171296171 --- /dev/null +++ b/compass_app/model/README.md @@ -0,0 +1,3 @@ +# compass_model + +Shared Data Model for the `compass_app` example. diff --git a/compass_app/model/analysis_options.yaml b/compass_app/model/analysis_options.yaml new file mode 100644 index 000000000..dee8927aa --- /dev/null +++ b/compass_app/model/analysis_options.yaml @@ -0,0 +1,30 @@ +# This file configures the static analysis results for your project (errors, +# warnings, and lints). +# +# This enables the 'recommended' set of lints from `package:lints`. +# This set helps identify many issues that may lead to problems when running +# or consuming Dart code, and enforces writing Dart using a single, idiomatic +# style and format. +# +# If you want a smaller set of lints you can change this to specify +# 'package:lints/core.yaml'. These are just the most critical lints +# (the recommended set includes the core lints). +# The core lints are also what is used by pub.dev for scoring packages. + +include: package:lints/recommended.yaml + +# Uncomment the following section to specify additional rules. + +# linter: +# rules: +# - camel_case_types + +# analyzer: +# exclude: +# - path/to/excluded/files/** + +# For more information about the core and recommended set of lints, see +# https://dart.dev/go/core-lints + +# For additional information about configuring this file, see +# https://dart.dev/guides/language/analysis-options diff --git a/compass_app/model/lib/model.dart b/compass_app/model/lib/model.dart new file mode 100644 index 000000000..8fe9c5145 --- /dev/null +++ b/compass_app/model/lib/model.dart @@ -0,0 +1,4 @@ +library; + +export 'src/model/continent/continent.dart'; +export 'src/model/destination/destination.dart'; diff --git a/compass_app/model/lib/src/model/continent/continent.dart b/compass_app/model/lib/src/model/continent/continent.dart new file mode 100644 index 000000000..a07a2d4d9 --- /dev/null +++ b/compass_app/model/lib/src/model/continent/continent.dart @@ -0,0 +1,19 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'continent.freezed.dart'; + +part 'continent.g.dart'; + +@freezed +class Continent with _$Continent { + const factory Continent({ + /// e.g. 'Europe' + required String name, + + /// e.g. 'https://rstr.in/google/tripedia/TmR12QdlVTT' + required String imageUrl, + }) = _Continent; + + factory Continent.fromJson(Map json) => + _$ContinentFromJson(json); +} diff --git a/compass_app/model/lib/src/model/continent/continent.freezed.dart b/compass_app/model/lib/src/model/continent/continent.freezed.dart new file mode 100644 index 000000000..f69d01046 --- /dev/null +++ b/compass_app/model/lib/src/model/continent/continent.freezed.dart @@ -0,0 +1,191 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'continent.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +Continent _$ContinentFromJson(Map json) { + return _Continent.fromJson(json); +} + +/// @nodoc +mixin _$Continent { + /// e.g. 'Europe' + String get name => throw _privateConstructorUsedError; + + /// e.g. 'https://rstr.in/google/tripedia/TmR12QdlVTT' + String get imageUrl => throw _privateConstructorUsedError; + + /// Serializes this Continent to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of Continent + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $ContinentCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $ContinentCopyWith<$Res> { + factory $ContinentCopyWith(Continent value, $Res Function(Continent) then) = + _$ContinentCopyWithImpl<$Res, Continent>; + @useResult + $Res call({String name, String imageUrl}); +} + +/// @nodoc +class _$ContinentCopyWithImpl<$Res, $Val extends Continent> + implements $ContinentCopyWith<$Res> { + _$ContinentCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of Continent + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? name = null, + Object? imageUrl = null, + }) { + return _then(_value.copyWith( + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + imageUrl: null == imageUrl + ? _value.imageUrl + : imageUrl // ignore: cast_nullable_to_non_nullable + as String, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$ContinentImplCopyWith<$Res> + implements $ContinentCopyWith<$Res> { + factory _$$ContinentImplCopyWith( + _$ContinentImpl value, $Res Function(_$ContinentImpl) then) = + __$$ContinentImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({String name, String imageUrl}); +} + +/// @nodoc +class __$$ContinentImplCopyWithImpl<$Res> + extends _$ContinentCopyWithImpl<$Res, _$ContinentImpl> + implements _$$ContinentImplCopyWith<$Res> { + __$$ContinentImplCopyWithImpl( + _$ContinentImpl _value, $Res Function(_$ContinentImpl) _then) + : super(_value, _then); + + /// Create a copy of Continent + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? name = null, + Object? imageUrl = null, + }) { + return _then(_$ContinentImpl( + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + imageUrl: null == imageUrl + ? _value.imageUrl + : imageUrl // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$ContinentImpl implements _Continent { + const _$ContinentImpl({required this.name, required this.imageUrl}); + + factory _$ContinentImpl.fromJson(Map json) => + _$$ContinentImplFromJson(json); + + /// e.g. 'Europe' + @override + final String name; + + /// e.g. 'https://rstr.in/google/tripedia/TmR12QdlVTT' + @override + final String imageUrl; + + @override + String toString() { + return 'Continent(name: $name, imageUrl: $imageUrl)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$ContinentImpl && + (identical(other.name, name) || other.name == name) && + (identical(other.imageUrl, imageUrl) || + other.imageUrl == imageUrl)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash(runtimeType, name, imageUrl); + + /// Create a copy of Continent + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$ContinentImplCopyWith<_$ContinentImpl> get copyWith => + __$$ContinentImplCopyWithImpl<_$ContinentImpl>(this, _$identity); + + @override + Map toJson() { + return _$$ContinentImplToJson( + this, + ); + } +} + +abstract class _Continent implements Continent { + const factory _Continent( + {required final String name, + required final String imageUrl}) = _$ContinentImpl; + + factory _Continent.fromJson(Map json) = + _$ContinentImpl.fromJson; + + /// e.g. 'Europe' + @override + String get name; + + /// e.g. 'https://rstr.in/google/tripedia/TmR12QdlVTT' + @override + String get imageUrl; + + /// Create a copy of Continent + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$ContinentImplCopyWith<_$ContinentImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/compass_app/model/lib/src/model/continent/continent.g.dart b/compass_app/model/lib/src/model/continent/continent.g.dart new file mode 100644 index 000000000..41aba78b6 --- /dev/null +++ b/compass_app/model/lib/src/model/continent/continent.g.dart @@ -0,0 +1,19 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'continent.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$ContinentImpl _$$ContinentImplFromJson(Map json) => + _$ContinentImpl( + name: json['name'] as String, + imageUrl: json['imageUrl'] as String, + ); + +Map _$$ContinentImplToJson(_$ContinentImpl instance) => + { + 'name': instance.name, + 'imageUrl': instance.imageUrl, + }; diff --git a/compass_app/model/lib/src/model/destination/destination.dart b/compass_app/model/lib/src/model/destination/destination.dart new file mode 100644 index 000000000..62bfcd0ee --- /dev/null +++ b/compass_app/model/lib/src/model/destination/destination.dart @@ -0,0 +1,34 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'destination.freezed.dart'; + +part 'destination.g.dart'; + +@freezed +class Destination with _$Destination { + const factory Destination({ + /// e.g. 'alaska' + required String ref, + + /// e.g. 'Alaska' + required String name, + + /// e.g. 'United States' + required String country, + + /// e.g. 'North America' + required String continent, + + /// e.g. 'Alaska is a haven for outdoor enthusiasts ...' + required String knownFor, + + /// e.g. ['Mountain', 'Off-the-beaten-path', 'Wildlife watching'] + required List tags, + + /// e.g. 'https://storage.googleapis.com/tripedia-images/destinations/alaska.jpg' + required String imageUrl, + }) = _Destination; + + factory Destination.fromJson(Map json) => + _$DestinationFromJson(json); +} diff --git a/compass_app/model/lib/src/model/destination/destination.freezed.dart b/compass_app/model/lib/src/model/destination/destination.freezed.dart new file mode 100644 index 000000000..faaec5902 --- /dev/null +++ b/compass_app/model/lib/src/model/destination/destination.freezed.dart @@ -0,0 +1,339 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'destination.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +Destination _$DestinationFromJson(Map json) { + return _Destination.fromJson(json); +} + +/// @nodoc +mixin _$Destination { + /// e.g. 'alaska' + String get ref => throw _privateConstructorUsedError; + + /// e.g. 'Alaska' + String get name => throw _privateConstructorUsedError; + + /// e.g. 'United States' + String get country => throw _privateConstructorUsedError; + + /// e.g. 'North America' + String get continent => throw _privateConstructorUsedError; + + /// e.g. 'Alaska is a haven for outdoor enthusiasts ...' + String get knownFor => throw _privateConstructorUsedError; + + /// e.g. ['Mountain', 'Off-the-beaten-path', 'Wildlife watching'] + List get tags => throw _privateConstructorUsedError; + + /// e.g. 'https://storage.googleapis.com/tripedia-images/destinations/alaska.jpg' + String get imageUrl => throw _privateConstructorUsedError; + + /// Serializes this Destination to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of Destination + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $DestinationCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $DestinationCopyWith<$Res> { + factory $DestinationCopyWith( + Destination value, $Res Function(Destination) then) = + _$DestinationCopyWithImpl<$Res, Destination>; + @useResult + $Res call( + {String ref, + String name, + String country, + String continent, + String knownFor, + List tags, + String imageUrl}); +} + +/// @nodoc +class _$DestinationCopyWithImpl<$Res, $Val extends Destination> + implements $DestinationCopyWith<$Res> { + _$DestinationCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of Destination + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? ref = null, + Object? name = null, + Object? country = null, + Object? continent = null, + Object? knownFor = null, + Object? tags = null, + Object? imageUrl = null, + }) { + return _then(_value.copyWith( + ref: null == ref + ? _value.ref + : ref // ignore: cast_nullable_to_non_nullable + as String, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + country: null == country + ? _value.country + : country // ignore: cast_nullable_to_non_nullable + as String, + continent: null == continent + ? _value.continent + : continent // ignore: cast_nullable_to_non_nullable + as String, + knownFor: null == knownFor + ? _value.knownFor + : knownFor // ignore: cast_nullable_to_non_nullable + as String, + tags: null == tags + ? _value.tags + : tags // ignore: cast_nullable_to_non_nullable + as List, + imageUrl: null == imageUrl + ? _value.imageUrl + : imageUrl // ignore: cast_nullable_to_non_nullable + as String, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$DestinationImplCopyWith<$Res> + implements $DestinationCopyWith<$Res> { + factory _$$DestinationImplCopyWith( + _$DestinationImpl value, $Res Function(_$DestinationImpl) then) = + __$$DestinationImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String ref, + String name, + String country, + String continent, + String knownFor, + List tags, + String imageUrl}); +} + +/// @nodoc +class __$$DestinationImplCopyWithImpl<$Res> + extends _$DestinationCopyWithImpl<$Res, _$DestinationImpl> + implements _$$DestinationImplCopyWith<$Res> { + __$$DestinationImplCopyWithImpl( + _$DestinationImpl _value, $Res Function(_$DestinationImpl) _then) + : super(_value, _then); + + /// Create a copy of Destination + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? ref = null, + Object? name = null, + Object? country = null, + Object? continent = null, + Object? knownFor = null, + Object? tags = null, + Object? imageUrl = null, + }) { + return _then(_$DestinationImpl( + ref: null == ref + ? _value.ref + : ref // ignore: cast_nullable_to_non_nullable + as String, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + country: null == country + ? _value.country + : country // ignore: cast_nullable_to_non_nullable + as String, + continent: null == continent + ? _value.continent + : continent // ignore: cast_nullable_to_non_nullable + as String, + knownFor: null == knownFor + ? _value.knownFor + : knownFor // ignore: cast_nullable_to_non_nullable + as String, + tags: null == tags + ? _value._tags + : tags // ignore: cast_nullable_to_non_nullable + as List, + imageUrl: null == imageUrl + ? _value.imageUrl + : imageUrl // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$DestinationImpl implements _Destination { + const _$DestinationImpl( + {required this.ref, + required this.name, + required this.country, + required this.continent, + required this.knownFor, + required final List tags, + required this.imageUrl}) + : _tags = tags; + + factory _$DestinationImpl.fromJson(Map json) => + _$$DestinationImplFromJson(json); + + /// e.g. 'alaska' + @override + final String ref; + + /// e.g. 'Alaska' + @override + final String name; + + /// e.g. 'United States' + @override + final String country; + + /// e.g. 'North America' + @override + final String continent; + + /// e.g. 'Alaska is a haven for outdoor enthusiasts ...' + @override + final String knownFor; + + /// e.g. ['Mountain', 'Off-the-beaten-path', 'Wildlife watching'] + final List _tags; + + /// e.g. ['Mountain', 'Off-the-beaten-path', 'Wildlife watching'] + @override + List get tags { + if (_tags is EqualUnmodifiableListView) return _tags; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_tags); + } + + /// e.g. 'https://storage.googleapis.com/tripedia-images/destinations/alaska.jpg' + @override + final String imageUrl; + + @override + String toString() { + return 'Destination(ref: $ref, name: $name, country: $country, continent: $continent, knownFor: $knownFor, tags: $tags, imageUrl: $imageUrl)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$DestinationImpl && + (identical(other.ref, ref) || other.ref == ref) && + (identical(other.name, name) || other.name == name) && + (identical(other.country, country) || other.country == country) && + (identical(other.continent, continent) || + other.continent == continent) && + (identical(other.knownFor, knownFor) || + other.knownFor == knownFor) && + const DeepCollectionEquality().equals(other._tags, _tags) && + (identical(other.imageUrl, imageUrl) || + other.imageUrl == imageUrl)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash(runtimeType, ref, name, country, continent, + knownFor, const DeepCollectionEquality().hash(_tags), imageUrl); + + /// Create a copy of Destination + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$DestinationImplCopyWith<_$DestinationImpl> get copyWith => + __$$DestinationImplCopyWithImpl<_$DestinationImpl>(this, _$identity); + + @override + Map toJson() { + return _$$DestinationImplToJson( + this, + ); + } +} + +abstract class _Destination implements Destination { + const factory _Destination( + {required final String ref, + required final String name, + required final String country, + required final String continent, + required final String knownFor, + required final List tags, + required final String imageUrl}) = _$DestinationImpl; + + factory _Destination.fromJson(Map json) = + _$DestinationImpl.fromJson; + + /// e.g. 'alaska' + @override + String get ref; + + /// e.g. 'Alaska' + @override + String get name; + + /// e.g. 'United States' + @override + String get country; + + /// e.g. 'North America' + @override + String get continent; + + /// e.g. 'Alaska is a haven for outdoor enthusiasts ...' + @override + String get knownFor; + + /// e.g. ['Mountain', 'Off-the-beaten-path', 'Wildlife watching'] + @override + List get tags; + + /// e.g. 'https://storage.googleapis.com/tripedia-images/destinations/alaska.jpg' + @override + String get imageUrl; + + /// Create a copy of Destination + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$DestinationImplCopyWith<_$DestinationImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/compass_app/model/lib/src/model/destination/destination.g.dart b/compass_app/model/lib/src/model/destination/destination.g.dart new file mode 100644 index 000000000..796e2bbbd --- /dev/null +++ b/compass_app/model/lib/src/model/destination/destination.g.dart @@ -0,0 +1,29 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'destination.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$DestinationImpl _$$DestinationImplFromJson(Map json) => + _$DestinationImpl( + ref: json['ref'] as String, + name: json['name'] as String, + country: json['country'] as String, + continent: json['continent'] as String, + knownFor: json['knownFor'] as String, + tags: (json['tags'] as List).map((e) => e as String).toList(), + imageUrl: json['imageUrl'] as String, + ); + +Map _$$DestinationImplToJson(_$DestinationImpl instance) => + { + 'ref': instance.ref, + 'name': instance.name, + 'country': instance.country, + 'continent': instance.continent, + 'knownFor': instance.knownFor, + 'tags': instance.tags, + 'imageUrl': instance.imageUrl, + }; diff --git a/compass_app/model/pubspec.yaml b/compass_app/model/pubspec.yaml new file mode 100644 index 000000000..da7ee8d11 --- /dev/null +++ b/compass_app/model/pubspec.yaml @@ -0,0 +1,17 @@ +name: compass_model +description: Compass App Data Model +publish_to: 'none' +version: 1.0.0 + +environment: + sdk: ^3.4.3 + +dev_dependencies: + build_runner: ^2.4.11 + freezed: ^2.5.7 + json_serializable: ^6.8.0 + lints: ^3.0.0 + test: ^1.24.0 +dependencies: + freezed_annotation: ^2.4.4 + json_annotation: ^4.9.0 diff --git a/compass_app/app/lib/data/services/placeholder b/compass_app/model/test/.gitkeep similarity index 100% rename from compass_app/app/lib/data/services/placeholder rename to compass_app/model/test/.gitkeep diff --git a/compass_app/server/.gitignore b/compass_app/server/.gitignore new file mode 100644 index 000000000..3a8579040 --- /dev/null +++ b/compass_app/server/.gitignore @@ -0,0 +1,3 @@ +# https://dart.dev/guides/libraries/private-files +# Created by `dart pub` +.dart_tool/ diff --git a/compass_app/server/README.md b/compass_app/server/README.md new file mode 100644 index 000000000..2929e34c0 --- /dev/null +++ b/compass_app/server/README.md @@ -0,0 +1,13 @@ +A server app built using [Shelf](https://pub.dev/packages/shelf). + +# Running the server + +## Running with the Dart SDK + +You can run the example with the [Dart SDK](https://dart.dev/get-dart) +like this: + +``` +$ dart run +Server listening on port 8080 +``` diff --git a/compass_app/server/analysis_options.yaml b/compass_app/server/analysis_options.yaml new file mode 100644 index 000000000..dee8927aa --- /dev/null +++ b/compass_app/server/analysis_options.yaml @@ -0,0 +1,30 @@ +# This file configures the static analysis results for your project (errors, +# warnings, and lints). +# +# This enables the 'recommended' set of lints from `package:lints`. +# This set helps identify many issues that may lead to problems when running +# or consuming Dart code, and enforces writing Dart using a single, idiomatic +# style and format. +# +# If you want a smaller set of lints you can change this to specify +# 'package:lints/core.yaml'. These are just the most critical lints +# (the recommended set includes the core lints). +# The core lints are also what is used by pub.dev for scoring packages. + +include: package:lints/recommended.yaml + +# Uncomment the following section to specify additional rules. + +# linter: +# rules: +# - camel_case_types + +# analyzer: +# exclude: +# - path/to/excluded/files/** + +# For more information about the core and recommended set of lints, see +# https://dart.dev/go/core-lints + +# For additional information about configuring this file, see +# https://dart.dev/guides/language/analysis-options diff --git a/compass_app/server/assets/destinations.json b/compass_app/server/assets/destinations.json new file mode 100644 index 000000000..be1f233c5 --- /dev/null +++ b/compass_app/server/assets/destinations.json @@ -0,0 +1,1235 @@ +[ + { + "ref": "alaska", + "name": "Alaska", + "country": "United States", + "continent": "North America", + "knownFor": "Alaska is a haven for outdoor enthusiasts and nature lovers. Visitors can experience glaciers, mountains, and wildlife, making it ideal for hiking, kayaking, and wildlife viewing. Alaska also offers a unique cultural experience with its rich Native American heritage and frontier spirit.", + "tags": ["Mountain", "Off-the-beaten-path", "Wildlife watching"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/alaska.jpg" + }, + { + "ref": "amalfi-coast", + "name": "Amalfi Coast", + "country": "Italy", + "continent": "Europe", + "knownFor": "Experience the breathtaking beauty of the Amalfi Coast, with its dramatic cliffs, colorful villages, and turquoise waters. Indulge in delicious Italian cuisine, explore charming towns like Positano and Amalfi, and soak up the sun on picturesque beaches.", + "tags": ["Beach", "Romantic", "Foodie"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/amalfi-coast.jpg" + }, + { + "ref": "amazon-rainforest", + "name": "Amazon Rainforest", + "country": "Brazil", + "continent": "South America", + "knownFor": "Immerse yourself in the biodiversity of the world's largest rainforest. Embark on jungle treks, spot exotic wildlife, and discover indigenous cultures. Take a boat trip down the Amazon River, explore the canopy on a zipline, and experience the unique ecosystem.", + "tags": ["Jungle", "Wildlife watching", "Adventure sports"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/amazon-rainforest.jpg" + }, + { + "ref": "andes-mountains", + "name": "The Andes Mountains", + "country": "South America", + "continent": "South America", + "knownFor": "The Andes Mountains, stretching along the western coast of South America, offer diverse landscapes and experiences. Visitors can trek to Machu Picchu in Peru, explore the salt flats of Salar de Uyuni in Bolivia, or visit the glaciers of Patagonia. The Andes also provide opportunities for skiing, mountaineering, and cultural encounters with indigenous communities.", + "tags": ["Mountain", "Hiking", "Cultural experiences"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/andes-mountains.jpg" + }, + { + "ref": "angkor-wat", + "name": "Angkor Wat", + "country": "Cambodia", + "continent": "Asia", + "knownFor": "Angkor Wat, a vast temple complex in Cambodia, is the largest religious monument in the world and a UNESCO World Heritage site. Visitors can explore the intricate carvings, towering spires, and vast courtyards, marveling at the architectural grandeur and rich history of the Khmer Empire.", + "tags": ["Historic", "Cultural experiences", "Sightseeing"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/angkor-wat.jpg" + }, + { + "ref": "antelope-canyon", + "name": "Antelope Canyon", + "country": "United States", + "continent": "North America", + "knownFor": "Experience the awe-inspiring beauty of Antelope Canyon, a slot canyon renowned for its flowing sandstone formations and mesmerizing light beams. Embark on guided tours to navigate the narrow passageways and capture stunning photographs. Learn about the Navajo Nation's history and culture, as the canyon is located on their land.", + "tags": ["Off-the-beaten-path", "Hiking", "Sightseeing"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/antelope-canyon.jpg" + }, + { + "ref": "aruba", + "name": "Aruba", + "country": "Aruba", + "continent": "South America", + "knownFor": "Indulge in the beauty of Aruba, a Caribbean paradise known for its pristine beaches, turquoise waters, and year-round sunshine. Relax on the white sands of Eagle Beach, explore the vibrant capital of Oranjestad, and discover hidden coves and natural wonders. Aruba offers a perfect escape for beach lovers and water sports enthusiasts.", + "tags": ["Beach", "Island", "Scuba diving"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/aruba.jpg" + }, + { + "ref": "asheville", + "name": "Asheville", + "country": "USA", + "continent": "North America", + "knownFor": "Asheville, nestled in the Blue Ridge Mountains of North Carolina, is a vibrant city known for its arts scene, craft breweries, and outdoor activities. Visitors can explore the historic Biltmore Estate, hike in the surrounding mountains, sample local beers, and enjoy the city's eclectic shops and restaurants.", + "tags": ["City", "Hiking", "Cultural experiences"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/asheville.jpg" + }, + { + "ref": "azores", + "name": "Azores", + "country": "Portugal", + "continent": "Europe", + "knownFor": "This archipelago in the mid-Atlantic boasts stunning volcanic landscapes, lush green hills, and dramatic coastlines. Hike to crater lakes, soak in natural hot springs, or go whale watching in the surrounding waters. With its relaxed atmosphere and unique culture, the Azores is a perfect destination for nature lovers and adventurers seeking an off-the-beaten-path experience.", + "tags": ["Island", "Off-the-beaten-path", "Hiking"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/azores.jpg" + }, + { + "ref": "bali", + "name": "Bali", + "country": "Indonesia", + "continent": "Asia", + "knownFor": "Discover the cultural heart of Indonesia on the island of Bali. Explore ancient temples, experience vibrant Hindu traditions, and relax on beautiful beaches. Practice yoga and meditation, indulge in spa treatments, and enjoy the island's lush natural beauty.", + "tags": ["Island", "Cultural experiences", "Wellness retreats"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/bali.jpg" + }, + { + "ref": "banff-national-park", + "name": "Banff National Park", + "country": "Canada", + "continent": "North America", + "knownFor": "Nestled in the Canadian Rockies, Banff National Park offers stunning mountain scenery, turquoise lakes, and abundant wildlife. Hike to Lake Louise, explore Johnston Canyon, and enjoy outdoor activities year-round.", + "tags": ["Mountain", "Hiking", "Wildlife watching"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/banff-national-park.jpg" + }, + { + "ref": "belize", + "name": "Belize", + "country": "Belize", + "continent": "North America", + "knownFor": "Embark on an unforgettable adventure in Belize, a Central American gem boasting lush rainforests, ancient Maya ruins, and the world's second-largest barrier reef. Explore the mysteries of Caracol and Xunantunich, dive into the Great Blue Hole, and discover diverse marine life. Belize offers a unique blend of cultural exploration and eco-tourism.", + "tags": ["Jungle", "Historic", "Scuba diving"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/belize.jpg" + }, + { + "ref": "bhutan", + "name": "Bhutan", + "country": "Bhutan", + "continent": "Asia", + "knownFor": "Discover the mystical kingdom of Bhutan, nestled in the Himalayas. Explore ancient monasteries, hike through pristine valleys, and experience the unique culture and traditions. Immerse yourself in the spiritual atmosphere and embrace the concept of Gross National Happiness.", + "tags": ["Mountain", "Cultural experiences", "Off-the-beaten-path"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/bhutan.jpg" + }, + { + "ref": "big-island-hawaii", + "name": "Big Island", + "country": "United States", + "continent": "North America", + "knownFor": "The Big Island of Hawaii offers diverse landscapes, from volcanic craters and black sand beaches to lush rainforests and snow-capped mountains. Visitors can witness the fiery glow of Kilauea volcano, snorkel with manta rays, and stargaze atop Mauna Kea.", + "tags": ["Island", "Hiking", "Snorkeling"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/big-island-hawaii.jpg" + }, + { + "ref": "big-sur", + "name": "Big Sur", + "country": "United States", + "continent": "North America", + "knownFor": "Experience the breathtaking beauty of California's rugged coastline along Big Sur. Drive the iconic Pacific Coast Highway, stopping at dramatic cliffs, hidden coves, and redwood forests. Enjoy hiking, camping, or simply soaking up the awe-inspiring views of this natural wonderland.", + "tags": ["Road trip destination", "Secluded", "Hiking"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/big-sur.jpg" + }, + { + "ref": "bora-bora", + "name": "Bora Bora", + "country": "French Polynesia", + "continent": "Oceania", + "knownFor": "Bora Bora is synonymous with luxury and tranquility. Overwater bungalows perched above turquoise lagoons offer a unique and indulgent experience. Visitors can enjoy snorkeling, diving, and swimming amidst vibrant coral reefs and diverse marine life. The island's lush interior provides opportunities for hiking and exploring, while Polynesian culture adds a touch of exotic charm.", + "tags": ["Island", "Luxury", "Secluded"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/bora-bora.jpg" + }, + { + "ref": "botswana", + "name": "Okavango Delta", + "country": "Botswana", + "continent": "Africa", + "knownFor": "The Okavango Delta, a unique inland delta in Botswana, is a haven for wildlife enthusiasts. Visitors can embark on safari adventures, encountering elephants, lions, hippos, and a diverse array of birds. The delta's waterways offer opportunities for mokoro (canoe) excursions and boat tours, providing a close-up view of the abundant wildlife and stunning landscapes.", + "tags": ["Wildlife watching", "Adventure sports", "Luxury"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/botswana.jpg" + }, + { + "ref": "bruges", + "name": "Bruges", + "country": "Belgium", + "continent": "Europe", + "knownFor": "Step back in time in this charming medieval city with its cobblestone streets, canals, and well-preserved architecture. Explore the historic Markt square, indulge in delicious Belgian chocolate and beer, or take a boat tour through the canals. Bruges offers a romantic and picturesque escape for history buffs and those seeking a quintessential European experience.", + "tags": ["City", "Historic", "Romantic"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/bruges.jpg" + }, + { + "ref": "brunei", + "name": "Brunei", + "country": "Brunei", + "continent": "Asia", + "knownFor": "This small sultanate on the island of Borneo offers a fascinating blend of culture and nature. Visitors can explore the opulent Sultan Omar Ali Saifuddin Mosque, delve into the lush rainforests, and discover the unique water village of Kampong Ayer.", + "tags": ["Secluded", "Cultural experiences", "Island"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/brunei.jpg" + }, + { + "ref": "budapest", + "name": "Budapest", + "country": "Hungary", + "continent": "Europe", + "knownFor": "Discover the charm of Budapest, a historic city divided by the Danube River. Explore Buda's Castle District with its medieval streets and Fisherman's Bastion, offering panoramic views. Relax in the thermal baths, a legacy of the Ottoman era, or visit the Hungarian Parliament Building, a stunning example of Gothic Revival architecture.", + "tags": ["City", "Historic", "Cultural experiences"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/budapest.jpg" + }, + { + "ref": "burgundy", + "name": "Burgundy", + "country": "France", + "continent": "Europe", + "knownFor": "Burgundy, a region in eastern France, is renowned for its world-class wines, charming villages, and rich history. Explore vineyards, indulge in wine tastings, and visit medieval castles and abbeys. Cycle through rolling hills, savor gourmet cuisine, and experience the art de vivre of this picturesque region.", + "tags": ["Rural", "Wine tasting", "Cultural experiences"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/burgundy.jpg" + }, + { + "ref": "cambodia", + "name": "Cambodia", + "country": "Cambodia", + "continent": "Asia", + "knownFor": "Cambodia, a Southeast Asian nation, is a captivating blend of ancient wonders, natural beauty, and vibrant culture. Explore the magnificent temples of Angkor, relax on pristine beaches, and cruise along the Mekong River. Experience the warmth of the Cambodian people, savor delicious cuisine, and discover the rich history of this fascinating country.", + "tags": ["Historic", "Cultural experiences", "Beach"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/cambodia.jpg" + }, + { + "ref": "canadian-rockies", + "name": "Canadian Rockies", + "country": "Canada", + "continent": "North America", + "knownFor": "Embark on an adventure through the majestic Canadian Rockies, where towering mountains, turquoise lakes, and glaciers create a breathtaking landscape. Hike through scenic trails, go skiing or snowboarding in world-class resorts, and encounter diverse wildlife. Experience the thrill of outdoor activities and the beauty of untouched nature.", + "tags": ["Mountain", "Hiking", "Adventure sports"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/canadian-rockies.jpg" + }, + { + "ref": "canary-islands", + "name": "Canary Islands", + "country": "Spain", + "continent": "Europe", + "knownFor": "Discover the volcanic beauty of the Canary Islands, a Spanish archipelago off the coast of Africa. Explore diverse landscapes, from the dramatic volcanic peaks of Tenerife and Mount Teide to the golden beaches of Gran Canaria and Fuerteventura. Enjoy water sports, hiking, and stargazing in this island paradise.", + "tags": ["Island", "Beach", "Hiking"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/canary-islands.jpg" + }, + { + "ref": "cappadocia", + "name": "Cappadocia", + "country": "Turkey", + "continent": "Asia", + "knownFor": "Embark on a magical journey through a surreal landscape of fairy chimneys, cave dwellings, and underground cities. Soar above the valleys in a hot air balloon, explore ancient rock-cut churches, and experience the unique culture and hospitality of the region.", + "tags": ["Historic", "Off-the-beaten-path", "Adventure sports"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/cappadocia.jpg" + }, + { + "ref": "chilean-lake-district", + "name": "Chilean Lake District", + "country": "Chile", + "continent": "South America", + "knownFor": "The Chilean Lake District is a paradise for nature lovers, with snow-capped volcanoes, turquoise lakes, and lush forests. Hike in national parks, go kayaking on the lakes, and enjoy the tranquility of the surroundings. The region's German heritage adds a unique cultural element.", + "tags": ["Lake", "Mountain", "Hiking"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/chilean-lake-district.jpg" + }, + { + "ref": "cinque-terre", + "name": "Cinque Terre", + "country": "Italy", + "continent": "Europe", + "knownFor": "Explore the picturesque villages of Cinque Terre, perched on the rugged Italian Riviera coastline. Hike the scenic trails connecting the five colorful towns, each with its unique character. Enjoy fresh seafood, local wines, and breathtaking views of the Mediterranean Sea.", + "tags": ["Hiking", "Coastal", "Foodie"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/cinque-terre.jpg" + }, + { + "ref": "colombia", + "name": "Colombia", + "country": "Colombia", + "continent": "South America", + "knownFor": "Colombia is a vibrant country with a diverse landscape, ranging from the Andes Mountains to the Caribbean coast. Explore the colonial city of Cartagena, hike in the Cocora Valley, or dance the night away in Medellín. Discover the coffee region, learn about the indigenous cultures, and experience the warmth of the Colombian people.", + "tags": ["City", "Mountain", "Cultural experiences"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/colombia.jpg" + }, + { + "ref": "corsica", + "name": "Corsica", + "country": "France", + "continent": "Europe", + "knownFor": "This mountainous Mediterranean island offers a diverse landscape of rugged mountains, pristine beaches, and charming villages. Visitors can enjoy hiking, water sports, exploring historical sites, and experiencing the unique Corsican culture.", + "tags": ["Mountain", "Beach", "Cultural experiences"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/corsica.jpg" + }, + { + "ref": "costa-rica", + "name": "Osa Peninsula", + "country": "Costa Rica", + "continent": "North America", + "knownFor": "A haven for eco-tourism, the Osa Peninsula boasts incredible biodiversity, lush rainforests, and pristine beaches. Adventure seekers can go zip-lining, kayaking, and hiking, while nature enthusiasts can spot monkeys, sloths, and exotic birds. The Corcovado National Park, known as one of the most biodiverse places on Earth, is a must-visit for wildlife watching.", + "tags": ["Jungle", "Secluded", "Wildlife watching"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/costa-rica.jpg" + }, + { + "ref": "dubai", + "name": "Dubai", + "country": "United Arab Emirates", + "continent": "Asia", + "knownFor": "Dubai is a modern metropolis known for its towering skyscrapers, luxurious shopping malls, and extravagant attractions. Visitors can experience the thrill of the Burj Khalifa, the world's tallest building, shop at the Dubai Mall, or enjoy a desert safari. With its vibrant nightlife and world-class dining scene, Dubai offers a truly cosmopolitan experience.", + "tags": ["City", "Luxury", "Shopping"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/dubai.jpg" + }, + { + "ref": "dubrovnik", + "name": "Dubrovnik", + "country": "Croatia", + "continent": "Europe", + "knownFor": "Dubrovnik, the \"Pearl of the Adriatic\", is a historic walled city renowned for its stunning architecture, ancient city walls, and breathtaking coastal views. Visitors can explore historical sites, enjoy boat trips, and experience the vibrant cultural scene.", + "tags": ["Historic", "Sightseeing", "City"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/dubrovnik.jpg" + }, + { + "ref": "ethiopia", + "name": "Ethiopia", + "country": "Ethiopia", + "continent": "Africa", + "knownFor": "Ethiopia, a landlocked country in the Horn of Africa, is a unique destination with ancient history, diverse landscapes, and rich culture. Explore the rock-hewn churches of Lalibela, trek through the Simien Mountains, and witness the vibrant tribal traditions. From bustling cities to remote villages, Ethiopia offers an unforgettable journey.", + "tags": ["Off-the-beaten-path", "Cultural experiences", "Hiking"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/ethiopia.jpg" + }, + { + "ref": "fiesole", + "name": "Fiesole", + "country": "Italy", + "continent": "Europe", + "knownFor": "Step back in time in Fiesole, a charming hilltop town overlooking Florence, Italy. Explore Etruscan and Roman ruins, visit the beautiful Fiesole Cathedral, and wander through quaint streets lined with historic buildings. Enjoy breathtaking views of the Tuscan countryside and escape the hustle and bustle of Florence.", + "tags": ["Historic", "Rural", "Romantic"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/fiesole.jpg" + }, + { + "ref": "fiji", + "name": "Fiji", + "country": "Fiji", + "continent": "Oceania", + "knownFor": "Fiji, an archipelago in the South Pacific, is renowned for its pristine beaches, crystal-clear waters, and lush rainforests. Visitors can indulge in water activities like snorkeling, scuba diving, and surfing, or explore the islands' rich cultural heritage and traditions. Fiji is also a popular destination for honeymoons and romantic getaways.", + "tags": ["Island", "Beach", "Scuba diving"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/fiji.jpg" + }, + { + "ref": "french-alps", + "name": "French Alps", + "country": "France", + "continent": "Europe", + "knownFor": "The French Alps offer stunning mountain scenery, charming villages, and world-class skiing. During the winter months, hit the slopes in renowned resorts like Chamonix and Val d'Isère. In the summer, enjoy hiking, mountain biking, and paragliding.", + "tags": ["Mountain", "Skiing", "Hiking"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/french-alps.jpg" + }, + { + "ref": "french-polynesia", + "name": "French Polynesia", + "country": "France", + "continent": "Oceania", + "knownFor": "Escape to the paradise of French Polynesia, where overwater bungalows, turquoise lagoons, and pristine beaches await. Dive into the vibrant underwater world, explore volcanic landscapes, and experience Polynesian culture. Indulge in luxury resorts, romantic getaways, and unforgettable island hopping adventures.", + "tags": ["Tropical", "Island", "Luxury"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/french-polynesia.jpg" + }, + { + "ref": "french-riviera", + "name": "French Riviera", + "country": "France", + "continent": "Europe", + "knownFor": "Discover the glamour of the French Riviera, where glamorous beaches, luxury yachts, and charming coastal towns line the Mediterranean coast. Explore the vibrant city of Nice, visit the iconic Monte Carlo casino, and soak up the sun on the beaches of Cannes. Experience the region's luxurious lifestyle, stunning scenery, and vibrant nightlife.", + "tags": ["Beach", "Luxury", "Nightlife"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/french-riviera.jpg" + }, + { + "ref": "galapagos-islands", + "name": "Galapagos Islands", + "country": "Ecuador", + "continent": "South America", + "knownFor": "Embark on a once-in-a-lifetime adventure to the Galapagos Islands, a living laboratory of evolution. Observe unique wildlife, including giant tortoises, marine iguanas, and blue-footed boobies. Go snorkeling or diving among vibrant coral reefs and explore volcanic landscapes.", + "tags": ["Island", "Wildlife watching", "Scuba diving"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/galapagos-islands.jpg" + }, + { + "ref": "galicia", + "name": "Galicia", + "country": "Spain", + "continent": "Europe", + "knownFor": "This region in northwestern Spain boasts rugged coastlines, green landscapes, and a rich Celtic heritage. Visitors can enjoy fresh seafood, explore charming towns, embark on the Camino de Santiago pilgrimage, and discover the unique Galician culture.", + "tags": ["Hiking", "Cultural experiences", "Foodie"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/galicia.jpg" + }, + { + "ref": "grand-canyon", + "name": "Grand Canyon", + "country": "United States", + "continent": "North America", + "knownFor": "Witness the awe-inspiring vastness and natural beauty of the Grand Canyon, a UNESCO World Heritage Site. Hike along the rim for breathtaking panoramic views, descend into the canyon for a challenging adventure, or raft down the Colorado River. The Grand Canyon offers an unforgettable experience for nature enthusiasts and adventure seekers.", + "tags": ["Mountain", "Hiking", "Adventure sports"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/grand-canyon.jpg" + }, + { + "ref": "great-barrier-reef", + "name": "Great Barrier Reef", + "country": "Australia", + "continent": "Australia", + "knownFor": "The Great Barrier Reef is the world's largest coral reef system, teeming with marine life. Snorkel or scuba dive among colorful corals, spot tropical fish, and experience the underwater wonders of this natural treasure.", + "tags": ["Snorkeling", "Scuba diving", "Adventure sports"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/great-barrier-reef.jpg" + }, + { + "ref": "great-bear-rainforest", + "name": "Great Bear Rainforest", + "country": "Canada", + "continent": "North America", + "knownFor": "The Great Bear Rainforest is a vast and pristine wilderness area on the Pacific coast of British Columbia. It is home to a diverse array of wildlife, including grizzly bears, black bears, wolves, and whales. Visitors can explore the rainforest by boat, kayak, or on foot, and experience the magic of this untouched ecosystem.", + "tags": ["Wildlife watching", "Hiking", "Eco-conscious"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/great-bear-rainforest.jpg" + }, + { + "ref": "greek-islands", + "name": "Greek Islands", + "country": "Greece", + "continent": "Europe", + "knownFor": "Island hop through the Greek Islands, each with its own unique charm and allure. Explore ancient ruins, relax on pristine beaches, and indulge in delicious Greek cuisine. Experience the vibrant nightlife of Mykonos, the romantic sunsets of Santorini, and the historical treasures of Crete. Discover a world of crystal-clear waters, whitewashed villages, and endless sunshine.", + "tags": ["Island", "Beach", "Historic"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/greek-islands.jpg" + }, + { + "ref": "greenland", + "name": "Ilulissat Icefjord", + "country": "Greenland", + "continent": "North America", + "knownFor": "Ilulissat Icefjord is a UNESCO World Heritage site and a breathtaking natural wonder with massive icebergs calving from the Sermeq Kujalleq glacier. Visitors can take boat tours to witness the stunning ice formations, go hiking in the surrounding area, and experience the unique culture of Greenland.", + "tags": ["Off-the-beaten-path", "Adventure sports", "Wildlife watching"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/greenland.jpg" + }, + { + "ref": "guadeloupe", + "name": "Guadeloupe", + "country": "France", + "continent": "North America", + "knownFor": "Guadeloupe, a butterfly-shaped archipelago in the Caribbean, is a French overseas territory boasting stunning natural landscapes. With its lush rainforests, volcanic peaks, and white-sand beaches, it's a paradise for outdoor enthusiasts. Hike to cascading waterfalls, explore vibrant coral reefs, and savor the unique blend of French and Creole culture.", + "tags": ["Island", "Tropical", "Hiking"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/guadeloupe.jpg" + }, + { + "ref": "guatemala", + "name": "Lake Atitlán", + "country": "Guatemala", + "continent": "North America", + "knownFor": "Lake Atitlán, surrounded by volcanoes and traditional Mayan villages, offers a scenic and cultural experience in Guatemala. Visitors can explore the lakeside towns, hike to viewpoints, visit Mayan ruins, and learn about local traditions. The lake also provides opportunities for kayaking, swimming, and boat trips.", + "tags": ["Lake", "Cultural experiences", "Hiking"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/guatemala.jpg" + }, + { + "ref": "ha-long-bay", + "name": "Ha Long Bay", + "country": "Vietnam", + "continent": "Asia", + "knownFor": "Ha Long Bay is a breathtaking UNESCO World Heritage Site, featuring thousands of limestone islands and islets rising from emerald waters. Visitors can cruise through the bay, explore hidden caves, kayak among the karst formations, and experience the unique beauty of this natural wonder.", + "tags": ["Island", "Secluded", "Sightseeing"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/ha-long-bay.jpg" + }, + { + "ref": "harrisburg", + "name": "Harrisburg", + "country": "USA", + "continent": "North America", + "knownFor": "Discover the charm of Harrisburg, Pennsylvania's historic capital city. Explore the impressive Pennsylvania State Capitol Building, delve into history at the National Civil War Museum, and enjoy family fun at City Island. With its scenic riverfront location, Harrisburg offers a blend of cultural attractions and outdoor activities.", + "tags": ["City", "Historic", "Family-friendly"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/harrisburg.jpg" + }, + { + "ref": "havana", + "name": "Havana", + "country": "Cuba", + "continent": "North America", + "knownFor": "Step back in time in Havana, the vibrant capital of Cuba. Stroll along the Malecón, a seaside promenade, and admire the colorful vintage cars. Explore Old Havana, a UNESCO World Heritage Site, with its colonial architecture and lively squares. Immerse yourself in Cuban culture, music, and dance.", + "tags": ["City", "Historic", "Cultural experiences"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/havana.jpg" + }, + { + "ref": "ho-chi-minh-city", + "name": "Ho Chi Minh City", + "country": "Vietnam", + "continent": "Asia", + "knownFor": "Ho Chi Minh City is a vibrant metropolis with a rich history and delicious street food. Explore the bustling markets, visit historical landmarks like the Cu Chi Tunnels, and immerse yourself in the city's energetic atmosphere. The blend of French colonial architecture and modern skyscrapers creates a unique cityscape.", + "tags": ["City", "Cultural experiences", "Food tours"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/ho-chi-minh-city.jpg" + }, + { + "ref": "iceland", + "name": "Iceland", + "country": "Iceland", + "continent": "Europe", + "knownFor": "Iceland's dramatic landscapes include glaciers, volcanoes, geysers, and waterfalls. Explore the Golden Circle, relax in geothermal pools, and witness the Northern Lights in winter.", + "tags": ["Secluded", "Adventure sports", "Winter destination"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/iceland.jpg" + }, + { + "ref": "japan-alps", + "name": "Japanese Alps", + "country": "Japan", + "continent": "Asia", + "knownFor": "The Japanese Alps offer stunning mountain scenery, traditional villages, and outdoor adventures. Hike through the Kamikochi Valley, ski in Hakuba, or soak in the onsen hot springs. Visit Matsumoto Castle, a historic landmark, and experience the unique culture of the mountain communities.", + "tags": ["Mountain", "Hiking", "Skiing"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/japan-alps.jpg" + }, + { + "ref": "jeju-island", + "name": "Jeju Island", + "country": "South Korea", + "continent": "Asia", + "knownFor": "Escape to the volcanic paradise of Jeju Island, a popular destination off the coast of South Korea. Discover stunning natural landscapes, from volcanic craters and lava tubes to pristine beaches and waterfalls. Explore unique museums, hike Mount Hallasan, and relax in charming coastal towns.", + "tags": ["Island", "Hiking", "Secluded"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/jeju-island.jpg" + }, + { + "ref": "jordan", + "name": "Petra", + "country": "Jordan", + "continent": "Asia", + "knownFor": "Petra, an ancient city carved into rose-colored sandstone cliffs, is a UNESCO World Heritage site and one of the New Seven Wonders of the World. Visitors can marvel at the Treasury, explore the Siq, and discover hidden tombs and temples. Petra offers a glimpse into the fascinating history and culture of the Nabataean civilization.", + "tags": ["Historic", "Cultural experiences", "Off-the-beaten-path"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/jordan.jpg" + }, + { + "ref": "kauai", + "name": "Kauai", + "country": "United States", + "continent": "North America", + "knownFor": "Escape to the Garden Isle of Kauai, a paradise of lush rainforests, dramatic cliffs, and pristine beaches. Hike the challenging Kalalau Trail along the Na Pali Coast, kayak the Wailua River, or relax on Poipu Beach. Discover hidden waterfalls, explore Waimea Canyon, and experience the island's laid-back atmosphere.", + "tags": ["Island", "Hiking", "Beach"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/kauai.jpg" + }, + { + "ref": "kenya", + "name": "Kenya", + "country": "Kenya", + "continent": "Africa", + "knownFor": "Embark on an unforgettable safari adventure in Kenya, home to diverse wildlife and stunning landscapes. Witness the Great Migration, encounter lions, elephants, and rhinos, and experience the rich culture of the Maasai people.", + "tags": ["Wildlife watching", "Safari", "Cultural experiences"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/kenya.jpg" + }, + { + "ref": "kyoto", + "name": "Kyoto", + "country": "Japan", + "continent": "Asia", + "knownFor": "Kyoto, Japan's former capital, is a cultural treasure trove with numerous temples, shrines, and gardens. Experience traditional tea ceremonies, stroll through the Arashiyama Bamboo Grove, and immerse yourself in Japanese history and spirituality.", + "tags": ["Historic", "Cultural experiences", "City"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/kyoto.jpg" + }, + { + "ref": "lake-bled", + "name": "Lake Bled", + "country": "Slovenia", + "continent": "Europe", + "knownFor": "Nestled in the Julian Alps, Lake Bled is a fairytale-like destination with a stunning glacial lake, a charming island church, and a medieval castle perched on a cliff. Visitors can enjoy swimming, boating, hiking, and exploring the surrounding mountains. Bled is also known for its delicious cream cake and thermal springs.", + "tags": ["Lake", "Mountain", "Romantic"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/lake-bled.jpg" + }, + { + "ref": "lake-como", + "name": "Lake Como", + "country": "Italy", + "continent": "Europe", + "knownFor": "Escape to the picturesque shores of Lake Como, surrounded by charming villages, luxurious villas, and stunning mountain scenery. Take a boat tour on the lake, explore historic gardens, and indulge in fine dining and Italian wines. Enjoy hiking, biking, and water sports in the surrounding area.", + "tags": ["Lake", "Romantic", "Luxury"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/lake-como.jpg" + }, + { + "ref": "lake-district", + "name": "Lake District National Park", + "country": "England", + "continent": "Europe", + "knownFor": "Nestled in the heart of Cumbria, the Lake District offers breathtaking landscapes with rolling hills, shimmering lakes, and charming villages. Visitors can enjoy hiking, boating, and exploring the literary legacy of Beatrix Potter and William Wordsworth. The region is also known for its delicious local produce and cozy pubs.", + "tags": ["Lake", "Hiking", "Rural"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/lake-district.jpg" + }, + { + "ref": "lake-garda", + "name": "Lake Garda", + "country": "Italy", + "continent": "Europe", + "knownFor": "Lake Garda is the largest lake in Italy, known for its stunning scenery, charming towns, and historic sites. Visitors can enjoy swimming, sailing, windsurfing, and hiking in the surrounding mountains. The area is also famous for its lemon groves and olive oil production, offering delicious local cuisine and wine tasting experiences.", + "tags": ["Lake", "Mountain", "Food tours"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/lake-garda.jpg" + }, + { + "ref": "lake-tahoe", + "name": "Lake Tahoe", + "country": "USA", + "continent": "North America", + "knownFor": "Lake Tahoe offers a blend of outdoor adventure and stunning natural beauty. Visitors enjoy skiing in the winter and water sports like kayaking and paddleboarding in the summer. The crystal-clear lake, surrounded by mountains, provides breathtaking scenery and a relaxing atmosphere.", + "tags": ["Lake", "Mountain", "Hiking"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/lake-tahoe.jpg" + }, + { + "ref": "laos", + "name": "Laos", + "country": "Laos", + "continent": "Asia", + "knownFor": "Laos is a landlocked country in Southeast Asia, known for its laid-back atmosphere, stunning natural beauty, and ancient temples. Explore the UNESCO World Heritage city of Luang Prabang, kayak down the Mekong River, discover the Kuang Si Falls, and visit the Pak Ou Caves filled with thousands of Buddha statues.", + "tags": ["Off-the-beaten-path", "Cultural experiences", "River"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/laos.jpg" + }, + { + "ref": "lofoten-islands", + "name": "Lofoten Islands", + "country": "Norway", + "continent": "Europe", + "knownFor": "Experience the breathtaking beauty of the Arctic Circle with dramatic landscapes, towering mountains, and charming fishing villages. Hike scenic trails, kayak along pristine fjords, and marvel at the Northern Lights. Explore Viking history and indulge in fresh seafood delicacies.", + "tags": ["Island", "Secluded", "Hiking"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/lofoten-islands.jpg" + }, + { + "ref": "lombok", + "name": "Lombok", + "country": "Indonesia", + "continent": "Asia", + "knownFor": "Lombok, Bali's less-crowded neighbor, offers pristine beaches, lush rainforests, and the majestic Mount Rinjani volcano. Visitors can enjoy surfing, diving, hiking, and exploring the island's cultural attractions.", + "tags": ["Beach", "Mountain", "Adventure sports"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/lombok.jpg" + }, + { + "ref": "luang-prabang", + "name": "Luang Prabang", + "country": "Laos", + "continent": "Asia", + "knownFor": "Immerse yourself in the tranquility of Luang Prabang, a UNESCO World Heritage town in Laos. Visit ornate temples like Wat Xieng Thong, witness the alms-giving ceremony at dawn, and explore the night market. Cruise down the Mekong River, discover Kuang Si Falls, and experience the town's spiritual atmosphere.", + "tags": ["Off-the-beaten-path", "Cultural experiences", "River"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/luang-prabang.jpg" + }, + { + "ref": "madagascar", + "name": "Madagascar", + "country": "Madagascar", + "continent": "Africa", + "knownFor": "Madagascar, an island nation off the southeast coast of Africa, is a biodiversity hotspot with unique flora and fauna. Explore rainforests, baobab-lined avenues, and pristine beaches. Encounter lemurs, chameleons, and other endemic species, and discover the rich cultural heritage of the Malagasy people. Madagascar offers an unforgettable adventure for nature lovers.", + "tags": ["Island", "Wildlife watching", "Hiking"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/madagascar.jpg" + }, + { + "ref": "maldives", + "name": "Maldives", + "country": "Maldives", + "continent": "Asia", + "knownFor": "The Maldives, a tropical island nation, offers luxurious overwater bungalows, pristine beaches, and world-class diving. Relax on the white sand, swim in the crystal-clear waters, and explore the vibrant coral reefs. The Maldives is the perfect destination for a romantic getaway or a relaxing beach vacation.", + "tags": ["Island", "Beach", "Scuba diving"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/maldives.jpg" + }, + { + "ref": "mallorca", + "name": "Mallorca", + "country": "Spain", + "continent": "Europe", + "knownFor": "Mallorca offers a diverse experience, from stunning beaches and turquoise waters to charming villages and rugged mountains. Visitors can explore historic Palma, hike the Serra de Tramuntana, or simply relax on the beach and enjoy the Mediterranean sunshine.", + "tags": ["Beach", "Island", "Hiking"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/mallorca.jpg" + }, + { + "ref": "malta", + "name": "Malta", + "country": "Malta", + "continent": "Europe", + "knownFor": "Malta, an archipelago in the central Mediterranean, is a captivating blend of history, culture, and stunning natural beauty. Its ancient temples, fortified cities, and hidden coves attract history buffs and adventurers alike. From exploring the UNESCO-listed capital Valletta to diving in crystal-clear waters, Malta offers a diverse experience for every traveler.", + "tags": ["Island", "Historic", "Cultural experiences"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/malta.jpg" + }, + { + "ref": "marrakech", + "name": "Marrakech", + "country": "Morocco", + "continent": "Africa", + "knownFor": "Step into a world of vibrant colours and bustling souks in Marrakech. Explore the historic Medina, with its maze-like alleys and hidden treasures, or marvel at the intricate architecture of mosques and palaces. Indulge in the rich flavours of Moroccan cuisine and experience the unique culture of this magical city.", + "tags": ["Cultural experiences", "City", "Shopping"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/marrakech.jpg" + }, + { + "ref": "maui", + "name": "Maui", + "country": "United States", + "continent": "North America", + "knownFor": "Experience the diverse landscapes of Maui, from volcanic craters to lush rainforests and stunning beaches. Drive the scenic Road to Hana, watch the sunrise from Haleakala Crater, or snorkel in Molokini Crater. Enjoy water sports, whale watching, and the island's relaxed vibes.", + "tags": ["Island", "Road trip", "Beach"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/maui.jpg" + }, + { + "ref": "milford-sound", + "name": "Milford Sound", + "country": "New Zealand", + "continent": "Oceania", + "knownFor": "Milford Sound, nestled in Fiordland National Park, offers breathtaking landscapes with towering cliffs, cascading waterfalls, and pristine waters. Visitors can cruise the fiord, kayak among the peaks, or hike the Milford Track for a multi-day adventure. The area is also known for its rich biodiversity, including dolphins, seals, and penguins.", + "tags": ["Secluded", "Hiking", "Wildlife watching"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/milford-sound.jpg" + }, + { + "ref": "moab", + "name": "Moab", + "country": "United States", + "continent": "North America", + "knownFor": "Moab is an adventurer's paradise, renowned for its stunning red rock formations and world-class outdoor activities. Hiking, mountain biking, and off-roading are popular pursuits in Arches and Canyonlands National Parks. The Colorado River offers white-water rafting and kayaking, while the surrounding desert landscapes provide endless opportunities for exploration and discovery.", + "tags": ["Desert", "Adventure sports", "Off-the-beaten-path"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/moab.jpg" + }, + { + "ref": "montenegro", + "name": "Montenegro", + "country": "Montenegro", + "continent": "Europe", + "knownFor": "Montenegro is a small Balkan country with a dramatic coastline, soaring mountains, and charming towns. Explore the Bay of Kotor, a UNESCO World Heritage Site, hike in Durmitor National Park, or relax on the beaches of Budva. Discover the historic cities of Kotor and Cetinje and enjoy the local seafood specialties.", + "tags": ["Beach", "Mountain", "Historic"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/montenegro.jpg" + }, + { + "ref": "mosel-valley", + "name": "Mosel Valley", + "country": "Germany", + "continent": "Europe", + "knownFor": "Embark on a journey through picturesque vineyards and charming villages along the Mosel River. Explore medieval castles, indulge in world-renowned Riesling wines, and savor authentic German cuisine. Hike or bike along scenic trails, or take a leisurely river cruise to soak in the breathtaking landscapes.", + "tags": ["River", "Wine tasting", "Hiking"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/mosel-valley.jpg" + }, + { + "ref": "myanmar", + "name": "Myanmar", + "country": "Myanmar", + "continent": "Asia", + "knownFor": "Myanmar (formerly Burma) is a country rich in culture and history, with ancient temples, stunning landscapes, and friendly people. Explore the iconic Shwedagon Pagoda in Yangon, visit the temple complex of Bagan, and cruise along the Irrawaddy River.", + "tags": ["Secluded", "Cultural experiences", "Off-the-beaten-path"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/myanmar.jpg" + }, + { + "ref": "mykonos", + "name": "Mykonos", + "country": "Greece", + "continent": "Europe", + "knownFor": "Mykonos is a glamorous Greek island famous for its whitewashed houses, iconic windmills, and vibrant nightlife. Visitors can relax on beautiful beaches, explore charming towns, and indulge in delicious Greek cuisine. Mykonos is also known for its luxury hotels, designer boutiques, and lively beach clubs.", + "tags": ["Island", "Beach", "Nightlife"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/mykonos.jpg" + }, + { + "ref": "nairobi", + "name": "Nairobi", + "country": "Kenya", + "continent": "Africa", + "knownFor": "Nairobi, the bustling capital of Kenya, serves as a gateway to the country's renowned safari destinations. Visit the David Sheldrick Elephant Orphanage, explore the Karen Blixen Museum, and experience the vibrant nightlife and cultural scene.", + "tags": ["City", "Wildlife watching", "Cultural experiences"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/nairobi.jpg" + }, + { + "ref": "namibia", + "name": "Sossusvlei", + "country": "Namibia", + "continent": "Africa", + "knownFor": "Sossusvlei is a mesmerizing salt and clay pan surrounded by towering red sand dunes in the Namib Desert. Visitors can embark on scenic drives, climb the dunes for panoramic views, and capture breathtaking photos of the unique landscape. Sossusvlei is a photographer's paradise and a must-visit for desert enthusiasts.", + "tags": ["Desert", "Off-the-beaten-path", "Photography"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/namibia.jpg" + }, + { + "ref": "napa-valley", + "name": "Napa Valley", + "country": "United States", + "continent": "North America", + "knownFor": "Indulge in the world-renowned wine region of Napa Valley, California. Visit picturesque vineyards, sample exquisite wines, and savor gourmet cuisine at Michelin-starred restaurants. Explore charming towns like St. Helena and Yountville, or relax in luxurious spa resorts. Napa Valley offers a sophisticated and relaxing getaway for wine lovers and foodies.", + "tags": ["Wine tasting", "Food tours", "Luxury"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/napa-valley.jpg" + }, + { + "ref": "new-orleans", + "name": "New Orleans", + "country": "United States", + "continent": "North America", + "knownFor": "New Orleans is a vibrant city with a unique culture, known for its jazz music, Mardi Gras celebrations, and Creole cuisine. Explore the French Quarter, listen to live music on Frenchmen Street, or visit the historic cemeteries. Experience the nightlife, enjoy the local food, and immerse yourself in the spirit of New Orleans.", + "tags": ["City", "Cultural experiences", "Foodie"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/new-orleans.jpg" + }, + { + "ref": "new-zealand", + "name": "New Zealand", + "country": "New Zealand", + "continent": "Oceania", + "knownFor": "Embark on an adventure in New Zealand, a land of diverse landscapes, from snow-capped mountains and glaciers to geothermal wonders and lush rainforests. Hike through Fiordland National Park, explore the Waitomo Caves, and experience the thrill of bungee jumping and white-water rafting. Discover the Maori culture, indulge in delicious local cuisine, and marvel at the country's natural beauty.", + "tags": ["Adventure sports", "Hiking", "Cultural experiences"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/new-zealand.jpg" + }, + { + "ref": "newfoundland", + "name": "Newfoundland", + "country": "Canada", + "continent": "North America", + "knownFor": "Newfoundland boasts rugged coastlines, charming fishing villages, and abundant wildlife. Hike along scenic trails, go whale watching, and experience the unique local culture. The island's friendly people and lively music scene add to its appeal.", + "tags": ["Island", "Wildlife watching", "Hiking"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/newfoundland.jpg" + }, + { + "ref": "nicaragua", + "name": "Nicaragua", + "country": "Nicaragua", + "continent": "North America", + "knownFor": "Nicaragua offers a diverse landscape of volcanoes, lakes, beaches, and rainforests. Visitors can enjoy adventure activities like surfing, volcano boarding, and zip-lining, as well as exploring colonial cities and experiencing the rich Nicaraguan culture.", + "tags": ["Adventure sports", "Beach", "Cultural experiences"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/nicaragua.jpg" + }, + { + "ref": "northern-territory", + "name": "Kakadu National Park", + "country": "Australia", + "continent": "Oceania", + "knownFor": "Kakadu National Park is a UNESCO World Heritage site with diverse landscapes, including wetlands, sandstone escarpments, and ancient rock art sites. Visitors can explore Aboriginal culture, go bird watching, take boat tours through wetlands, and admire cascading waterfalls. Kakadu is a haven for wildlife, with crocodiles, wallabies, and numerous bird species.", + "tags": ["National Park", "Wildlife watching", "Cultural experiences"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/northern-territory.jpg" + }, + { + "ref": "oaxaca", + "name": "Oaxaca", + "country": "Mexico", + "continent": "North America", + "knownFor": "Immerse yourself in the vibrant culture and rich history of Oaxaca, Mexico. Explore ancient Zapotec ruins, visit colorful markets filled with handicrafts, and experience traditional festivals. Sample the region's renowned cuisine, including mole sauces and mezcal. Oaxaca offers a unique and authentic travel experience for culture enthusiasts and foodies.", + "tags": ["Cultural experiences", "Food tours", "Historic"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/oaxaca.jpg" + }, + { + "ref": "okavango-delta", + "name": "Okavango Delta", + "country": "Botswana", + "continent": "Africa", + "knownFor": "The Okavango Delta, a vast inland delta in Botswana, is a haven for wildlife. Explore the waterways by mokoro (traditional canoe) and witness elephants, lions, hippos, and an array of bird species in their natural habitat.", + "tags": ["River", "Wildlife watching", "Adventure sports"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/okavango-delta.jpg" + }, + { + "ref": "oman", + "name": "Oman", + "country": "Oman", + "continent": "Asia", + "knownFor": "Oman, a country on the Arabian Peninsula, is a hidden gem with dramatic landscapes, ancient forts, and warm hospitality. Explore the vast deserts, swim in turquoise wadis, and hike through rugged mountains. Visit traditional souks, experience Bedouin culture, and discover the unique blend of modernity and tradition in this captivating destination.", + "tags": ["Desert", "Secluded", "Cultural experiences"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/oman.jpg" + }, + { + "ref": "pagos-islands", + "name": "Galápagos Islands", + "country": "Ecuador", + "continent": "South America", + "knownFor": "A unique archipelago off the coast of Ecuador, the Galápagos Islands is a haven for wildlife enthusiasts. Encounter giant tortoises, marine iguanas, blue-footed boobies, and sea lions in their natural habitat. Snorkeling and diving opportunities reveal a vibrant underwater world.", + "tags": ["Island", "Wildlife watching", "Snorkeling"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/pagos-islands.jpg" + }, + { + "ref": "patagonia", + "name": "Patagonia", + "country": "Argentina and Chile", + "continent": "South America", + "knownFor": "Patagonia is a vast region at the southern tip of South America, known for its glaciers, mountains, and diverse wildlife. Visitors can embark on trekking adventures, witness the Perito Moreno Glacier, go kayaking, and spot penguins, whales, and other animals.", + "tags": ["Hiking", "Adventure sports", "Wildlife watching"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/patagonia.jpg" + }, + { + "ref": "peru", + "name": "Arequipa", + "country": "Peru", + "continent": "South America", + "knownFor": "Arequipa, known as the \"White City\" due to its buildings made of white volcanic stone, is a beautiful colonial city in southern Peru. Visitors can explore the historic center, visit the Santa Catalina Monastery, and enjoy stunning views of the surrounding volcanoes. Arequipa is also a gateway to the Colca Canyon, one of the deepest canyons in the world.", + "tags": ["City", "Historic", "Hiking"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/peru.jpg" + }, + { + "ref": "peruvian-amazon", + "name": "Peruvian Amazon", + "country": "Peru", + "continent": "South America", + "knownFor": "Venture into the heart of the Amazon rainforest, a biodiversity hotspot teeming with exotic wildlife and indigenous cultures. Embark on jungle treks, navigate the Amazon River, and spot unique flora and fauna. Experience the thrill of adventure travel and connect with nature in its purest form.", + "tags": ["Jungle", "Adventure sports", "Wildlife watching"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/peruvian-amazon.jpg" + }, + { + "ref": "phnom-penh", + "name": "Phnom Penh", + "country": "Cambodia", + "continent": "Asia", + "knownFor": "Immerse yourself in the vibrant culture and rich history of Phnom Penh, Cambodia's bustling capital. Visit the Royal Palace, explore ancient temples like Wat Phnom, and delve into the poignant history at the Tuol Sleng Genocide Museum. Experience the city's energetic nightlife and savor delicious Khmer cuisine.", + "tags": ["City", "Historic", "Cultural experiences"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/phnom-penh.jpg" + }, + { + "ref": "phuket", + "name": "Phuket", + "country": "Thailand", + "continent": "Asia", + "knownFor": "Relax on the stunning beaches of Phuket, Thailand's largest island. Explore the turquoise waters of the Andaman Sea, go snorkeling or scuba diving among coral reefs, or visit nearby islands like Phi Phi. Enjoy the vibrant nightlife, indulge in Thai massages, and experience the local culture. Phuket offers a perfect blend of relaxation and adventure for beach lovers and those seeking a tropical escape.", + "tags": ["Beach", "Island", "Snorkeling"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/phuket.jpg" + }, + { + "ref": "positano", + "name": "Positano", + "country": "Italy", + "continent": "Europe", + "knownFor": "Nestled along the Amalfi Coast, Positano enchants with its colorful cliffside houses, pebble beaches, and luxury boutiques. Explore the narrow streets, indulge in delicious Italian cuisine, and soak up the romantic atmosphere.", + "tags": ["Beach", "Romantic", "Luxury"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/positano.jpg" + }, + { + "ref": "prague", + "name": "Prague", + "country": "Czech Republic", + "continent": "Europe", + "knownFor": "Prague, with its stunning architecture and rich history, is a fairytale city. Explore the Prague Castle, wander through the Old Town Square, and enjoy a traditional Czech meal. The city's charming atmosphere and affordable prices make it a popular destination.", + "tags": ["City", "Historic", "Sightseeing"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/prague.jpg" + }, + { + "ref": "puerto-rico", + "name": "Puerto Rico", + "country": "United States", + "continent": "North America", + "knownFor": "Puerto Rico is a Caribbean island with a rich history, vibrant culture, and stunning natural beauty. Explore the historic forts of Old San Juan, relax on the beaches of Vieques, or hike in El Yunque National Forest. Experience the bioluminescent bays, go salsa dancing, and enjoy the local cuisine.", + "tags": ["Island", "Beach", "Historic"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/puerto-rico.jpg" + }, + { + "ref": "punta-cana", + "name": "Punta Cana", + "country": "Dominican Republic", + "continent": "North America", + "knownFor": "Punta Cana is a renowned beach destination with pristine white sand, crystal-clear waters, and luxurious resorts. Enjoy water sports, golf, or simply unwind by the ocean and experience the vibrant Dominican culture.", + "tags": ["Beach", "All-inclusive", "Family-friendly"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/punta-cana.jpg" + }, + { + "ref": "quebec-city", + "name": "Quebec City", + "country": "Canada", + "continent": "North America", + "knownFor": "Experience the European charm of Quebec City, a UNESCO World Heritage Site. Wander through the historic Old Town with its cobblestone streets and fortified walls, visit the iconic Chateau Frontenac, and admire the stunning views of the St. Lawrence River. Quebec City offers a unique blend of history, culture, and French Canadian charm for a memorable getaway.", + "tags": ["City", "Historic", "Cultural experiences"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/quebec-city.jpg" + }, + { + "ref": "queenstown", + "name": "Queenstown", + "country": "New Zealand", + "continent": "Oceania", + "knownFor": "Experience the adventure capital of the world in Queenstown, New Zealand. Surrounded by stunning mountains and Lake Wakatipu, this town offers everything from bungy jumping and skydiving to skiing and hiking.", + "tags": ["Adventure sports", "Lake", "Mountain"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/queenstown.jpg" + }, + { + "ref": "rajasthan", + "name": "Rajasthan", + "country": "India", + "continent": "Asia", + "knownFor": "Rajasthan, a state in northwestern India, is known for its opulent palaces, vibrant culture, and desert landscapes. Visitors can explore the cities of Jaipur, Jodhpur, and Udaipur, with their magnificent forts and palaces. The region also offers camel safaris, desert camping, and opportunities to experience traditional Rajasthani music and dance.", + "tags": ["Historic", "Desert", "Cultural experiences"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/rajasthan.jpg" + }, + { + "ref": "reunion-island", + "name": "Réunion Island", + "country": "France", + "continent": "Africa", + "knownFor": "This volcanic island boasts dramatic landscapes, including Piton de la Fournaise, one of the world's most active volcanoes. Hike through lush rainforests, relax on black sand beaches, and experience the unique Creole culture.", + "tags": ["Secluded", "Hiking", "Tropical"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/reunion-island.jpg" + }, + { + "ref": "rio-de-janeiro", + "name": "Rio de Janeiro", + "country": "Brazil", + "continent": "South America", + "knownFor": "Rio de Janeiro is a vibrant city known for its stunning beaches, iconic landmarks like Christ the Redeemer and Sugarloaf Mountain, and lively Carnival celebrations. Visitors can enjoy sunbathing, surfing, and exploring the city's diverse neighborhoods, experiencing the infectious energy and cultural richness of Brazil.", + "tags": ["City", "Beach", "Party"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/rio-de-janeiro.jpg" + }, + { + "ref": "salar-de-uyuni", + "name": "Salar de Uyuni", + "country": "Bolivia", + "continent": "South America", + "knownFor": "Witness the surreal beauty of the world's largest salt flats, a mesmerizing landscape that transforms into a giant mirror during the rainy season. Capture incredible photos, explore unique rock formations, and visit nearby lagoons teeming with flamingos.", + "tags": ["Desert", "Off-the-beaten-path", "Photography"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/salar-de-uyuni.jpg" + }, + { + "ref": "san-diego", + "name": "San Diego", + "country": "United States", + "continent": "North America", + "knownFor": "San Diego, a sunny coastal city in Southern California, boasts beautiful beaches, a world-famous zoo, and a vibrant cultural scene. Explore Balboa Park, visit the historic Gaslamp Quarter, and enjoy water sports along the Pacific coast.", + "tags": ["City", "Beach", "Family-friendly"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/san-diego.jpg" + }, + { + "ref": "san-miguel-de-allende", + "name": "San Miguel de Allende", + "country": "Mexico", + "continent": "North America", + "knownFor": "This colonial city in central Mexico is a UNESCO World Heritage site with stunning Spanish architecture, vibrant cultural events, and a thriving arts scene. Visitors can explore historic churches, wander through cobbled streets lined with colorful houses, and enjoy delicious Mexican cuisine. San Miguel de Allende is also a popular destination for art classes and workshops.", + "tags": ["City", "Historic", "Cultural experiences"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/san-miguel-de-allende.jpg" + }, + { + "ref": "san-sebastian", + "name": "San Sebastian", + "country": "Spain", + "continent": "Europe", + "knownFor": "Indulge in the culinary delights of this coastal paradise, renowned for its Michelin-starred restaurants and pintxos bars. Relax on the beautiful beaches, explore the charming Old Town, and hike or bike in the surrounding hills. Experience the vibrant culture and lively festivals of the Basque region.", + "tags": ["Beach", "City", "Foodie"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/san-sebastian.jpg" + }, + { + "ref": "santorini", + "name": "Santorini", + "country": "Greece", + "continent": "Europe", + "knownFor": "Santorini's iconic whitewashed villages perched on volcanic cliffs offer breathtaking views of the Aegean Sea. Explore charming Oia, visit ancient Akrotiri, and enjoy romantic sunsets with caldera views.", + "tags": ["Island", "Romantic", "Luxury"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/santorini.jpg" + }, + { + "ref": "sardinia", + "name": "Sardinia", + "country": "Italy", + "continent": "Europe", + "knownFor": "Experience the allure of Sardinia, a Mediterranean island boasting stunning coastlines, turquoise waters, and rugged mountains. Explore charming villages, ancient ruins, and secluded coves. Indulge in delicious Sardinian cuisine, hike scenic trails, and discover the island's rich history and culture.", + "tags": ["Island", "Beach", "Hiking"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/sardinia.jpg" + }, + { + "ref": "scotland", + "name": "Scotland", + "country": "United Kingdom", + "continent": "Europe", + "knownFor": "Scotland is known for its dramatic landscapes, historic castles, and vibrant cities. Explore the Scottish Highlands, visit Edinburgh Castle, and sample Scotch whisky. The country's rich history and culture, along with its friendly people, make it a captivating destination.", + "tags": ["Mountain", "Historic", "Cultural experiences"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/scotland.jpg" + }, + { + "ref": "scottish-highlands", + "name": "Scottish Highlands", + "country": "Scotland", + "continent": "Europe", + "knownFor": "The Scottish Highlands, a mountainous region in northern Scotland, is renowned for its rugged beauty, ancient castles, and rich history. Visitors can explore the dramatic landscapes through hiking, climbing, and scenic drives, or visit historic sites like Loch Ness and Eilean Donan Castle. The region is also famous for its whisky distilleries and traditional Highland culture.", + "tags": ["Mountain", "Historic", "Hiking"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/scottish-highlands.jpg" + }, + { + "ref": "seoul", + "name": "Seoul", + "country": "South Korea", + "continent": "Asia", + "knownFor": "Seoul is a vibrant metropolis blending modern skyscrapers with ancient palaces and temples. Visitors can explore historical landmarks, experience K-pop culture, indulge in delicious Korean cuisine, and enjoy the city's bustling nightlife.", + "tags": ["City", "Cultural experiences", "Nightlife"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/seoul.jpg" + }, + { + "ref": "serengeti-national-park", + "name": "Serengeti National Park", + "country": "Tanzania", + "continent": "Africa", + "knownFor": "Serengeti National Park is renowned for its incredible wildlife and the annual Great Migration, where millions of wildebeest, zebras, and other animals traverse the plains in search of fresh grazing. Visitors can embark on thrilling safari adventures, witness predator-prey interactions, and marvel at the diversity of the African savanna.", + "tags": ["Wildlife watching", "Safari", "Adventure"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/serengeti-national-park.jpg" + }, + { + "ref": "seville", + "name": "Seville", + "country": "Spain", + "continent": "Europe", + "knownFor": "Seville, the vibrant capital of Andalusia, is renowned for its flamenco dancing, Moorish architecture, and lively tapas bars. Explore the stunning Alcázar palace, witness a passionate flamenco performance, and wander through the charming Santa Cruz district.", + "tags": ["City", "Cultural experiences", "Food tours"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/seville.jpg" + }, + { + "ref": "singapore", + "name": "Singapore", + "country": "Singapore", + "continent": "Asia", + "knownFor": "Experience a vibrant mix of cultures, cutting-edge architecture, and lush green spaces in this dynamic city-state. Discover futuristic Gardens by the Bay, indulge in diverse culinary delights, and explore world-class shopping and entertainment options.", + "tags": ["City", "Cultural experiences", "Foodie"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/singapore.jpg" + }, + { + "ref": "slovenia", + "name": "Slovenia", + "country": "Slovenia", + "continent": "Europe", + "knownFor": "Slovenia is a small country in Central Europe with stunning alpine scenery, charming towns, and a rich history. Visit Lake Bled, a picturesque lake with a church on an island, explore the Postojna Cave, or hike in Triglav National Park. Discover the capital city of Ljubljana, enjoy the local wines, and experience the Slovenian hospitality.", + "tags": ["Lake", "Mountain", "City"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/slovenia.jpg" + }, + { + "ref": "sri-lanka", + "name": "Sri Lanka", + "country": "Sri Lanka", + "continent": "Asia", + "knownFor": "Sri Lanka is an island nation off the southern coast of India, known for its ancient ruins, beautiful beaches, and diverse wildlife. Visit the Sigiriya rock fortress, relax on the beaches of Bentota, or go on a safari in Yala National Park. Explore the tea plantations, experience the local culture, and enjoy the delicious Sri Lankan cuisine.", + "tags": ["Island", "Beach", "Wildlife watching"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/sri-lanka.jpg" + }, + { + "ref": "svalbard", + "name": "Svalbard", + "country": "Norway", + "continent": "Europe", + "knownFor": "Svalbard, an Arctic archipelago under Norwegian sovereignty, is a remote and captivating destination for adventurers and nature enthusiasts. Witness glaciers, fjords, and ice-covered landscapes. Spot polar bears, walruses, and reindeer, and experience the midnight sun or the northern lights. Svalbard offers a unique opportunity to explore the Arctic wilderness.", + "tags": ["Off-the-beaten-path", "Wildlife watching", "Winter destination"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/svalbard.jpg" + }, + { + "ref": "swiss-alps", + "name": "Swiss Alps", + "country": "Switzerland", + "continent": "Europe", + "knownFor": "Discover the breathtaking beauty of the Swiss Alps, a paradise for outdoor enthusiasts. Hike through scenic mountain trails, go skiing or snowboarding in world-class resorts, or take a scenic train ride through the mountains. Enjoy the fresh air, charming villages, and stunning scenery. The Swiss Alps offer an unforgettable experience for nature lovers and adventure seekers.", + "tags": ["Mountain", "Hiking", "Skiing"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/swiss-alps.jpg" + }, + { + "ref": "tasmania", + "name": "Tasmania", + "country": "Australia", + "continent": "Oceania", + "knownFor": "Discover the wild beauty of Tasmania, an island state off the coast of Australia. Explore Cradle Mountain-Lake St Clair National Park, with its rugged mountains and pristine lakes. Visit Port Arthur Historic Site, a former penal colony, or encounter unique wildlife like Tasmanian devils and quolls.", + "tags": ["Island", "Hiking", "Wildlife watching"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/tasmania.jpg" + }, + { + "ref": "tel-aviv", + "name": "Tel Aviv", + "country": "Israel", + "continent": "Asia", + "knownFor": "Experience the vibrant and cosmopolitan city of Tel Aviv, known for its beaches, Bauhaus architecture, and thriving nightlife. Relax on the sandy shores of the Mediterranean Sea, explore the trendy neighborhoods of Neve Tzedek and Florentin, and enjoy the city's diverse culinary scene. Tel Aviv offers a perfect blend of beach relaxation, cultural experiences, and exciting nightlife.", + "tags": ["City", "Beach", "Nightlife"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/tel-aviv.jpg" + }, + { + "ref": "trans-siberian-railway", + "name": "Trans-Siberian Railway", + "country": "Russia", + "continent": "Asia", + "knownFor": "The Trans-Siberian Railway is the longest railway line in the world, stretching over 9,000 kilometers from Moscow to Vladivostok. This epic journey offers travelers a unique opportunity to experience the vastness and diversity of Russia, passing through bustling cities, remote villages, and stunning natural landscapes.", + "tags": ["Adventure sports", "Cultural experiences", "Long-haul vacation"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/trans-siberian-railway.jpg" + }, + { + "ref": "transylvania", + "name": "Transylvania", + "country": "Romania", + "continent": "Europe", + "knownFor": "Transylvania, a region in Romania, is famous for its medieval towns, fortified churches, and stunning Carpathian Mountain scenery. Visitors can explore Bran Castle, associated with the Dracula legend, visit historic cities like Brasov and Sibiu, and hike or ski in the mountains. The region also offers opportunities to experience traditional Romanian culture and cuisine.", + "tags": ["Historic", "Mountain", "Cultural experiences"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/transylvania.jpg" + }, + { + "ref": "tulum", + "name": "Tulum", + "country": "Mexico", + "continent": "North America", + "knownFor": "Tulum seamlessly blends ancient Mayan history with modern bohemian vibes. Visitors can explore the Tulum Archaeological Site, perched on cliffs overlooking the Caribbean Sea, and discover well-preserved ruins. Pristine beaches offer relaxation and water activities, while the town's eco-chic atmosphere provides yoga retreats, wellness centers, and sustainable dining options.", + "tags": ["Beach", "Cultural experiences", "Wellness retreats"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/tulum.jpg" + }, + { + "ref": "turkish-riviera", + "name": "Turkish Riviera", + "country": "Turkey", + "continent": "Asia", + "knownFor": "The Turkish Riviera offers a mix of ancient ruins, stunning beaches, and turquoise waters. Explore the historical sites of Ephesus and Antalya, relax on the sandy shores, and enjoy water sports like sailing and snorkeling. The region's delicious cuisine and affordable prices add to its appeal.", + "tags": ["Beach", "Historic", "Sightseeing"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/turkish-riviera.jpg" + }, + { + "ref": "tuscany", + "name": "Tuscany", + "country": "Italy", + "continent": "Europe", + "knownFor": "Explore the rolling hills and vineyards of Tuscany, indulging in wine tastings and farm-to-table cuisine. Discover charming medieval towns, Renaissance art, and historic cities like Florence and Siena. Immerse yourself in the region's rich culture and art scene, or simply relax and soak up the idyllic scenery.", + "tags": ["Cultural experiences", "Food tours", "Wine tasting"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/tuscany.jpg" + }, + { + "ref": "us-virgin-islands", + "name": "US Virgin Islands", + "country": "United States", + "continent": "North America", + "knownFor": "Escape to the Caribbean paradise of the US Virgin Islands, where you can relax on pristine beaches, explore coral reefs, and experience the laid-back island lifestyle. Visit the historic towns of Charlotte Amalie and Christiansted, go sailing or snorkeling in crystal-clear waters, or simply soak up the sun. The US Virgin Islands offer a perfect tropical getaway for beach lovers and those seeking a relaxing escape.", + "tags": ["Island", "Beach", "Relaxing"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/us-virgin-islands.jpg" + }, + { + "ref": "vancouver-island", + "name": "Vancouver Island", + "country": "Canada", + "continent": "North America", + "knownFor": "Vancouver Island, located off Canada's Pacific coast, is a haven for nature lovers and adventure seekers. Explore the rugged coastline, ancient rainforests, and snow-capped mountains. Go whale watching, kayaking, or surfing, and discover charming towns and vibrant cities like Victoria. Vancouver Island offers a perfect blend of wilderness and urban experiences.", + "tags": ["Island", "Adventure sports", "Wildlife watching"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/vancouver-island.jpg" + }, + { + "ref": "vienna", + "name": "Vienna", + "country": "Austria", + "continent": "Europe", + "knownFor": "Step into the imperial city of Vienna, where grand palaces, historical landmarks, and elegant cafes exude charm and sophistication. Explore museums, art galleries, and renowned opera houses, or visit Schönbrunn Palace and delve into Habsburg history. Enjoy classical music concerts, indulge in Viennese pastries, and experience the city's rich cultural heritage.", + "tags": ["City", "Historic", "Cultural experiences"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/vienna.jpg" + }, + { + "ref": "vietnam", + "name": "Vietnam", + "country": "Vietnam", + "continent": "Asia", + "knownFor": "Vietnam offers a rich tapestry of culture, history, and natural beauty. Explore the bustling streets of Hanoi, cruise through the scenic Ha Long Bay, and discover the ancient town of Hoi An. From delicious street food to stunning landscapes, Vietnam is a destination that will captivate your senses.", + "tags": ["Cultural experiences", "Food tours", "Sightseeing"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/vietnam.jpg" + }, + { + "ref": "western-australia", + "name": "Western Australia", + "country": "Australia", + "continent": "Australia", + "knownFor": "Western Australia, the largest state in Australia, is a land of vast landscapes, stunning coastlines, and unique wildlife. Explore the vibrant city of Perth, swim with whale sharks at Ningaloo Reef, and discover the ancient rock formations of the Kimberley region. From wineries to deserts, Western Australia offers a diverse and unforgettable experience.", + "tags": ["Beach", "Road trip destination", "Wildlife watching"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/western-australia.jpg" + }, + { + "ref": "yellowstone-national-park", + "name": "Yellowstone National Park", + "country": "United States", + "continent": "North America", + "knownFor": "Witness the geothermal wonders of Yellowstone, with its geysers, hot springs, and mudpots. Observe abundant wildlife, including bison, elk, and wolves. Explore the Grand Canyon of the Yellowstone, go hiking or camping, and enjoy winter sports.", + "tags": ["National Park", "Wildlife watching", "Hiking"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/yellowstone-national-park.jpg" + }, + { + "ref": "yosemite-national-park", + "name": "Yosemite National Park", + "country": "United States", + "continent": "North America", + "knownFor": "Yosemite National Park, located in California's Sierra Nevada mountains, is renowned for its towering granite cliffs, giant sequoia trees, and stunning waterfalls. Visitors can enjoy hiking, camping, rock climbing, and exploring the park's natural wonders, including Yosemite Valley, Half Dome, and Glacier Point.", + "tags": ["Mountain", "Hiking", "Sightseeing"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/yosemite-national-park.jpg" + }, + { + "ref": "yucatan-peninsula", + "name": "Yucatan Peninsula", + "country": "Mexico", + "continent": "North America", + "knownFor": "Discover ancient Mayan ruins, explore vibrant coral reefs, and relax on pristine beaches in the Yucatan Peninsula. Dive into cenotes, swim with whale sharks, and experience the rich culture and history of this captivating region.", + "tags": ["Beach", "Historic", "Scuba diving"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/yucatan-peninsula.jpg" + }, + { + "ref": "zanzibar", + "name": "Zanzibar", + "country": "Tanzania", + "continent": "Africa", + "knownFor": "Zanzibar is a Tanzanian archipelago off the coast of East Africa, known for its stunning beaches, turquoise waters, and historical Stone Town. Visitors can relax on the beach, explore the UNESCO-listed Stone Town, go diving or snorkeling, and experience the island's unique blend of African, Arab, and European influences.", + "tags": ["Beach", "Cultural experiences", "Scuba diving"], + "imageUrl": "https://storage.googleapis.com/tripedia-images/destinations/zanzibar.jpg" + } +] diff --git a/compass_app/server/bin/compass_server.dart b/compass_app/server/bin/compass_server.dart new file mode 100644 index 000000000..925abe8b0 --- /dev/null +++ b/compass_app/server/bin/compass_server.dart @@ -0,0 +1,26 @@ +import 'dart:io'; + +import 'package:compass_server/routes/continent.dart'; +import 'package:compass_server/routes/destination.dart'; +import 'package:shelf/shelf.dart'; +import 'package:shelf/shelf_io.dart'; +import 'package:shelf_router/shelf_router.dart'; + +// Configure routes. +final _router = Router() + ..get('/continent', continentHandler) + ..get('/destination', destinationHandler); + +void main(List args) async { + // Use any available host or container IP (usually `0.0.0.0`). + final ip = InternetAddress.anyIPv4; + + // Configure a pipeline that logs requests. + final handler = + Pipeline().addMiddleware(logRequests()).addHandler(_router.call); + + // For running in containers, we respect the PORT environment variable. + final port = int.parse(Platform.environment['PORT'] ?? '8080'); + final server = await serve(handler, ip, port); + print('Server listening on port ${server.port}'); +} diff --git a/compass_app/server/lib/routes/continent.dart b/compass_app/server/lib/routes/continent.dart new file mode 100644 index 000000000..8ca75bc57 --- /dev/null +++ b/compass_app/server/lib/routes/continent.dart @@ -0,0 +1,39 @@ +import 'dart:convert'; + +import 'package:shelf/shelf.dart'; +import 'package:compass_model/model.dart'; + +final _continents = [ + Continent( + name: 'Europe', + imageUrl: 'https://rstr.in/google/tripedia/TmR12QdlVTT', + ), + Continent( + name: 'Asia', + imageUrl: 'https://rstr.in/google/tripedia/VJ8BXlQg8O1', + ), + Continent( + name: 'South America', + imageUrl: 'https://rstr.in/google/tripedia/flm_-o1aI8e', + ), + Continent( + name: 'Africa', + imageUrl: 'https://rstr.in/google/tripedia/-nzi8yFOBpF', + ), + Continent( + name: 'North America', + imageUrl: 'https://rstr.in/google/tripedia/jlbgFDrSUVE', + ), + Continent( + name: 'Oceania', + imageUrl: 'https://rstr.in/google/tripedia/vxyrDE-fZVL', + ), + Continent( + name: 'Australia', + imageUrl: 'https://rstr.in/google/tripedia/z6vy6HeRyvZ', + ), +]; + +Response continentHandler(Request req) { + return Response.ok(jsonEncode(_continents)); +} diff --git a/compass_app/server/lib/routes/destination.dart b/compass_app/server/lib/routes/destination.dart new file mode 100644 index 000000000..7c9fe4474 --- /dev/null +++ b/compass_app/server/lib/routes/destination.dart @@ -0,0 +1,9 @@ +import 'dart:io'; + +import 'package:shelf/shelf.dart'; + +Future destinationHandler(Request req) async { + final file = File('assets/destinations.json'); + final jsonString = await file.readAsString(); + return Response.ok(jsonString); +} diff --git a/compass_app/server/pubspec.yaml b/compass_app/server/pubspec.yaml new file mode 100644 index 000000000..8c28c3674 --- /dev/null +++ b/compass_app/server/pubspec.yaml @@ -0,0 +1,19 @@ +name: compass_server +description: A server app using the shelf package and Docker. +publish_to: 'none' +version: 1.0.0 + +environment: + sdk: ^3.4.3 + +dependencies: + args: ^2.4.0 + shelf: ^1.4.0 + shelf_router: ^1.1.0 + compass_model: + path: ../model + +dev_dependencies: + http: ^1.1.0 + lints: ^3.0.0 + test: ^1.24.0 diff --git a/compass_app/server/test/server_test.dart b/compass_app/server/test/server_test.dart new file mode 100644 index 000000000..d6cc659b8 --- /dev/null +++ b/compass_app/server/test/server_test.dart @@ -0,0 +1,53 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:compass_model/model.dart'; +import 'package:http/http.dart'; +import 'package:test/test.dart'; + +void main() { + final port = '8080'; + final host = 'http://0.0.0.0:$port'; + late Process p; + + setUp(() async { + p = await Process.start( + 'dart', + ['run', 'bin/compass_server.dart'], + environment: {'PORT': port}, + ); + // Wait for server to start and print to stdout. + await p.stdout.first; + }); + + tearDown(() => p.kill()); + + test('Get Continent end-point', () async { + // Query /continent end-point + final response = await get(Uri.parse('$host/continent')); + expect(response.statusCode, 200); + // Parse json response list + final list = jsonDecode(response.body) as List; + // Parse items + final continents = list.map((element) => Continent.fromJson(element)); + expect(continents.length, 7); + expect(continents.first.name, 'Europe'); + }); + + test('Get Destination end-point', () async { + // Query /destination end-point + final response = await get(Uri.parse('$host/destination')); + expect(response.statusCode, 200); + // Parse json response list + final list = jsonDecode(response.body) as List; + // Parse items + final destination = list.map((element) => Destination.fromJson(element)); + expect(destination.length, 137); + expect(destination.first.name, 'Alaska'); + }); + + test('404', () async { + final response = await get(Uri.parse('$host/foobar')); + expect(response.statusCode, 404); + }); +}