diff --git a/.gitignore b/.gitignore index 546f686..d797cd2 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ API/bin API/obj .idea +/rust-backend/.env.example diff --git a/Mobile/.gitignore b/Mobile/.gitignore index d90f52b..6b4559a 100644 --- a/Mobile/.gitignore +++ b/Mobile/.gitignore @@ -43,4 +43,5 @@ app.*.map.json /android/app/release environment.json -node_modules \ No newline at end of file +node_modules +.env \ No newline at end of file diff --git a/Mobile/lib/base/sidemenu.dart b/Mobile/lib/base/sidemenu.dart index 0208d7d..95032a5 100644 --- a/Mobile/lib/base/sidemenu.dart +++ b/Mobile/lib/base/sidemenu.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:mobile/api.dart' as api; +import 'package:mobile/services/api.dart' as api; import 'package:shared_preferences/shared_preferences.dart'; import 'package:google_fonts/google_fonts.dart'; import 'variables.dart'; @@ -96,7 +96,7 @@ class _SideMenuState extends State { Navigator.pushReplacementNamed(context, '/favorites'); }, ), - ListTile( + ListTile( title: const Text('Profile'), leading: const Icon(Icons.person), selected: _selectedIndex == 2, @@ -104,6 +104,14 @@ class _SideMenuState extends State { Navigator.pushReplacementNamed(context, '/profile'); }, ), + ListTile( + title: const Text('GuideBook'), + leading: const Icon(Icons.person), + selected: _selectedIndex == 5, + onTap: () { + Navigator.pushReplacementNamed(context, '/tourist-guide-book'); + }, + ), const Divider( color: Colors.grey, thickness: 2, diff --git a/Mobile/lib/createreview.dart b/Mobile/lib/createreview.dart index 4b7060b..86d9d7f 100644 --- a/Mobile/lib/createreview.dart +++ b/Mobile/lib/createreview.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:mobile/base/sidemenu.dart'; import 'models.dart' as models; -import 'api.dart' as api; +import 'services/api.dart' as api; import 'package:path/path.dart' as path; class CreateReviewPage extends StatefulWidget { diff --git a/Mobile/lib/editprofile.dart b/Mobile/lib/editprofile.dart index bc230b7..ff4b456 100644 --- a/Mobile/lib/editprofile.dart +++ b/Mobile/lib/editprofile.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:mobile/models.dart' as models; import 'package:shared_preferences/shared_preferences.dart'; -import 'api.dart' as api; +import 'services/api.dart' as api; import 'base/variables.dart'; class EditProfilePage extends StatefulWidget { diff --git a/Mobile/lib/favorites.dart b/Mobile/lib/favorites.dart index 8ac10bf..667abb5 100644 --- a/Mobile/lib/favorites.dart +++ b/Mobile/lib/favorites.dart @@ -1,5 +1,5 @@ import 'dart:convert'; -import 'api.dart' as api; +import 'services/api.dart' as api; import 'package:flutter/material.dart'; import 'base/sidemenu.dart'; // Import the base layout widget import 'models.dart'; diff --git a/Mobile/lib/login.dart b/Mobile/lib/login.dart index 6c21619..084e553 100644 --- a/Mobile/lib/login.dart +++ b/Mobile/lib/login.dart @@ -4,7 +4,7 @@ import 'package:mobile/models.dart' as models; import 'package:shared_preferences/shared_preferences.dart'; import 'package:google_fonts/google_fonts.dart'; import 'dart:convert'; -import 'api.dart' as api; +import 'services/api.dart' as api; import 'base/variables.dart'; class LoginPage extends StatefulWidget { diff --git a/Mobile/lib/main.dart b/Mobile/lib/main.dart index e593790..2214f47 100644 --- a/Mobile/lib/main.dart +++ b/Mobile/lib/main.dart @@ -8,13 +8,14 @@ import 'package:mobile/createreview.dart'; import 'package:mobile/favorites.dart'; import 'package:mobile/register.dart'; import 'package:mobile/reviewlist.dart'; +import 'package:mobile/touristguidebook.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'login.dart'; import 'base/sidemenu.dart'; import 'profile.dart'; import 'models.dart'; import 'package:geolocator/geolocator.dart'; -import 'api.dart' as api; +import 'services/api.dart' as api; import 'package:http/http.dart' as http; void main() async { @@ -48,6 +49,7 @@ class MyApp extends StatelessWidget { '/register': (context) => const RegisterPage(), '/reviews': (context) => const ReviewListPage(), '/create-review': (context) => const CreateReviewPage(), + '/tourist-guide-book': (context) => const TouristGuideBookPage(), }, ); } diff --git a/Mobile/lib/profile.dart b/Mobile/lib/profile.dart index 6fa34fc..121176c 100644 --- a/Mobile/lib/profile.dart +++ b/Mobile/lib/profile.dart @@ -4,7 +4,7 @@ import 'package:mobile/base/variables.dart'; import 'package:mobile/models.dart' as models; import 'package:shared_preferences/shared_preferences.dart'; import 'base/sidemenu.dart'; -import 'api.dart' as api; +import 'services/api.dart' as api; import 'editprofile.dart'; class ProfilePage extends StatefulWidget { diff --git a/Mobile/lib/register.dart b/Mobile/lib/register.dart index b5d9503..0e5df1b 100644 --- a/Mobile/lib/register.dart +++ b/Mobile/lib/register.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:mobile/base/sidemenu.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:mobile/base/variables.dart'; -import 'api.dart' as api; +import 'services/api.dart' as api; class RegisterPage extends StatefulWidget { const RegisterPage({super.key}); diff --git a/Mobile/lib/reviewlist.dart b/Mobile/lib/reviewlist.dart index 4dc2d1b..837d2cc 100644 --- a/Mobile/lib/reviewlist.dart +++ b/Mobile/lib/reviewlist.dart @@ -2,7 +2,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:mobile/base/sidemenu.dart'; import 'models.dart' as models; -import 'api.dart' as api; +import 'services/api.dart' as api; class ReviewListPage extends StatefulWidget { const ReviewListPage({super.key}); diff --git a/Mobile/lib/api.dart b/Mobile/lib/services/api.dart similarity index 98% rename from Mobile/lib/api.dart rename to Mobile/lib/services/api.dart index 7dc969f..e99a012 100644 --- a/Mobile/lib/api.dart +++ b/Mobile/lib/services/api.dart @@ -193,5 +193,3 @@ void logout() async { prefs.remove('refresh-token'); prefs.remove('id'); } - -//------------------------------OPENAI API------------------------------// diff --git a/Mobile/lib/services/openaiservice.dart b/Mobile/lib/services/openaiservice.dart new file mode 100644 index 0000000..a619232 --- /dev/null +++ b/Mobile/lib/services/openaiservice.dart @@ -0,0 +1,49 @@ +import 'package:http/http.dart' as http; +import 'dart:convert'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; + +class OpenAIService { + static Future> getGuideBook(String country) async { + final apiKey = dotenv.env['OPENAI_API_KEY']; + final uri = Uri.parse('https://api.openai.com/v1/chat/completions'); + + final requestBody = jsonEncode({ + "model": "gpt-4o-mini", + "messages": [ + { + "role": "system", + "content": "You are a Tourist-guide book that makes a guidebook over a given country and further instructions in the message." + }, + { + "role": "user", + "content": "Make a tourist guide book over $country. The topics should be: A little introduction to how the people of the country is like and what to expect from the environment, famous tourist attractions in the country(amusement park always included), Basic words that would be nice to learn in the country's language(should always be from English to the country's language) and transportation. These topics should be in a Json-Object format." + } + ], + }); + + final response = await http.post( + uri, + headers: { + 'Authorization': 'Bearer $apiKey', + 'Content-Type': 'application/json', + }, + body: requestBody, + ); + + if (response.statusCode == 200) { + + try { + return jsonDecode(response.body); + } catch (e) { + return { + 'navn': 'Fejl', + 'art': 'Ukendt', + 'type': 'Fejl', + 'description': 'Fejl ved JSON parsing: $e' + }; + } + } else { + throw Exception('Fejl ved hentning af data: ${response.statusCode}'); + } + } +} \ No newline at end of file diff --git a/Mobile/lib/touristguidebook.dart b/Mobile/lib/touristguidebook.dart new file mode 100644 index 0000000..85d44af --- /dev/null +++ b/Mobile/lib/touristguidebook.dart @@ -0,0 +1,86 @@ +import 'package:dropdown_search/dropdown_search.dart'; +import 'package:flutter/material.dart'; +import 'package:mobile/base/sidemenu.dart'; +import 'package:mobile/models.dart' as models; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'dart:convert'; +import 'services/openaiservice.dart' as api; +import 'base/variables.dart'; + +class TouristGuideBookPage extends StatefulWidget { + const TouristGuideBookPage({super.key}); + + @override + State createState() => _TouristGuideBookPageState(); +} + +class _TouristGuideBookPageState extends State { + String? _selectedCountry; + final List countries = [ + "Afghanistan", "Albania", "Algeria", "Andorra", "Angola", "Argentina", + // Add more countries + ]; + + Future getBook() async { + if (_selectedCountry != null) { + final response = await api.OpenAIService.getGuideBook(_selectedCountry!); + print(response); // For debugging to check the response + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Choose a country')), + ); + } + } + + @override + Widget build(BuildContext context) { + return SideMenu( + selectedIndex: 5, + body: Scaffold( + appBar: AppBar( + title: Text('Select Country'), + ), + body: Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + DropdownSearch( + items: countries, + popupProps: const PopupProps.menu( + showSearchBox: true, + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + labelText: "Search a country", + border: OutlineInputBorder(), + ), + ), + ), + dropdownDecoratorProps: const DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + labelText: "Select a country", + hintText: "Search a country", + border: OutlineInputBorder(), + ), + ), + onChanged: (String? country) { + setState(() { + _selectedCountry = country; + }); + }, + selectedItem: "Select a country you want a touristbook for", + ), + ElevatedButton( + onPressed: getBook, + child: Text('Get Guidebook'), + ), + ], + ), + ), + ), + ), + ); + } +} + diff --git a/Mobile/pubspec.lock b/Mobile/pubspec.lock index fb0194d..e1a71f1 100644 --- a/Mobile/pubspec.lock +++ b/Mobile/pubspec.lock @@ -73,6 +73,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + dropdown_search: + dependency: "direct main" + description: + name: dropdown_search + sha256: "55106e8290acaa97ed15bea1fdad82c3cf0c248dd410e651f5a8ac6870f783ab" + url: "https://pub.dev" + source: hosted + version: "5.0.6" fake_async: dependency: transitive description: @@ -142,6 +150,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_dotenv: + dependency: "direct main" + description: + name: flutter_dotenv + sha256: "9357883bdd153ab78cbf9ffa07656e336b8bbb2b5a3ca596b0b27e119f7c7d77" + url: "https://pub.dev" + source: hosted + version: "5.1.0" flutter_image_compress: dependency: "direct main" description: diff --git a/Mobile/pubspec.yaml b/Mobile/pubspec.yaml index 9b609b5..237d531 100644 --- a/Mobile/pubspec.yaml +++ b/Mobile/pubspec.yaml @@ -43,6 +43,8 @@ dependencies: geolocator: ^13.0.1 image_picker: ^1.1.2 flutter_image_compress: ^2.3.0 + flutter_dotenv: ^5.1.0 + dropdown_search: ^5.0.6 dev_dependencies: flutter_test: diff --git a/rust-backend/.env.example b/rust-backend/.env.example deleted file mode 100644 index f574e23..0000000 --- a/rust-backend/.env.example +++ /dev/null @@ -1,7 +0,0 @@ -JWT_SECRET=DenHerMåAldrigVæreOffentligKunIDetteDemoProjekt -AWS_ACCESS_KEY_ID= -AWS_SECRET_ACCESS_KEY= -AWS_ENDPOINT_URL= -AWS_REGION= -R2_BUCKET_NAME= -R2_BUCKET_URL=