diff --git a/README.md b/README.md
index 8c4770018..4b7976dd6 100644
--- a/README.md
+++ b/README.md
@@ -1,181 +1,102 @@
-# Lab 5: Logout
+# CSC207 Course Project- Recipe Management System
+# Group 150
+# Collaborators: Noura Francis, Adya Gopaul, Zain Malik
-## Preamble
-In the current homework, you added code to the login use case to save the currently-logged-in
-user by saving the user in the Data Access Layer. You also added a unit test for this.
+# Table of Contents
+- [Software Specifications](#software-specifications)
+- [Features of the Software](#features-of-the-software)
+- [Installation Instructions](#installation-instructions)
+- [Usage Guide](#usage-guide)
+- [License](#license)
+- [Feedback](#feedback)
-In this lab, you will complete a logout use case as a team. You will also begin to discuss your project
-and the use cases that need to be implemented.
+## Software Specifications
+This program allows users to either create a new account for a recipe organization system, or log in to their account
+using existing user credentials. Users will be able to search for recipes and apply filters to narrow search results and
+easily obtain recipes that appeal to them. Users save recipes to their profiles to better organize them and reference
+them for future use. Additionally, users may choose to review recipes that they have saved by providing a rating from
+one to five, with one being the lowest and five being the highest, and an optional comment.
-We have created all the Clean Architecture classes necessary for the logout use case.
-By Friday, your team will submit:
-- your completed lab code [for credit]
-- a draft of your project blueprint proposal. [required, but not for credit]
+## Features of Software
+### User Management
+- **Sign Up**: The user can create an account using a username and password.
+- **Log In**: Existing users can log in to their accounts.
+- **Log Out**: Users can securely log out of the application.
-# Phase 2 [for credit]
-_(recall, Phase 1 was your solo task of adding the storage of the currently-logged-in user)_
-## Task 0: Fork this repo on GitHub
-**To get started, one team member should fork this repo on GitHub and share it with the team.
-All of you should then clone it.**
+### Recipe Search
+- **Search for Recipes**: Users can search for recipes by name.
+- **Filter Search Results**: Filters include options for:
+ - Calories
+ - Nutrients (Protein, Carbs, Fat)
-* * *
-
-Suggested logistics: One of you should invite the others to collaborate on their fork of the
-original repo on GitHub. You can do this in your repo on GitHub under `Settings -> Collaborators`.
-This will allow you to push branches to a common repo and then use pull requests to contribute
-your code and review. To prevent others from pushing directly to the main branch,
-we recommend you set branch protection rules on GitHub. Below are how the settings might look if you
-add branch protection rules:
-
-
-
-* * *
-
-Open the project in IntelliJ and make sure you can successfully run `app/Main.java`.
-Note: you may need to set the Project SDK in the `Project Structure...` menu, and possibly
-also manually link the Maven project, as you did in Phase 1.
-
-## Task 1: Understanding the Program
-
-You may notice that we have refactored the CA engine code _slightly_ since Phase 1, but the
-way we build the engine is drastically different: we have switched from using Factories to
-using the Builder design pattern, which we'll be discussing in lecture soon.
-
-Open up `app.Main` and read it as a team.
-- What are the Views and what are the current Use Cases?
-- Which Uses Cases are triggered from each View?
-- Which version of the DAO is `app.Main` using?
-
-The major change since Phase 1 is that we have added the `app.AppBuilder` class which makes
-it easier to understand how our CA engine is being constructed — it also makes `app.Main` nice and concise!
-- Why do all those `addX` methods end in `return this;`?
-
-Run the program and make sure the signup and login Use Cases work.
-
-Currently, you'll notice that the "Log Out" button still doesn't actually log you out. It's time to fix
-that button, which is part of the `LoggedInView`.
-We have created all the classes for you, but some of the code is missing.
-As a team, your task is to fill in the missing code so that the Logout Use Case is implemented.
-**The next part of the readme describes how your team will do this.**
-
-* * *
-
-**Your team will know when you are done when:**
-
-- Clicking the "Log Out" button takes the user back to the Login View when you use the program.
-- The provided `LogoutInteractorTest` test passes.
-
-The "Log Out" button is an instance variable in class `LoggedInVew`. Go find it.
-Also look at the `interface_adapter.change_password.LoggedInViewModel`, which contains any
-data showing on the `LoggedInVew`.
-
-* * *
-## Task 2: Dividing up the work
+
-There are `TODO` comments left in the files
-Recall that you can use the TODO tool window to conveniently pull up a complete list.
-Once the TODOs are all complete, the "Log Out" button _should_ work!
+### Recipe Interaction
+- **Review Recipe**: Users can add reviews to a recipe.
+- **Save Recipe**: Users can save recipes to their profiles for easy access later.
-As a team, split up the TODOs (see below) between the members of your team.
-There are TODOs in seven of the files.
-Make sure each member has at least one TODO which they will be responsible for completing.
-If your team prefers to work in pairs, that is fine too. Your individual branches
-will not be graded for this — only the final, working version.
+### Profile Features
+- **View Saved Recipes**: Users can view a personalized list of saved recipes.
-The TODOs are summarized below (by file) to help your team decide how to split them up:
-* * *
-
-- `Main.java`
+---
- - [ ] TODO: add the Logout Use Case to the app using the appBuilder
-* * *
+## Installation Instructions
-- `LoggedInView.java` (tip: refer to the other views for similar code)
- - [ ] TODO: save the logout controller in the instance variable.
- - [ ] TODO: execute the logout use case through the Controller
-
-* * *
+## Prerequisites
+- **Java Development Kit (JDK)**: Amazon Corretto 11 or higher
+- **Apache Maven**
-- `LogoutController.java` (tip: refer to the other controllers for similar code)
- - [ ] TODO: Save the interactor in the instance variable.
- - [ ] TODO: run the use case interactor for the logout use case
+## Instructions for CMD:
+- **Clone the repository from**: [The Repository](https://github.com/NAF308/Recipe-Management-System)
+- git clone https://github.com/NAF308/Recipe-Management-System.git
+- cd Recipe-Management-System
-* * *
-- `LogoutInputData.java` (should be done with the LogoutInteractor TODOs below)
+- **Use Maven to compile the project and fetch dependencies**
+- mvn clean install
- - [ ] TODO: save the current username in an instance variable and add a getter.
-- `LogoutInteractor.java` (tip: refer to `ChangePasswordInteractor.java` for similar code)
+- **Run the application**
+- mvn exec:java -Dexec.mainClass="app.Main"
- - [ ] TODO: save the DAO and Presenter in the instance variables.
- - [ ] TODO: implement the logic of the Logout Use Case
-* * *
+---
-- `LogoutOutputData.java`
- - [ ] TODO: save the parameters in the instance variables.
+## Usage Guide
+- Once the application is started. You will see the Sign up screen.
+- Choose your username and password.
+- Repeat the password then press sign up
+- You will then see the Login screen
+- Enter your password then press log in
+- You will then see the Profile Screen
+- Here you can change your password and go to recipe search and saved recipes
+- Pressing Recipe Search will take you to the recipe search window
+- There you can write parameters for the recipe search eg. Recipe Name, and calories range
+- Note: At Least one parameter needs to be filled and when writing ranges, the minimum can not be more than the maximum.
+- Pressing search will make an API call and you’ll be taken to the search results window.
+- In the Profile view if you press the Saved Recipes button, you’ll see a list of all the recipes you saved along with the rating you gave them.
-* * *
+## Feedback
+Considering the user’s experience with the Recipe Management System is an integral aspect of this project. We, the creators of this project, are open to accepting feedback on your experience using the system, suggest ways in which the system can be improved or recommend features you would like to see in the future. If you wish to do so, the best way to contact us is through the following email addresses.
+- Noura Francis: noura.francis@mail.utoronto.ca
+- Adya Gopaul: adya.gopaul@mail.utoronto.ca
+- Zain Malik: za.malik@mail.utoronto.ca
-- `LogoutPresenter.java` (tip: refer to `SignupPresenter.java` for similar code)
+## Contributions
+This project is not accepting contributions at the moment. Persons interested in working with this software may create a fork of the repository by pressing the “Fork” button located in the top-right corner of the repository. Changes to the code may be made on the individual’s local repository after cloning their fork. Any merge requests made will neither be reviewed nor merged into the project. The individual will be solely responsible for any and all changes made to their fork of the repository.
- - [ ] TODO: assign to the three instance variables.
- - [ ] TODO: have prepareSuccessView update the LoggedInState
- - [ ] TODO: have prepareSuccessView update the LoginState
* * *
+=======
-1. Make a branch named the first part of your UofT email address, everything before the `@`.
-For example, if your email address is `paul.gries@mail.utoronto.ca`, then the branch name would
-be `paul.gries`.
-
-Make sure you switch to the new branch.
-
-In the terminal, this would look like below, but replaced with your own information:
-```
-git branch paul.gries
-git switch paul.gries
-```
-
-2. Complete your assigned TODOs and make a pull request on GitHub. In your pull request,
- briefly describe what your TODOs were and how you implemented them. If you aren't sure
- about part of it, include this in your pull request so everyone knows what to look
- for when reviewing — or you can of course discuss with your team before making your
- pull request since you are physically working in the same space.
- - **Important: don't push any changes to the `.idea` folder, as that
- may cause issues for your other teammates, as some files contain
- configurations specific to your individual IntelliJ projects.**
-
-3. Review each other's pull requests to ensure each TODO is correctly implemented and that
- there are no Checkstyle issues in the files that were modified.
-
-4. Once all TODOs are completed, your team should debug as needed to ensure the
- correctness of the code. Setting a breakpoint where the log-out use case
- interactor starts its work will likely be a great place to start when debugging.
-
-And that's it; you now have a working Logout Use Case! Instructions for
-how to submit your work on MarkUs will be posted later.
-
-Your team should spend the rest of the lab working on your project blueprint.
-
-* * *
-
-# Project Blueprint
-
-See Quercus for details about the project blueprint! By the end of the week,
-the goal is for your team to have a fully drafted blueprint so that your team
-will be ready to get started on your project after Reading Week.
diff --git a/accessibility-report.md b/accessibility-report.md
new file mode 100644
index 000000000..1d30aa435
--- /dev/null
+++ b/accessibility-report.md
@@ -0,0 +1,45 @@
+# Accessibility Report
+
+## Principles of Universal Design
+
+### Equitable Use
+“The design is useful and marketable to people with diverse abilities”
+- A computer mouse, touchpad or touch-screen device can be used to navigate the application, making it usable to people of different abilities.
+- Individuals who may not have the fine motor skills necessary for using a computer mouse will still be able to use the application to its full capabilities using a touch screen.
+
+### Flexibility in Use
+“The design accommodate a wide range of individual preferences and abilities”
+- Currently, the application on its own does not support full flexibility of use, however a user may still be able to utilize accessibility features built into their personal electronic devices to facilitate speech-to-text functionality, manually increase the font size or zoom in to improve their experience using the application.
+
+### Simple and Intuitive Use
+“Use of the design is easy to understand, regardless of the user’s experience, knowledge, language skills or current concentration level”
+- Simple, non-technical langauge is used to communicate instructions to the user, so that prompts, labels and messages can be easily understood by anyone, regardless of their level of experience with technology.
+- The flow of the application is logical and cohesive, and the sequence of views that a user engages with is particulary intuitive. Prompts and text labels are also provided for additional clarity.
+
+### Perceptible Information
+“The design communicates necessary information effectively to the user, regardless of ambient conditions or user’s sensory abilities”
+- Non-technical language with appropriate prompts, labels and error messages.
+- While this may be adequate for our purposes, the project can be further improved by using colour to reflect different functionalities. For example, having “back” and “cancel” buttons in red and “search” and “save” buttons in green may make the buttons' functionality clearer to the user and prevent accidental mis-clicks.
+
+### Tolerance for Error
+“The design minimizes hazards and the adverse consequences of accidental or unintended actions”
+- The "back" buttons and "cancel" buttons allow a user to exit the program in the event that they wish to restart after making a mistake.
+- However, once a change is made it cannot be undone. This aspect can be improved upon by implementing an “undo” or “edit” functionality for features such as reviewing a recipe.
+- Furthermore, a “forgot password” feature can be added so that users will not lose access to their account and saved recipes, even if they are unable to remember their login credentials.
+
+### Low Physical Effort
+“The design can be used efficiently and comfortably and with a minimum of fatigue”
+- The application is quite straightforward and does not require excessive button clicks to navigate to different views and features, which reduces the physical effort exerted by the user.
+- However, this principle can be improved upon further by increasing the size of the buttons to make them more easily clickable.
+
+### Size and Space for Approach and Use
+“Appropriate size and space is provided for approach, reach, manipulation and use regardless of user’s body size, posture, or mobility”
+- The main frame of the application can be made larger and depending on the device on which the software is being used, the user may also be able to zoom in on parts of the frame they wish to see better.
+- However, this is largely dependent on the user’s device rather than the software itself. This principle can be improved upon by implementing a feature that allows the user to enlarge the size of the panels and/or text on the frame rather than manually zoom in.
+
+
+
+## Intended Users
+In the event that this program were to be made available to customers, we would likely market the program towards individuals who are passionate about cooking or baking, whether that be as a profession, hobby, or necessity to provide for oneself and/or one’s family. Such individuals would benefit from having a system which allows them to easily search for new recipes and save them in the same place. Additionally, someone who is health conscious or has a condition that requires strict dietary monitoring (such as diabetes, high blood pressure, or simply a food allergy) may be attracted to the program’s filtering feature as it would make searching for recipes that meet their dietary requirements much easier.
+
+Despite the wide variety of people who would enjoy using this program, it may not be best suited for individuals who do not cook often, whether that be due to a busy schedule, or to a preference for ready-made meals over those they have prepared themselves. An example of people who may fall into this category are healthcare workers, such as doctors and nurses, who work long hours and may not have the time to dedicate to meal planning and preparation. Additionally, since this project is entirely web-based, it may not be used by those who struggle to use or do not have reliable access to technology, for example elderly individuals or people who do not have their own electronic devices. Such groups may have no need for a Recipe Organization System, or may prefer physical cookbooks to online recipes.
diff --git a/images/image.png b/images/image.png
new file mode 100644
index 000000000..b04d67487
Binary files /dev/null and b/images/image.png differ
diff --git a/images/recipe search screenshot.png b/images/recipe search screenshot.png
new file mode 100644
index 000000000..f8339eafc
Binary files /dev/null and b/images/recipe search screenshot.png differ
diff --git a/src/main/java/api/recipe_search_api.java b/src/main/java/api/recipe_search_api.java
new file mode 100644
index 000000000..7850015c6
--- /dev/null
+++ b/src/main/java/api/recipe_search_api.java
@@ -0,0 +1,197 @@
+package api;
+
+import entity.Recipe;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.util.*;
+
+public class recipe_search_api {
+
+ private static final String API_URL = "https://api.edamam.com/api/recipes/v2";
+ private static final String APP_ID = "bb181cd2";
+ private static final String APP_KEY = "bf09ce656684790d61f32c328f1e720f";
+
+ private final Runnable customTask;
+
+ // Constructor accepting a Runnable
+ public recipe_search_api(Runnable customTask) {
+ this.customTask = customTask;
+ }
+
+ // Default constructor for compatibility
+ public recipe_search_api() {
+ this.customTask = null;
+ }
+
+ public List searchRecipebyName(String q, String cal_min, String cal_max, String carb_min, String carb_max, String protein_min, String protein_max, String fat_min, String fat_max) {
+ final OkHttpClient client = new OkHttpClient().newBuilder()
+ .build();
+ String newq = "";
+ if (!"".equals(q)) {
+ newq = "&q=" + q;
+ }
+ String calories1 = "";
+ if (!"".equals(cal_min)) {
+ if (!"".equals(cal_max)) {
+ calories1 = "&calories=" + cal_min + "-" + cal_max;
+ }
+ }
+ if (!"".equals(cal_min)) {
+ if ("".equals(cal_max)) {
+ calories1 = "&calories=" + cal_min + "%2B";
+ }
+ }
+ if ("".equals(cal_min)) {
+ if (!"".equals(cal_max)) {
+ calories1 = "&calories=" + cal_max;
+ }
+ }
+
+ String carbs = "";
+ if (!"".equals(carb_min)) {
+ if (!"".equals(carb_max)) {
+ carbs = "&nutrients%5BCHOCDF%5D=" + carb_min + "-" + carb_max;
+ }
+ }
+ if (!"".equals(carb_min)) {
+ if ("".equals(carb_max)) {
+ carbs = "&nutrients%5BCHOCDF%5D=" + carb_min + "%2B";
+ }
+ }
+ if ("".equals(carb_min)) {
+ if (!"".equals(carb_max)) {
+ carbs = "&nutrients%5BCHOCDF%5D=" + carb_max;
+ }
+ }
+
+ String protein = "";
+ if (!"".equals(protein_min)) {
+ if (!"".equals(protein_max)) {
+ protein = "&nutrients%5BPROCNT%5D=" + protein_min + "-" + protein_max;
+ }
+ }
+ if (!"".equals(protein_min)) {
+ if ("".equals(protein_max)) {
+ protein = "&nutrients%5BPROCNT%5D=" + protein_min + "%2B";
+ }
+ }
+ if ("".equals(protein_min)) {
+ if (!"".equals(protein_max)) {
+ protein = "&nutrients%5BPROCNT%5D=" + protein_max;
+ }
+ }
+
+ String fat = "";
+ if (!"".equals(fat_min)) {
+ if (!"".equals(fat_max)) {
+ fat = "&nutrients%5BPROCNT%5D=" + fat_min + "-" + fat_max;
+ }
+ }
+ if (!"".equals(fat_min)) {
+ if ("".equals(fat_max)) {
+ fat = "&nutrients%5BPROCNT%5D=" + fat_min + "%2B";
+ }
+ }
+ if ("".equals(fat_min)) {
+ if (!"".equals(fat_max)) {
+ fat = "&nutrients%5BPROCNT%5D=" + fat_max;
+ }
+ }
+
+ final Request request = new Request.Builder()
+ .url(String.format("%s?type=public%s&app_id=%s&app_key=%s%s%s%s%s", API_URL, newq, APP_ID, APP_KEY, calories1, carbs, fat, protein))
+ .build();
+
+ try {
+ Response response = client.newCall(request).execute();
+
+ // Check response
+ if (!response.isSuccessful()) {
+ throw new IOException("Unexpected code: " + response);
+ }
+
+ String responseBody = response.body().string();
+ JSONObject root = new JSONObject(responseBody);
+
+ JSONArray hits = root.getJSONArray("hits");
+ List recipes = new ArrayList<>();
+
+ for (int i = 0; i < hits.length(); i++) {
+ JSONObject recipeObj = hits.getJSONObject(i).getJSONObject("recipe");
+
+ // Extract recipe details
+ String name = recipeObj.getString("label");
+ int servings = recipeObj.optInt("yield", 1);
+ int calories = (int) Math.round(recipeObj.getDouble("calories") / servings);
+ String url = recipeObj.getString("url");
+ String image = recipeObj.getJSONObject("images").getJSONObject("THUMBNAIL").getString("url");
+
+ // Extract nutrients
+ Map nutrients = new HashMap<>();
+ JSONObject totalNutrients = recipeObj.getJSONObject("totalNutrients");
+ for (String key : totalNutrients.keySet()) {
+ JSONObject nutrient = totalNutrients.getJSONObject(key);
+ nutrients.put(nutrient.getString("label"), (int) nutrient.getDouble("quantity"));
+ }
+
+ // Extract tags
+ Set tags = new HashSet<>();
+ JSONArray healthLabels = recipeObj.getJSONArray("healthLabels");
+ for (int j = 0; j < healthLabels.length(); j++) {
+ tags.add(healthLabels.getString(j));
+ }
+
+ // Build the Recipe object
+ Recipe recipe = Recipe.builder()
+ .name(name)
+ .servings(servings)
+ .calories(calories)
+ .nutrients(nutrients)
+ .tags(tags)
+ .url(url)
+ .image(image)
+ .build();
+
+ recipes.add(recipe);
+ }
+
+ return recipes;
+ } catch (IOException | JSONException event) {
+ throw new RuntimeException(event);
+ }
+ }
+
+ public static void main(String[] args) {
+ // Example: Pass a custom void task to the constructor
+ Runnable task = () -> System.out.println("Custom task is running...");
+
+ // Use the constructor with the custom task
+ recipe_search_api apiWithTask = new recipe_search_api(task);
+
+ // Run the custom task
+ if (apiWithTask.customTask != null) {
+ apiWithTask.customTask.run();
+ }
+
+ // Default usage without a task
+ recipe_search_api api = new recipe_search_api();
+ List recipes = api.searchRecipebyName("chicken","100","200","","","","","","");
+
+ for (Recipe recipe : recipes) {
+ System.out.println("Name: " + recipe.getName());
+ System.out.println("Servings: " + recipe.getServings());
+ System.out.println("Calories: " + recipe.getCalories());
+ System.out.println("Nutrients: " + recipe.getNutrients());
+ System.out.println("Tags: " + recipe.getTags());
+ System.out.println("URL: " + recipe.getUrl());
+ System.out.println("Image: " + recipe.getImage());
+ System.out.println("-----------------------------");
+ }
+ }
+}
diff --git a/src/main/java/api/recipe_search_api_interface.java b/src/main/java/api/recipe_search_api_interface.java
new file mode 100644
index 000000000..c17cd20bc
--- /dev/null
+++ b/src/main/java/api/recipe_search_api_interface.java
@@ -0,0 +1,10 @@
+package api;
+
+import entity.Recipe;
+
+import java.util.List;
+
+public interface recipe_search_api_interface {
+
+ List searchRecipe();
+}
diff --git a/src/main/java/app/AppBuilder.java b/src/main/java/app/AppBuilder.java
index e9eef5c81..37241acd0 100644
--- a/src/main/java/app/AppBuilder.java
+++ b/src/main/java/app/AppBuilder.java
@@ -7,17 +7,32 @@
import javax.swing.WindowConstants;
import data_access.InMemoryUserDataAccessObject;
+import data_access.RecipeDataAccessObject;
import entity.CommonUserFactory;
import entity.UserFactory;
import interface_adapter.ViewManagerModel;
import interface_adapter.change_password.ChangePasswordController;
import interface_adapter.change_password.ChangePasswordPresenter;
-import interface_adapter.change_password.LoggedInViewModel;
import interface_adapter.login.LoginController;
import interface_adapter.login.LoginPresenter;
import interface_adapter.login.LoginViewModel;
import interface_adapter.logout.LogoutController;
import interface_adapter.logout.LogoutPresenter;
+import interface_adapter.profile.ProfileController;
+import interface_adapter.profile.ProfilePresenter;
+import interface_adapter.profile.ProfileViewModel;
+import interface_adapter.recipe_details.RecipeDetailsViewModel;
+import interface_adapter.recipe_review.RecipeReviewController;
+import interface_adapter.recipe_review.RecipeReviewPresenter;
+import interface_adapter.recipe_search.RecipeSearchController;
+import interface_adapter.recipe_search.RecipeSearchPresenter;
+import interface_adapter.recipe_search.RecipeSearchViewModel;
+import interface_adapter.saved_recipes.SavedrecipesController;
+import interface_adapter.saved_recipes.SavedrecipesPresenter;
+import interface_adapter.saved_recipes.SavedrecipesViewModel;
+import interface_adapter.search_results.SearchResultsController;
+import interface_adapter.search_results.SearchResultsPresenter;
+import interface_adapter.search_results.SearchResultsViewModel;
import interface_adapter.signup.SignupController;
import interface_adapter.signup.SignupPresenter;
import interface_adapter.signup.SignupViewModel;
@@ -30,11 +45,30 @@
import use_case.logout.LogoutInputBoundary;
import use_case.logout.LogoutInteractor;
import use_case.logout.LogoutOutputBoundary;
+import use_case.profile.ProfileInputBoundary;
+import use_case.profile.ProfileInteractor;
+import use_case.profile.ProfileOutputBoundary;
+import use_case.recipe_search.RecipeSearchInputBoundary;
+import use_case.recipe_search.RecipeSearchInteractor;
+import use_case.recipe_search.RecipeSearchOutputBoundary;
+import use_case.review_recipe.RecipeReviewInteractor;
+import use_case.review_recipe.RecipeReviewOutputBoundary;
+import use_case.saved_recipes.SavedRecipeInputBoundary;
+import use_case.saved_recipes.SavedRecipeInteractor;
+import use_case.saved_recipes.SavedRecipeOutputBoundry;
+import use_case.search_results.SearchResultsInputBoundary;
+import use_case.search_results.SearchResultsInteractor;
+import use_case.search_results.SearchResultsOutputBoundary;
import use_case.signup.SignupInputBoundary;
import use_case.signup.SignupInteractor;
import use_case.signup.SignupOutputBoundary;
-import view.LoggedInView;
+import view.ForgotPasswordView;
import view.LoginView;
+import view.ProfileView;
+import view.RecipeDetailsView;
+import view.RecipeSearchView;
+import view.SavedrecipesView;
+import view.SearchResultsView;
import view.SignupView;
import view.ViewManager;
@@ -57,15 +91,26 @@ public class AppBuilder {
private final ViewManagerModel viewManagerModel = new ViewManagerModel();
private final ViewManager viewManager = new ViewManager(cardPanel, cardLayout, viewManagerModel);
+ private final RecipeDataAccessObject recipeDataAccessObject = new RecipeDataAccessObject();
+
// thought question: is the hard dependency below a problem?
private final InMemoryUserDataAccessObject userDataAccessObject = new InMemoryUserDataAccessObject();
private SignupView signupView;
private SignupViewModel signupViewModel;
private LoginViewModel loginViewModel;
- private LoggedInViewModel loggedInViewModel;
- private LoggedInView loggedInView;
+ private ProfileViewModel profileViewModel;
+ private ProfileView profileView;
+ private RecipeSearchViewModel recipeSearchViewModel;
+ private RecipeSearchView recipeSearchView;
+ private SearchResultsView searchResultsView;
+ private SearchResultsViewModel searchResultsViewModel;
private LoginView loginView;
+ private ForgotPasswordView forgotPasswordView;
+ private SavedrecipesViewModel savedrecipesViewModel;
+ private RecipeDetailsViewModel recipeDetailsViewModel;
+ private RecipeDetailsView recipeDetailsView;
+ private SavedrecipesView savedrecipesView;
public AppBuilder() {
cardPanel.setLayout(cardLayout);
@@ -97,10 +142,54 @@ public AppBuilder addLoginView() {
* Adds the LoggedIn View to the application.
* @return this builder
*/
- public AppBuilder addLoggedInView() {
- loggedInViewModel = new LoggedInViewModel();
- loggedInView = new LoggedInView(loggedInViewModel);
- cardPanel.add(loggedInView, loggedInView.getViewName());
+ public AppBuilder addProfileView() {
+ profileViewModel = new ProfileViewModel();
+ profileView = new ProfileView(profileViewModel);
+ cardPanel.add(profileView, profileView.getViewName());
+ return this;
+ }
+
+ /**
+ * Adds the Saved recipe View to the application.
+ * @return this builder
+ */
+ public AppBuilder addSavedRecipesView() {
+ savedrecipesViewModel = new SavedrecipesViewModel();
+ savedrecipesView = new SavedrecipesView(savedrecipesViewModel);
+ cardPanel.add(savedrecipesView, savedrecipesView.getViewName());
+ return this;
+ }
+
+ /**
+ * Adds the Search Recipe View to the application.
+ * @return this builder
+ */
+ public AppBuilder addRecipeSearchView() {
+ recipeSearchViewModel = new RecipeSearchViewModel();
+ recipeSearchView = new RecipeSearchView(recipeSearchViewModel);
+ cardPanel.add(recipeSearchView, recipeSearchView.getViewName());
+ return this;
+ }
+
+ /**
+ * Adds the Search Results View to the application.
+ * @return this builder
+ */
+ public AppBuilder addSearchResultsView() {
+ searchResultsViewModel = new SearchResultsViewModel();
+ searchResultsView = new SearchResultsView(searchResultsViewModel);
+ cardPanel.add(searchResultsView, searchResultsView.getViewName());
+ return this;
+ }
+
+ /**
+ * Adds the Recipe Details View to the application.
+ * @return this builder
+ */
+ public AppBuilder addRecipeDetailsView() {
+ recipeDetailsViewModel = new RecipeDetailsViewModel();
+ recipeDetailsView = new RecipeDetailsView(recipeDetailsViewModel);
+ cardPanel.add(recipeDetailsView, recipeDetailsView.getViewName());
return this;
}
@@ -119,13 +208,28 @@ public AppBuilder addSignupUseCase() {
return this;
}
+ /**
+ * Adds the Profile Use Case to the application.
+ * @return this builder
+ */
+ public AppBuilder addProfileUseCase() {
+ final ProfileOutputBoundary profileOutputBoundary = new ProfilePresenter(savedrecipesViewModel,
+ viewManagerModel, recipeSearchViewModel);
+ final ProfileInputBoundary userProfileInteractor = new ProfileInteractor(profileOutputBoundary,
+ userDataAccessObject);
+
+ final ProfileController controller = new ProfileController(userProfileInteractor);
+ profileView.setProfileController(controller);
+ return this;
+ }
+
/**
* Adds the Login Use Case to the application.
* @return this builder
*/
public AppBuilder addLoginUseCase() {
final LoginOutputBoundary loginOutputBoundary = new LoginPresenter(viewManagerModel,
- loggedInViewModel, loginViewModel);
+ profileViewModel, loginViewModel);
final LoginInputBoundary loginInteractor = new LoginInteractor(
userDataAccessObject, loginOutputBoundary);
@@ -134,20 +238,34 @@ public AppBuilder addLoginUseCase() {
return this;
}
+ /**
+ * Adds the saved recipes Use Case to the application.
+ * @return this builder
+ */
+ public AppBuilder addSavedrecipesUseCase() {
+ final SavedRecipeOutputBoundry savedRecipeOutputBoundry = new SavedrecipesPresenter(savedrecipesViewModel,
+ profileViewModel, viewManagerModel);
+ final SavedRecipeInputBoundary userSavedRecipeInteractor = new SavedRecipeInteractor(savedRecipeOutputBoundry);
+
+ final SavedrecipesController savedrecipesController = new SavedrecipesController(userSavedRecipeInteractor);
+ savedrecipesView.setSavedrecipesController(savedrecipesController);
+ return this;
+ }
+
/**
* Adds the Change Password Use Case to the application.
* @return this builder
*/
public AppBuilder addChangePasswordUseCase() {
final ChangePasswordOutputBoundary changePasswordOutputBoundary =
- new ChangePasswordPresenter(loggedInViewModel);
+ new ChangePasswordPresenter(profileViewModel);
final ChangePasswordInputBoundary changePasswordInteractor =
new ChangePasswordInteractor(userDataAccessObject, changePasswordOutputBoundary, userFactory);
final ChangePasswordController changePasswordController =
new ChangePasswordController(changePasswordInteractor);
- loggedInView.setChangePasswordController(changePasswordController);
+ profileView.setChangePasswordController(changePasswordController);
return this;
}
@@ -157,13 +275,58 @@ public AppBuilder addChangePasswordUseCase() {
*/
public AppBuilder addLogoutUseCase() {
final LogoutOutputBoundary logoutOutputBoundary = new LogoutPresenter(viewManagerModel,
- loggedInViewModel, loginViewModel);
+ profileViewModel, loginViewModel);
final LogoutInputBoundary logoutInteractor =
new LogoutInteractor(userDataAccessObject, logoutOutputBoundary);
final LogoutController logoutController = new LogoutController(logoutInteractor);
- loggedInView.setLogoutController(logoutController);
+ profileView.setLogoutController(logoutController);
+ return this;
+ }
+
+ /**
+ * Adds the Search Use Case to the application.
+ * @return this builder
+ */
+ public AppBuilder addSearchUseCase() {
+ final RecipeSearchOutputBoundary recipeSearchOutputBoundary = new RecipeSearchPresenter(
+ recipeSearchViewModel, searchResultsViewModel, viewManagerModel, profileViewModel);
+ final RecipeSearchInputBoundary searchInteractor = new RecipeSearchInteractor(
+ recipeDataAccessObject, recipeSearchOutputBoundary);
+
+ final RecipeSearchController controller = new RecipeSearchController(searchInteractor);
+ recipeSearchView.setSearchController(controller);
+ return this;
+ }
+
+ /**
+ * Adds the Search Results Use Case to the application.
+ * @return this builder
+ */
+ public AppBuilder addSearchResultsUseCase() {
+ final SearchResultsOutputBoundary searchResultsOutputBoundary = new SearchResultsPresenter(
+ searchResultsViewModel, recipeDetailsViewModel, viewManagerModel, recipeSearchViewModel);
+ final SearchResultsInputBoundary resultsInteractor = new SearchResultsInteractor(searchResultsOutputBoundary,
+ userDataAccessObject);
+
+ final SearchResultsController controller = new SearchResultsController(resultsInteractor);
+ searchResultsView.setSearchResultsController(controller);
+ return this;
+ }
+
+ /**
+ * Adds the Recipe Review Use Case to the application.
+ * @return this builder
+ */
+ public AppBuilder addRecipeReviewUseCase() {
+ final RecipeReviewOutputBoundary recipeReviewOutputBoundary = new RecipeReviewPresenter(viewManagerModel,
+ profileViewModel);
+ final RecipeReviewInteractor recipeReviewInteractor = new RecipeReviewInteractor(recipeReviewOutputBoundary,
+ userDataAccessObject);
+
+ final RecipeReviewController controller = new RecipeReviewController(recipeReviewInteractor);
+ recipeDetailsView.setRecipeReviewController(controller);
return this;
}
@@ -172,7 +335,7 @@ public AppBuilder addLogoutUseCase() {
* @return the application
*/
public JFrame build() {
- final JFrame application = new JFrame("Login Example");
+ final JFrame application = new JFrame("Recipe Management");
application.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
application.add(cardPanel);
diff --git a/src/main/java/app/Main.java b/src/main/java/app/Main.java
index bef63ad7a..639234329 100644
--- a/src/main/java/app/Main.java
+++ b/src/main/java/app/Main.java
@@ -12,15 +12,24 @@ public class Main {
*/
public static void main(String[] args) {
final AppBuilder appBuilder = new AppBuilder();
- // TODO: add the Logout Use Case to the app using the appBuilder
final JFrame application = appBuilder
- .addLoginView()
- .addSignupView()
- .addLoggedInView()
- .addSignupUseCase()
- .addLoginUseCase()
- .addChangePasswordUseCase()
- .build();
+ .addLoginView()
+ .addSignupView()
+ .addProfileView()
+ .addSavedRecipesView()
+ .addRecipeSearchView()
+ .addSearchResultsView()
+ .addRecipeDetailsView()
+ .addSignupUseCase()
+ .addLoginUseCase()
+ .addChangePasswordUseCase()
+ .addProfileUseCase()
+ .addLogoutUseCase()
+ .addSearchUseCase()
+ .addSavedrecipesUseCase()
+ .addSearchResultsUseCase()
+ .addRecipeReviewUseCase()
+ .build();
application.pack();
application.setVisible(true);
diff --git a/src/main/java/data_access/InMemoryUserDataAccessObject.java b/src/main/java/data_access/InMemoryUserDataAccessObject.java
index 71f00862c..b34581e61 100644
--- a/src/main/java/data_access/InMemoryUserDataAccessObject.java
+++ b/src/main/java/data_access/InMemoryUserDataAccessObject.java
@@ -7,6 +7,9 @@
import use_case.change_password.ChangePasswordUserDataAccessInterface;
import use_case.login.LoginUserDataAccessInterface;
import use_case.logout.LogoutUserDataAccessInterface;
+import use_case.profile.ProfileDataAccessInterface;
+import use_case.review_recipe.RecipeReviewDataAccessInterface;
+import use_case.search_results.SearchResultsDataAccessInterface;
import use_case.signup.SignupUserDataAccessInterface;
/**
@@ -16,7 +19,10 @@
public class InMemoryUserDataAccessObject implements SignupUserDataAccessInterface,
LoginUserDataAccessInterface,
ChangePasswordUserDataAccessInterface,
- LogoutUserDataAccessInterface {
+ LogoutUserDataAccessInterface,
+ ProfileDataAccessInterface,
+ SearchResultsDataAccessInterface,
+ RecipeReviewDataAccessInterface {
private final Map users = new HashMap<>();
diff --git a/src/main/java/data_access/RecipeDataAccessObject.java b/src/main/java/data_access/RecipeDataAccessObject.java
new file mode 100644
index 000000000..2e19a84ce
--- /dev/null
+++ b/src/main/java/data_access/RecipeDataAccessObject.java
@@ -0,0 +1,127 @@
+package data_access;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import entity.Recipe;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import use_case.recipe_search.RecipeSearchDataAccessInterface;
+
+/**
+ * The DAO for recipe data.
+ */
+public class RecipeDataAccessObject implements RecipeSearchDataAccessInterface {
+
+ private static final String API_URL = "https://api.edamam.com/api/recipes/v2";
+ private static final String APP_ID = "bb181cd2";
+ private static final String APP_KEY = "bf09ce656684790d61f32c328f1e720f";
+
+ private final Runnable customTask;
+
+ // Default constructor for compatibility
+ public RecipeDataAccessObject() {
+ this.customTask = null;
+ }
+
+ private String buildRangeQuery(String key, String min, String max) {
+ String val = "";
+ if (!"".equals(min) && !"".equals(max)) {
+ val = String.format("&%s=%s-%s", key, min, max);
+ }
+ else if (!"".equals(min)) {
+ val = String.format("&%s=%s%%2B", key, min);
+ }
+ else if (!"".equals(max)) {
+ val = String.format("&%s=%s", key, max);
+ }
+ return val;
+ }
+
+ @Override
+ public List searchRecipe(String q, String cal_min, String cal_max, String carb_min, String carb_max, String protein_min, String protein_max, String fat_min, String fat_max) {
+ final OkHttpClient client = new OkHttpClient().newBuilder().build();
+
+ // Build query parameters
+ String newq = "";
+ if (!"".equals(q)) {
+ newq = "&q=" + q;
+ }
+ final String calories = buildRangeQuery("calories", cal_min, cal_max);
+ final String carbs = buildRangeQuery("nutrients%5BCHOCDF%5D", carb_min, carb_max);
+ final String protein = buildRangeQuery("nutrients%5BPROCNT%5D", protein_min, protein_max);
+ final String fat = buildRangeQuery("nutrients%5BFAT%5D", fat_min, fat_max);
+
+ final Request request = new Request.Builder()
+ .url(String.format("%s?type=public%s&app_id=%s&app_key=%s%s%s%s%s",
+ API_URL, newq, APP_ID, APP_KEY, calories, carbs, protein, fat))
+ .build();
+
+ try {
+ final Response response = client.newCall(request).execute();
+
+ // Check response
+ if (!response.isSuccessful()) {
+ throw new IOException("Unexpected code: " + response);
+ }
+
+ final String responseBody = response.body().string();
+ final JSONObject root = new JSONObject(responseBody);
+
+ final JSONArray hits = root.getJSONArray("hits");
+ final List recipes = new ArrayList<>();
+
+ for (int i = 0; i < hits.length(); i++) {
+ final JSONObject recipeObj = hits.getJSONObject(i).getJSONObject("recipe");
+
+ // Extract recipe details
+ final String name = recipeObj.getString("label");
+ final int servings = recipeObj.optInt("yield", 1);
+ final int caloriesValue = (int) Math.round(recipeObj.getDouble("calories") / servings);
+ final String url = recipeObj.getString("url");
+ // final String image = recipeObj.getJSONObject("images").getJSONObject("THUMBNAIL").getString("url");
+
+ // Extract nutrients
+ final Map nutrients = new HashMap<>();
+ final JSONObject totalNutrients = recipeObj.getJSONObject("totalNutrients");
+ for (String key : totalNutrients.keySet()) {
+ final JSONObject nutrient = totalNutrients.getJSONObject(key);
+ nutrients.put(nutrient.getString("label"), (int) nutrient.getDouble("quantity"));
+ }
+
+ // // Extract tags
+ // final Set tags = new HashSet<>();
+ // final JSONArray healthLabels = recipeObj.getJSONArray("healthLabels");
+ // for (int j = 0; j < healthLabels.length(); j++) {
+ // tags.add(healthLabels.getString(j));
+ // }
+
+ // Build the Recipe object
+ final Recipe recipe = Recipe.builder()
+ .name(name)
+ .servings(servings)
+ .calories(caloriesValue)
+ .nutrients(nutrients)
+ // .tags(tags)
+ .url(url)
+ // .image(image)
+ .build();
+
+ recipes.add(recipe);
+ }
+
+ return recipes;
+ }
+ catch (IOException | JSONException event) {
+ throw new RuntimeException(event);
+ }
+ }
+}
diff --git a/src/main/java/entity/CommonUser.java b/src/main/java/entity/CommonUser.java
index ba25fd20a..deb1694b5 100644
--- a/src/main/java/entity/CommonUser.java
+++ b/src/main/java/entity/CommonUser.java
@@ -1,5 +1,8 @@
package entity;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* A simple implementation of the User interface.
*/
@@ -7,10 +10,12 @@ public class CommonUser implements User {
private final String name;
private final String password;
+ private final Map recipes;
public CommonUser(String name, String password) {
this.name = name;
this.password = password;
+ this.recipes = new HashMap<>();
}
@Override
@@ -23,4 +28,26 @@ public String getPassword() {
return password;
}
+ public Map getRecipes() {
+ return recipes;
+ }
+
+ /**
+ * Add Recipe to the user's saved recipes.
+ * @param recipe the recipe object being saved.
+ * @param amount rating of the recipe.
+ */
+ public void addRecipe(Recipe recipe, Integer amount) {
+ recipes.put(recipe, amount);
+ }
+
+ public void reviewRecipe(Recipe recipe, int rating) {
+ if (recipes.containsKey(recipe)) {
+ recipes.put(recipe, rating);
+ }
+ else {
+ throw new IllegalArgumentException("Recipe not found");
+ }
+ }
}
+
diff --git a/src/main/java/entity/Recipe.java b/src/main/java/entity/Recipe.java
new file mode 100644
index 000000000..64e50031f
--- /dev/null
+++ b/src/main/java/entity/Recipe.java
@@ -0,0 +1,159 @@
+package entity;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Recipe object.
+ */
+public class Recipe {
+
+ private final String name;
+ private final int servings;
+ private final int calories;
+ private final Map nutrients;
+ private final Set tags;
+ private final String url;
+ private final String image;
+
+ public Recipe(String name, int servings, int calories, Map nutrients,
+ Set tags, String url, String image) {
+ this.name = name;
+ this.servings = servings;
+ this.calories = calories;
+ this.nutrients = nutrients;
+ this.tags = tags;
+ this.url = url;
+ this.image = image;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getServings() {
+ return servings;
+ }
+
+ public int getCalories() {
+ return calories;
+ }
+
+ public Map getNutrients() {
+ return nutrients;
+ }
+
+ public Set getTags() {
+ return tags;
+ }
+
+ /**
+ * Method to build a recipe object.
+ * @return Recipe builder class
+ */
+ public static RecipeBuilder builder() {
+ return new RecipeBuilder();
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public String getImage() {
+ return image;
+ }
+
+ /**
+ * Recipe builder class.
+ */
+ public static class RecipeBuilder {
+ private String name;
+ private int servings;
+ private int calories;
+ private Map nutrients;
+ private Set tags;
+ private String url;
+ private String image;
+
+ RecipeBuilder() {
+ }
+
+ /**
+ * Recipe name input.
+ * @param nameInput name of recipe
+ * @return sets the name of the recipe.
+ */
+ public RecipeBuilder name(String nameInput) {
+ this.name = nameInput;
+ return this;
+ }
+
+ /**
+ * Recipe serving number input.
+ * @param servingsInput serving number of recipe
+ * @return sets the serving number of the recipe.
+ */
+ public RecipeBuilder servings(int servingsInput) {
+ this.servings = servingsInput;
+ return this;
+ }
+
+ /**
+ * Recipe calories input.
+ * @param caloriesInput calories of recipe
+ * @return sets the calories of the recipe.
+ */
+ public RecipeBuilder calories(int caloriesInput) {
+ this.calories = caloriesInput;
+ return this;
+ }
+
+ /**
+ * Recipe nutrients input.
+ * @param nutrientsInput nutrients of recipe
+ * @return sets the nutrients of the recipe.
+ */
+ public RecipeBuilder nutrients(Map nutrientsInput) {
+ this.nutrients = nutrientsInput;
+ return this;
+ }
+
+ /**
+ * Recipe tags input.
+ * @param tagsInput tags of recipe
+ * @return sets the tags of the recipe.
+ */
+ public RecipeBuilder tags(Set tagsInput) {
+ this.tags = tagsInput;
+ return this;
+ }
+
+ /**
+ * Recipe url input.
+ * @param urlInput url of recipe
+ * @return sets the url of the recipe.
+ */
+ public RecipeBuilder url(String urlInput) {
+ this.url = urlInput;
+ return this;
+ }
+
+ /**
+ * Recipe image input.
+ * @param imageInput image of recipe
+ * @return sets the image of the recipe.
+ */
+ public RecipeBuilder image(String imageInput) {
+ this.image = imageInput;
+ return this;
+ }
+
+ /**
+ * Builds the recipe.
+ * @return New Recipe
+ */
+ public Recipe build() {
+ return new Recipe(name, servings, calories, nutrients, tags, url, image);
+ }
+ }
+}
diff --git a/src/main/java/entity/User.java b/src/main/java/entity/User.java
index 0ad073902..9fad33692 100644
--- a/src/main/java/entity/User.java
+++ b/src/main/java/entity/User.java
@@ -1,5 +1,7 @@
package entity;
+import java.util.Map;
+
/**
* The representation of a user in our program.
*/
@@ -17,4 +19,8 @@ public interface User {
*/
String getPassword();
+ Map getRecipes();
+
+ void addRecipe(Recipe recipe, Integer amount);
+
}
diff --git a/src/main/java/interface_adapter/change_password/ChangePasswordPresenter.java b/src/main/java/interface_adapter/change_password/ChangePasswordPresenter.java
index 3efca4e46..7d8080b25 100644
--- a/src/main/java/interface_adapter/change_password/ChangePasswordPresenter.java
+++ b/src/main/java/interface_adapter/change_password/ChangePasswordPresenter.java
@@ -1,5 +1,6 @@
package interface_adapter.change_password;
+import interface_adapter.profile.ProfileViewModel;
import use_case.change_password.ChangePasswordOutputBoundary;
import use_case.change_password.ChangePasswordOutputData;
@@ -8,10 +9,10 @@
*/
public class ChangePasswordPresenter implements ChangePasswordOutputBoundary {
- private final LoggedInViewModel loggedInViewModel;
+ private final ProfileViewModel profileViewModel;
- public ChangePasswordPresenter(LoggedInViewModel loggedInViewModel) {
- this.loggedInViewModel = loggedInViewModel;
+ public ChangePasswordPresenter(ProfileViewModel profileViewModel) {
+ this.profileViewModel = profileViewModel;
}
@Override
@@ -20,7 +21,7 @@ public void prepareSuccessView(ChangePasswordOutputData outputData) {
// since the output data only contains the username, which remains the same.
// We still fire the property changed event, but just to let the view know that
// it can alert the user that their password was changed successfully..
- loggedInViewModel.firePropertyChanged("password");
+ profileViewModel.firePropertyChanged("password");
}
diff --git a/src/main/java/interface_adapter/change_password/LoggedInViewModel.java b/src/main/java/interface_adapter/change_password/LoggedInViewModel.java
deleted file mode 100644
index 85c81e4e6..000000000
--- a/src/main/java/interface_adapter/change_password/LoggedInViewModel.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package interface_adapter.change_password;
-
-import interface_adapter.ViewModel;
-
-/**
- * The View Model for the Logged In View.
- */
-public class LoggedInViewModel extends ViewModel {
-
- public LoggedInViewModel() {
- super("logged in");
- setState(new LoggedInState());
- }
-
-}
diff --git a/src/main/java/interface_adapter/forgotPassword/ForgotPasswordState.java b/src/main/java/interface_adapter/forgotPassword/ForgotPasswordState.java
new file mode 100644
index 000000000..976a489a2
--- /dev/null
+++ b/src/main/java/interface_adapter/forgotPassword/ForgotPasswordState.java
@@ -0,0 +1,30 @@
+package interface_adapter.forgotPassword;
+
+/**
+ * View state for unimplemented functionality.
+ */
+public class ForgotPasswordState {
+ private String username = "";
+
+ private String password = "";
+
+ public ForgotPasswordState() {
+
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+}
diff --git a/src/main/java/interface_adapter/forgotPassword/ForgotPasswordViewModel.java b/src/main/java/interface_adapter/forgotPassword/ForgotPasswordViewModel.java
new file mode 100644
index 000000000..a1489e53a
--- /dev/null
+++ b/src/main/java/interface_adapter/forgotPassword/ForgotPasswordViewModel.java
@@ -0,0 +1,14 @@
+package interface_adapter.forgotPassword;
+
+import interface_adapter.ViewModel;
+
+/**
+ * View model for unimplemented functionality.
+ */
+public class ForgotPasswordViewModel extends ViewModel {
+
+ public ForgotPasswordViewModel() {
+ super("Forgot Password");
+ setState(new ForgotPasswordState());
+ }
+}
diff --git a/src/main/java/interface_adapter/login/LoginController.java b/src/main/java/interface_adapter/login/LoginController.java
index 57e950666..e8c713737 100644
--- a/src/main/java/interface_adapter/login/LoginController.java
+++ b/src/main/java/interface_adapter/login/LoginController.java
@@ -25,4 +25,7 @@ public void execute(String username, String password) {
loginUseCaseInteractor.execute(loginInputData);
}
+ // public void switchToForgotPasswordView() {
+ // loginUseCaseInteractor.switchToForgotPasswordView();
+ // }
}
diff --git a/src/main/java/interface_adapter/login/LoginPresenter.java b/src/main/java/interface_adapter/login/LoginPresenter.java
index 66560d51a..411a7864b 100644
--- a/src/main/java/interface_adapter/login/LoginPresenter.java
+++ b/src/main/java/interface_adapter/login/LoginPresenter.java
@@ -1,8 +1,9 @@
package interface_adapter.login;
import interface_adapter.ViewManagerModel;
-import interface_adapter.change_password.LoggedInState;
-import interface_adapter.change_password.LoggedInViewModel;
+import interface_adapter.forgotPassword.ForgotPasswordViewModel;
+import interface_adapter.profile.ProfileState;
+import interface_adapter.profile.ProfileViewModel;
import use_case.login.LoginOutputBoundary;
import use_case.login.LoginOutputData;
@@ -12,14 +13,14 @@
public class LoginPresenter implements LoginOutputBoundary {
private final LoginViewModel loginViewModel;
- private final LoggedInViewModel loggedInViewModel;
+ private final ProfileViewModel profileViewModel;
private final ViewManagerModel viewManagerModel;
public LoginPresenter(ViewManagerModel viewManagerModel,
- LoggedInViewModel loggedInViewModel,
+ ProfileViewModel profileViewModel,
LoginViewModel loginViewModel) {
this.viewManagerModel = viewManagerModel;
- this.loggedInViewModel = loggedInViewModel;
+ this.profileViewModel = profileViewModel;
this.loginViewModel = loginViewModel;
}
@@ -27,12 +28,12 @@ public LoginPresenter(ViewManagerModel viewManagerModel,
public void prepareSuccessView(LoginOutputData response) {
// On success, switch to the logged in view.
- final LoggedInState loggedInState = loggedInViewModel.getState();
- loggedInState.setUsername(response.getUsername());
- this.loggedInViewModel.setState(loggedInState);
- this.loggedInViewModel.firePropertyChanged();
+ final ProfileState profileState = profileViewModel.getState();
+ profileState.setUsername(response.getUsername());
+ this.profileViewModel.setState(profileState);
+ this.profileViewModel.firePropertyChanged();
- this.viewManagerModel.setState(loggedInViewModel.getViewName());
+ this.viewManagerModel.setState(profileViewModel.getViewName());
this.viewManagerModel.firePropertyChanged();
}
@@ -42,4 +43,8 @@ public void prepareFailView(String error) {
loginState.setLoginError(error);
loginViewModel.firePropertyChanged();
}
+ // public void switchToForgotPasswordView() {
+ // viewManagerModel.setState(forgotPasswordViewModel.getViewName());
+ // viewManagerModel.firePropertyChanged();
+ // }
}
diff --git a/src/main/java/interface_adapter/logout/LogoutController.java b/src/main/java/interface_adapter/logout/LogoutController.java
index e184a3bba..8907cc12c 100644
--- a/src/main/java/interface_adapter/logout/LogoutController.java
+++ b/src/main/java/interface_adapter/logout/LogoutController.java
@@ -1,6 +1,7 @@
package interface_adapter.logout;
import use_case.logout.LogoutInputBoundary;
+import use_case.logout.LogoutInputData;
/**
* The controller for the Logout Use Case.
@@ -10,7 +11,7 @@ public class LogoutController {
private LogoutInputBoundary logoutUseCaseInteractor;
public LogoutController(LogoutInputBoundary logoutUseCaseInteractor) {
- // TODO: Save the interactor in the instance variable.
+ this.logoutUseCaseInteractor = logoutUseCaseInteractor;
}
/**
@@ -18,8 +19,9 @@ public LogoutController(LogoutInputBoundary logoutUseCaseInteractor) {
* @param username the username of the user logging in
*/
public void execute(String username) {
- // TODO: run the use case interactor for the logout use case
// 1. instantiate the `LogoutInputData`, which should contain the username.
+ final LogoutInputData logoutInputData = new LogoutInputData(username);
// 2. tell the Interactor to execute.
+ logoutUseCaseInteractor.execute(logoutInputData);
}
}
diff --git a/src/main/java/interface_adapter/logout/LogoutPresenter.java b/src/main/java/interface_adapter/logout/LogoutPresenter.java
index 78ef306a1..01cdd55ab 100644
--- a/src/main/java/interface_adapter/logout/LogoutPresenter.java
+++ b/src/main/java/interface_adapter/logout/LogoutPresenter.java
@@ -1,8 +1,10 @@
package interface_adapter.logout;
import interface_adapter.ViewManagerModel;
-import interface_adapter.change_password.LoggedInViewModel;
+import interface_adapter.login.LoginState;
import interface_adapter.login.LoginViewModel;
+import interface_adapter.profile.ProfileState;
+import interface_adapter.profile.ProfileViewModel;
import use_case.logout.LogoutOutputBoundary;
import use_case.logout.LogoutOutputData;
@@ -11,14 +13,16 @@
*/
public class LogoutPresenter implements LogoutOutputBoundary {
- private LoggedInViewModel loggedInViewModel;
+ private ProfileViewModel profileViewModel;
private ViewManagerModel viewManagerModel;
private LoginViewModel loginViewModel;
public LogoutPresenter(ViewManagerModel viewManagerModel,
- LoggedInViewModel loggedInViewModel,
+ ProfileViewModel profileViewModel,
LoginViewModel loginViewModel) {
- // TODO: assign to the three instance variables.
+ this.profileViewModel = profileViewModel;
+ this.viewManagerModel = viewManagerModel;
+ this.loginViewModel = loginViewModel;
}
@Override
@@ -29,17 +33,24 @@ public void prepareSuccessView(LogoutOutputData response) {
// We also need to set the username in the LoggedInState to
// the empty string.
- // TODO: have prepareSuccessView update the LoggedInState
// 1. get the LoggedInState out of the appropriate View Model,
+ final ProfileState profileState = this.profileViewModel.getState();
// 2. set the username in the state to the empty string
+ profileState.setUsername("");
// 3. set the state in the LoggedInViewModel to the updated state
+ profileViewModel.setState(profileState);
// 4. firePropertyChanged so that the View that is listening is updated.
+ profileViewModel.firePropertyChanged();
- // TODO: have prepareSuccessView update the LoginState
// 5. get the LoginState out of the appropriate View Model,
+ final LoginState loginState = this.loginViewModel.getState();
// 6. set the username and password in the state to the empty string
+ loginState.setUsername("");
+ loginState.setPassword("");
// 7. set the state in the LoginViewModel to the updated state
+ loginViewModel.setState(loginState);
// 8. firePropertyChanged so that the View that is listening is updated.
+ loginViewModel.firePropertyChanged();
// This code tells the View Manager to switch to the LoginView.
this.viewManagerModel.setState(loginViewModel.getViewName());
diff --git a/src/main/java/interface_adapter/profile/ProfileController.java b/src/main/java/interface_adapter/profile/ProfileController.java
new file mode 100644
index 000000000..bc631355d
--- /dev/null
+++ b/src/main/java/interface_adapter/profile/ProfileController.java
@@ -0,0 +1,32 @@
+package interface_adapter.profile;
+
+import use_case.profile.ProfileInputBoundary;
+import use_case.profile.ProfileInputData;
+
+/**
+ * The Controller for the Profile View.
+ */
+public class ProfileController {
+
+ private final ProfileInputBoundary profileInputBoundary;
+
+ public ProfileController(ProfileInputBoundary profileInputBoundry) {
+ this.profileInputBoundary = profileInputBoundry;
+ }
+
+ /**
+ * Switch to SavedRecipes View.
+ * @param username the username of currently logged-in user.
+ */
+ public void switchToSavedRecipesView(String username) {
+ final ProfileInputData profileInputData = new ProfileInputData(username);
+ this.profileInputBoundary.switchToSavedRecipesView(profileInputData);
+ }
+
+ /**
+ * Switch to RecipeSearch View.
+ */
+ public void switchToRecipeSearchView() {
+ this.profileInputBoundary.switchToRecipeSearchView();
+ }
+}
diff --git a/src/main/java/interface_adapter/profile/ProfilePresenter.java b/src/main/java/interface_adapter/profile/ProfilePresenter.java
new file mode 100644
index 000000000..b930e9a0f
--- /dev/null
+++ b/src/main/java/interface_adapter/profile/ProfilePresenter.java
@@ -0,0 +1,38 @@
+package interface_adapter.profile;
+
+import interface_adapter.ViewManagerModel;
+import interface_adapter.recipe_search.RecipeSearchViewModel;
+import interface_adapter.saved_recipes.SavedrecipesState;
+import interface_adapter.saved_recipes.SavedrecipesViewModel;
+import use_case.profile.ProfileOutputBoundary;
+import use_case.profile.ProfileOutputData;
+
+public class ProfilePresenter implements ProfileOutputBoundary {
+
+ private final ProfileViewModel profileViewModel;
+ private final SavedrecipesViewModel savedrecipesViewModel;
+ private final ViewManagerModel viewManagerModel;
+ private final RecipeSearchViewModel recipeSearchViewModel;
+
+ public ProfilePresenter(SavedrecipesViewModel savedrecipesViewModel, ViewManagerModel viewManagerModel, RecipeSearchViewModel recipeSearchViewModel) {
+ this.savedrecipesViewModel = savedrecipesViewModel;
+ this.viewManagerModel = viewManagerModel;
+ this.profileViewModel = new ProfileViewModel();
+ this.recipeSearchViewModel = recipeSearchViewModel;
+ }
+
+ public void switchtoSavedrecipeView(ProfileOutputData response) {
+ final SavedrecipesState savedrecipesState = savedrecipesViewModel.getState();
+ savedrecipesState.setUsername(response.getUsername());
+ savedrecipesState.setRecipes(response.getRecipes());
+ this.savedrecipesViewModel.setState(savedrecipesState);
+ this.savedrecipesViewModel.firePropertyChanged();
+ viewManagerModel.setState(savedrecipesViewModel.getViewName());
+ viewManagerModel.firePropertyChanged();
+ }
+
+ public void switchToRecipeSearchView() {
+ viewManagerModel.setState(recipeSearchViewModel.getViewName());
+ viewManagerModel.firePropertyChanged();
+ }
+}
diff --git a/src/main/java/interface_adapter/change_password/LoggedInState.java b/src/main/java/interface_adapter/profile/ProfileState.java
similarity index 84%
rename from src/main/java/interface_adapter/change_password/LoggedInState.java
rename to src/main/java/interface_adapter/profile/ProfileState.java
index 54ed80537..bc081844c 100644
--- a/src/main/java/interface_adapter/change_password/LoggedInState.java
+++ b/src/main/java/interface_adapter/profile/ProfileState.java
@@ -1,22 +1,22 @@
-package interface_adapter.change_password;
+package interface_adapter.profile;
/**
* The State information representing the logged-in user.
*/
-public class LoggedInState {
+public class ProfileState {
private String username = "";
private String password = "";
private String passwordError;
- public LoggedInState(LoggedInState copy) {
+ public ProfileState(ProfileState copy) {
username = copy.username;
password = copy.password;
passwordError = copy.passwordError;
}
// Because of the previous copy constructor, the default constructor must be explicit.
- public LoggedInState() {
+ public ProfileState() {
}
diff --git a/src/main/java/interface_adapter/profile/ProfileViewModel.java b/src/main/java/interface_adapter/profile/ProfileViewModel.java
new file mode 100644
index 000000000..038a0870e
--- /dev/null
+++ b/src/main/java/interface_adapter/profile/ProfileViewModel.java
@@ -0,0 +1,15 @@
+package interface_adapter.profile;
+
+import interface_adapter.ViewModel;
+
+/**
+ * The View Model for the Logged In View.
+ */
+public class ProfileViewModel extends ViewModel {
+
+ public ProfileViewModel() {
+ super("profile");
+ setState(new ProfileState());
+ }
+
+}
diff --git a/src/main/java/interface_adapter/recipe_details/RecipeDetailsState.java b/src/main/java/interface_adapter/recipe_details/RecipeDetailsState.java
new file mode 100644
index 000000000..b336803f3
--- /dev/null
+++ b/src/main/java/interface_adapter/recipe_details/RecipeDetailsState.java
@@ -0,0 +1,38 @@
+package interface_adapter.recipe_details;
+
+import entity.Recipe;
+import entity.User;
+
+/**
+ * The state for the Recipe Details View Model.
+ */
+public class RecipeDetailsState {
+ // private int rating;
+ private Recipe recipe;
+ private String username;
+
+ public RecipeDetailsState(RecipeDetailsState copy) {
+ this.recipe = copy.recipe;
+ this.username = copy.username;
+ }
+
+ public RecipeDetailsState() {
+
+ }
+
+ public Recipe getRecipe() {
+ return recipe;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setRecipe(Recipe recipe) {
+ this.recipe = recipe;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+}
diff --git a/src/main/java/interface_adapter/recipe_details/RecipeDetailsViewModel.java b/src/main/java/interface_adapter/recipe_details/RecipeDetailsViewModel.java
new file mode 100644
index 000000000..f9d840ea0
--- /dev/null
+++ b/src/main/java/interface_adapter/recipe_details/RecipeDetailsViewModel.java
@@ -0,0 +1,15 @@
+package interface_adapter.recipe_details;
+
+import entity.Recipe;
+import interface_adapter.ViewModel;
+
+/**
+ * The View Model for the Recipe Details View.
+ */
+public class RecipeDetailsViewModel extends ViewModel {
+
+ public RecipeDetailsViewModel() {
+ super("Recipe Details");
+ setState(new RecipeDetailsState());
+ }
+}
diff --git a/src/main/java/interface_adapter/recipe_review/RecipeReviewController.java b/src/main/java/interface_adapter/recipe_review/RecipeReviewController.java
new file mode 100644
index 000000000..b1b52c113
--- /dev/null
+++ b/src/main/java/interface_adapter/recipe_review/RecipeReviewController.java
@@ -0,0 +1,32 @@
+package interface_adapter.recipe_review;
+
+import entity.Recipe;
+import use_case.review_recipe.RecipeReviewInputData;
+import use_case.review_recipe.ReviewRecipeInputBoundary;
+
+/**
+ * The controller for the Review Recipe Use Case.
+ */
+public class RecipeReviewController {
+ private final ReviewRecipeInputBoundary reviewRecipeUseCaseInteractor;
+
+ public RecipeReviewController(ReviewRecipeInputBoundary reviewRecipeUseCaseInteractor) {
+ this.reviewRecipeUseCaseInteractor = reviewRecipeUseCaseInteractor;
+ }
+
+ /**
+ * Executes the Review Recipe Use Case.
+ * @param recipe the recipe that the user is reviewing
+ * @param rating the rating that the user assigns to the recipe
+ */
+ public void execute(Recipe recipe, int rating) {
+ final RecipeReviewInputData recipeReviewInputData = new RecipeReviewInputData(recipe, rating);
+
+ reviewRecipeUseCaseInteractor.execute(recipeReviewInputData);
+ }
+
+ public void switchToSavedrecipesView() {
+ reviewRecipeUseCaseInteractor.switchToSavedrecipesView();
+ }
+
+}
diff --git a/src/main/java/interface_adapter/recipe_review/RecipeReviewPresenter.java b/src/main/java/interface_adapter/recipe_review/RecipeReviewPresenter.java
new file mode 100644
index 000000000..b6291232c
--- /dev/null
+++ b/src/main/java/interface_adapter/recipe_review/RecipeReviewPresenter.java
@@ -0,0 +1,75 @@
+package interface_adapter.recipe_review;
+
+import interface_adapter.ViewManagerModel;
+import interface_adapter.profile.ProfileState;
+import interface_adapter.profile.ProfileViewModel;
+import interface_adapter.saved_recipes.SavedrecipesViewModel;
+import use_case.review_recipe.RecipeReviewOutputBoundary;
+import use_case.review_recipe.RecipeReviewOutputData;
+
+public class RecipeReviewPresenter implements RecipeReviewOutputBoundary {
+ // private final RecipeReviewViewModel recipeReviewViewModel;
+ private final ViewManagerModel viewManagerModel;
+ // private final SavedrecipesViewModel savedRecipesViewModel;
+ private final ProfileViewModel profileViewModel;
+
+// public RecipeReviewPresenter(RecipeReviewViewModel recipeReviewViewModel, ViewManagerModel viewManagerModel) {
+// this.recipeReviewViewModel = recipeReviewViewModel;
+// this.viewManagerModel = viewManagerModel;
+// }
+
+ public RecipeReviewPresenter(ViewManagerModel viewManagerModel, ProfileViewModel profileViewModel) {
+ this.viewManagerModel = viewManagerModel;
+ this.profileViewModel = profileViewModel;
+ }
+
+ /**
+ * @param outputData the output data.
+ */
+// @Override
+// public void prepareSuccessView(RecipeReviewOutputData outputData) {
+// final RecipeReviewState recipeReviewState = recipeReviewViewModel.getState();
+// recipeReviewViewModel.setState(recipeReviewState);
+// }
+
+ @Override
+ public void prepareSuccessView(RecipeReviewOutputData outputData) {
+ final ProfileState profileState = profileViewModel.getState();
+ profileState.setUsername(outputData.getUsername());
+ this.profileViewModel.setState(profileState);
+ this.profileViewModel.firePropertyChanged();
+
+ this.viewManagerModel.setState(profileViewModel.getViewName());
+ this.viewManagerModel.firePropertyChanged();
+ }
+
+ /**
+ * @param errorMessage the explanation of the failure
+ * */
+// @Override
+// public void prepareFailView(String errorMessage) {
+// final RecipeReviewState recipeReviewState = recipeReviewViewModel.getState();
+// recipeReviewState.setError(errorMessage);
+// recipeReviewViewModel.firePropertyChanged();
+// }
+
+ @Override
+ public void prepareFailView(String errorMessage) {
+
+ }
+
+ /**
+ *
+ */
+// @Override
+// public void switchToSavedrecipesView() {
+// viewManagerModel.setState(savedRecipesViewModel.getViewName());
+// // viewManagerModel.setState(recipeReviewViewModel.getViewName());
+// viewManagerModel.firePropertyChanged();
+// }
+
+ @Override
+ public void switchToSavedrecipesView() {
+
+ }
+}
diff --git a/src/main/java/interface_adapter/recipe_review/RecipeReviewState.java b/src/main/java/interface_adapter/recipe_review/RecipeReviewState.java
new file mode 100644
index 000000000..944e681f2
--- /dev/null
+++ b/src/main/java/interface_adapter/recipe_review/RecipeReviewState.java
@@ -0,0 +1,45 @@
+package interface_adapter.recipe_review;
+
+import entity.Recipe;
+
+/**
+ * The State for the Recipe Review View Model.
+ */
+public class RecipeReviewState {
+ private Recipe recipe;
+ private String name;
+ private int rating;
+ private String error;
+
+ public Recipe getRecipe() {
+ return recipe;
+ }
+
+ public void setRecipe(Recipe recipe) {
+ this.recipe = recipe;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getRating() {
+ return rating;
+ }
+
+ public void setRating(int rating) {
+ this.rating = rating;
+ }
+
+ public String getError() {
+ return error;
+ }
+
+ public void setError(String error) {
+ this.error = error;
+ }
+}
diff --git a/src/main/java/interface_adapter/recipe_review/RecipeReviewViewModel.java b/src/main/java/interface_adapter/recipe_review/RecipeReviewViewModel.java
new file mode 100644
index 000000000..917c78df1
--- /dev/null
+++ b/src/main/java/interface_adapter/recipe_review/RecipeReviewViewModel.java
@@ -0,0 +1,13 @@
+package interface_adapter.recipe_review;
+
+import interface_adapter.ViewModel;
+
+/**
+ * The View Model for the Saved recipes View.
+ */
+public class RecipeReviewViewModel extends ViewModel {
+ public RecipeReviewViewModel() {
+ super("recipe review");
+ setState(new RecipeReviewState());
+ }
+}
diff --git a/src/main/java/interface_adapter/recipe_search/RecipeSearchController.java b/src/main/java/interface_adapter/recipe_search/RecipeSearchController.java
new file mode 100644
index 000000000..326b71739
--- /dev/null
+++ b/src/main/java/interface_adapter/recipe_search/RecipeSearchController.java
@@ -0,0 +1,50 @@
+package interface_adapter.recipe_search;
+
+import use_case.recipe_search.RecipeSearchInputBoundary;
+import use_case.recipe_search.RecipeSearchInputData;
+
+/**
+ * The controller for the Search Use Case.
+ */
+public class RecipeSearchController {
+
+ private final RecipeSearchInputBoundary searchUseCaseInteractor;
+
+ public RecipeSearchController(RecipeSearchInputBoundary searchUseCaseInteractor) {
+ this.searchUseCaseInteractor = searchUseCaseInteractor;
+ }
+
+ /**
+ * Executes the Search Use Case.
+ * @param recipeName the recipe name
+ * @param calMin the minimum value for calories
+ * @param calMax the maximum value for calories
+ * @param carbMin the minimum value for carbohydrates
+ * @param carbMax the maximum value for carbohydrates
+ * @param proteinMin the minimum value for protein
+ * @param proteinMax the maximum value for protein
+ * @param fatMin the minimum value for fat
+ * @param fatMax the maximum value for fat
+ */
+ public void execute(String recipeName, String calMin, String calMax, String carbMin, String carbMax, String proteinMin, String proteinMax, String fatMin, String fatMax) {
+ final RecipeSearchInputData recipeSearchInputData = new RecipeSearchInputData(
+ recipeName, calMin, calMax, carbMin, carbMax, proteinMin, proteinMax, fatMin, fatMax);
+
+ searchUseCaseInteractor.execute(recipeSearchInputData);
+ }
+
+ /**
+ * Executes the "switch to SearchResultsView" Use Case.
+ */
+ public void switchToSearchResultsView() {
+ searchUseCaseInteractor.switchToSearchResultsView();
+ }
+
+ /**
+ * Executes the "switch to ProfileView" Use Case.
+ */
+ public void switchToProfileView() {
+ searchUseCaseInteractor.switchToProfileView();
+ }
+
+}
diff --git a/src/main/java/interface_adapter/recipe_search/RecipeSearchPresenter.java b/src/main/java/interface_adapter/recipe_search/RecipeSearchPresenter.java
new file mode 100644
index 000000000..ea2adf177
--- /dev/null
+++ b/src/main/java/interface_adapter/recipe_search/RecipeSearchPresenter.java
@@ -0,0 +1,56 @@
+package interface_adapter.recipe_search;
+
+import interface_adapter.ViewManagerModel;
+import interface_adapter.profile.ProfileViewModel;
+import interface_adapter.search_results.SearchResultsState;
+import interface_adapter.search_results.SearchResultsViewModel;
+import use_case.recipe_search.RecipeSearchOutputBoundary;
+import use_case.recipe_search.RecipeSearchOutputData;
+
+/**
+ * The Presenter for the Search Use Case.
+ */
+public class RecipeSearchPresenter implements RecipeSearchOutputBoundary {
+
+ private final RecipeSearchViewModel recipeSearchviewModel;
+ private final SearchResultsViewModel searchResultsViewModel;
+ private final ProfileViewModel profileViewModel;
+ private final ViewManagerModel viewManagerModel;
+
+ public RecipeSearchPresenter(RecipeSearchViewModel recipeSearchviewModel, SearchResultsViewModel searchResultsViewModel, ViewManagerModel viewManagerModel, ProfileViewModel profileViewModel) {
+ this.recipeSearchviewModel = recipeSearchviewModel;
+ this.searchResultsViewModel = searchResultsViewModel;
+ this.viewManagerModel = viewManagerModel;
+ this.profileViewModel = profileViewModel;
+ }
+
+ @Override
+ public void prepareSuccessView(RecipeSearchOutputData outputData) {
+ final SearchResultsState searchResultsState = searchResultsViewModel.getState();
+ searchResultsState.setSearchResults(outputData.getSearchResults());
+ this.searchResultsViewModel.setState(searchResultsState);
+ this.searchResultsViewModel.firePropertyChanged();
+
+ this.viewManagerModel.setState(searchResultsViewModel.getViewName());
+ this.viewManagerModel.firePropertyChanged();
+ }
+
+ @Override
+ public void prepareFailView(String error) {
+ final RecipeSearchState recipeSearchState = recipeSearchviewModel.getState();
+ recipeSearchState.setErrorMessage(error);
+ recipeSearchviewModel.firePropertyChanged();
+ }
+
+ @Override
+ public void switchToResultsView() {
+ viewManagerModel.setState(searchResultsViewModel.getViewName());
+ viewManagerModel.firePropertyChanged();
+ }
+
+ @Override
+ public void switchToProfileView() {
+ viewManagerModel.setState(profileViewModel.getViewName());
+ viewManagerModel.firePropertyChanged();
+ }
+}
diff --git a/src/main/java/interface_adapter/recipe_search/RecipeSearchState.java b/src/main/java/interface_adapter/recipe_search/RecipeSearchState.java
new file mode 100644
index 000000000..b1586328a
--- /dev/null
+++ b/src/main/java/interface_adapter/recipe_search/RecipeSearchState.java
@@ -0,0 +1,98 @@
+package interface_adapter.recipe_search;
+
+/**
+ * The state for the Recipe Search View Model.
+ */
+public class RecipeSearchState {
+
+ private String recipeName = "";
+ private String calMin = "";
+ private String calMax = "";
+ private String carbMin = "";
+ private String carbMax = "";
+ private String proteinMin = "";
+ private String proteinMax = "";
+ private String fatMin = "";
+ private String fatMax = "";
+ private String errorMessage;
+
+ public String getRecipeName() {
+ return recipeName;
+ }
+
+ public void setRecipeName(String recipeName) {
+ this.recipeName = recipeName;
+ }
+
+ public String getCalMin() {
+ return calMin;
+ }
+
+ public void setCalMin(String calMin) {
+ this.calMin = calMin;
+ }
+
+ public String getCalMax() {
+ return calMax;
+ }
+
+ public void setCalMax(String calMax) {
+ this.calMax = calMax;
+ }
+
+ public String getCarbMin() {
+ return carbMin;
+ }
+
+ public void setCarbMin(String carbMin) {
+ this.carbMin = carbMin;
+ }
+
+ public String getCarbMax() {
+ return carbMax;
+ }
+
+ public void setCarbMax(String carbMax) {
+ this.carbMax = carbMax;
+ }
+
+ public String getProteinMin() {
+ return proteinMin;
+ }
+
+ public void setProteinMin(String proteinMin) {
+ this.proteinMin = proteinMin;
+ }
+
+ public String getProteinMax() {
+ return proteinMax;
+ }
+
+ public void setProteinMax(String proteinMax) {
+ this.proteinMax = proteinMax;
+ }
+
+ public String getFatMin() {
+ return this.fatMin;
+ }
+
+ public void setFatMin(String fatMin) {
+ this.fatMin = fatMin;
+ }
+
+ public String getFatMax() {
+ return this.fatMax;
+ }
+
+ public void setFatMax(String fatMax) {
+ this.fatMax = fatMax;
+ }
+
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+
+ public void setErrorMessage(String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+}
diff --git a/src/main/java/interface_adapter/recipe_search/RecipeSearchViewModel.java b/src/main/java/interface_adapter/recipe_search/RecipeSearchViewModel.java
new file mode 100644
index 000000000..51b3630c5
--- /dev/null
+++ b/src/main/java/interface_adapter/recipe_search/RecipeSearchViewModel.java
@@ -0,0 +1,14 @@
+package interface_adapter.recipe_search;
+
+import interface_adapter.ViewModel;
+
+/**
+ * The ViewModel for the Recipe Search View.
+ */
+public class RecipeSearchViewModel extends ViewModel {
+
+ public RecipeSearchViewModel() {
+ super("Recipe Search");
+ setState(new RecipeSearchState());
+ }
+}
diff --git a/src/main/java/interface_adapter/saved_recipes/SavedrecipesController.java b/src/main/java/interface_adapter/saved_recipes/SavedrecipesController.java
new file mode 100644
index 000000000..e18b0e3a8
--- /dev/null
+++ b/src/main/java/interface_adapter/saved_recipes/SavedrecipesController.java
@@ -0,0 +1,25 @@
+package interface_adapter.saved_recipes;
+
+import use_case.saved_recipes.SavedRecipeInputBoundary;
+import use_case.saved_recipes.SavedRecipeInputData;
+
+/**
+ * The controller for the Saved Recipes view.
+ */
+public class SavedrecipesController {
+
+ private final SavedRecipeInputBoundary savedRecipeInputBoundary;
+
+ public SavedrecipesController(SavedRecipeInputBoundary savedRecipeInputBoundary) {
+ this.savedRecipeInputBoundary = savedRecipeInputBoundary;
+ }
+
+ /**
+ * Switch to the Profile view.
+ * @param username username of currently logged-in User.
+ */
+ public void switchToProfileView(String username) {
+ final SavedRecipeInputData savedRecipeInputData = new SavedRecipeInputData(username);
+ savedRecipeInputBoundary.switchToProfileView(savedRecipeInputData);
+ }
+}
diff --git a/src/main/java/interface_adapter/saved_recipes/SavedrecipesPresenter.java b/src/main/java/interface_adapter/saved_recipes/SavedrecipesPresenter.java
new file mode 100644
index 000000000..471db3c21
--- /dev/null
+++ b/src/main/java/interface_adapter/saved_recipes/SavedrecipesPresenter.java
@@ -0,0 +1,36 @@
+package interface_adapter.saved_recipes;
+
+import interface_adapter.ViewManagerModel;
+import interface_adapter.profile.ProfileState;
+import interface_adapter.profile.ProfileViewModel;
+import use_case.saved_recipes.SavedRecipeOutputBoundry;
+import use_case.saved_recipes.SavedRecipeOutputData;
+
+/**
+ * The Presenter for the Saved Recipes view.
+ */
+public class SavedrecipesPresenter implements SavedRecipeOutputBoundry {
+
+ private final SavedrecipesViewModel savedrecipesViewModel;
+ private final ProfileViewModel profileViewModel;
+ private final ViewManagerModel viewManagerModel;
+
+ public SavedrecipesPresenter(SavedrecipesViewModel savedrecipesViewModel,
+ ProfileViewModel profileViewModel, ViewManagerModel viewManagerModel) {
+ this.savedrecipesViewModel = savedrecipesViewModel;
+ this.profileViewModel = profileViewModel;
+ this.viewManagerModel = viewManagerModel;
+ }
+
+ @Override
+ public void switchToProfileView(SavedRecipeOutputData response) {
+ final ProfileState profileState = profileViewModel.getState();
+ profileState.setUsername(response.getUsername());
+
+ this.profileViewModel.setState(profileState);
+ this.profileViewModel.firePropertyChanged();
+
+ this.viewManagerModel.setState(this.profileViewModel.getViewName());
+ this.viewManagerModel.firePropertyChanged();
+ }
+}
diff --git a/src/main/java/interface_adapter/saved_recipes/SavedrecipesState.java b/src/main/java/interface_adapter/saved_recipes/SavedrecipesState.java
new file mode 100644
index 000000000..b3638acf8
--- /dev/null
+++ b/src/main/java/interface_adapter/saved_recipes/SavedrecipesState.java
@@ -0,0 +1,38 @@
+package interface_adapter.saved_recipes;
+
+import java.util.Map;
+
+import entity.Recipe;
+
+/**
+ * The state for the saved recipes View Model.
+ */
+public class SavedrecipesState {
+ private String username;
+ private Map recipes;
+
+ public SavedrecipesState(SavedrecipesState copy) {
+ this.username = copy.username;
+ this.recipes = copy.recipes;
+ }
+
+ public SavedrecipesState() {
+
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public Map getRecipes() {
+ return recipes;
+ }
+
+ public void setRecipes(Map recipes) {
+ this.recipes = recipes;
+ }
+}
diff --git a/src/main/java/interface_adapter/saved_recipes/SavedrecipesViewModel.java b/src/main/java/interface_adapter/saved_recipes/SavedrecipesViewModel.java
new file mode 100644
index 000000000..1654be852
--- /dev/null
+++ b/src/main/java/interface_adapter/saved_recipes/SavedrecipesViewModel.java
@@ -0,0 +1,14 @@
+package interface_adapter.saved_recipes;
+
+import interface_adapter.ViewModel;
+
+/**
+ * The View Model for the SavedRecipe View.
+ */
+public class SavedrecipesViewModel extends ViewModel {
+
+ public SavedrecipesViewModel() {
+ super("SavedRecipes");
+ setState(new SavedrecipesState());
+ }
+}
diff --git a/src/main/java/interface_adapter/search_results/SearchResultsController.java b/src/main/java/interface_adapter/search_results/SearchResultsController.java
new file mode 100644
index 000000000..c53ca19c8
--- /dev/null
+++ b/src/main/java/interface_adapter/search_results/SearchResultsController.java
@@ -0,0 +1,32 @@
+package interface_adapter.search_results;
+
+import entity.Recipe;
+import use_case.search_results.SearchResultsInputBoundary;
+import use_case.search_results.SearchResultsInputData;
+
+/**
+ * The controller for the Search Results Use Case.
+ */
+public class SearchResultsController {
+
+ private final SearchResultsInputBoundary searchResultsInputBoundary;
+
+ public SearchResultsController(SearchResultsInputBoundary searchResultsInputBoundary) {
+ this.searchResultsInputBoundary = searchResultsInputBoundary;
+ }
+
+ /**
+ * Executes the "switch to RecipeSearchView" Use Case.
+ */
+ public void switchToRecipeSearchView() {
+ this.searchResultsInputBoundary.switchToRecipeSearchView();
+ }
+
+ /**
+ * Executes the "switch to RecipeDetailsView" Use Case.
+ */
+ public void switchToRecipeDetailsView(Recipe recipe) {
+ final SearchResultsInputData searchResultsInputData = new SearchResultsInputData(recipe);
+ this.searchResultsInputBoundary.switchToRecipeDetailsView(searchResultsInputData);
+ }
+}
diff --git a/src/main/java/interface_adapter/search_results/SearchResultsPresenter.java b/src/main/java/interface_adapter/search_results/SearchResultsPresenter.java
new file mode 100644
index 000000000..5cf326e3c
--- /dev/null
+++ b/src/main/java/interface_adapter/search_results/SearchResultsPresenter.java
@@ -0,0 +1,44 @@
+package interface_adapter.search_results;
+
+import interface_adapter.ViewManagerModel;
+import interface_adapter.recipe_details.RecipeDetailsState;
+import interface_adapter.recipe_details.RecipeDetailsViewModel;
+import interface_adapter.recipe_search.RecipeSearchViewModel;
+import use_case.search_results.SearchResultsOutputBoundary;
+import use_case.search_results.SearchResultsOutputData;
+
+/**
+ * The Presenter for the Search Results Use Case.
+ */
+public class SearchResultsPresenter implements SearchResultsOutputBoundary {
+
+ private final SearchResultsViewModel searchResultsViewModel;
+ private final RecipeDetailsViewModel recipeDetailsViewModel;
+ private final ViewManagerModel viewManagerModel;
+ private final RecipeSearchViewModel recipeSearchViewModel;
+
+ public SearchResultsPresenter(SearchResultsViewModel searchResultsViewModel, RecipeDetailsViewModel recipeDetailsViewModel, ViewManagerModel viewManagerModel , RecipeSearchViewModel recipeSearchViewModel) {
+ this.searchResultsViewModel = searchResultsViewModel;
+ this.recipeDetailsViewModel = recipeDetailsViewModel;
+ this.viewManagerModel = viewManagerModel;
+ this.recipeSearchViewModel = recipeSearchViewModel;
+ }
+
+ @Override
+ public void switchToRecipeSearchView() {
+ viewManagerModel.setState(recipeSearchViewModel.getViewName());
+ viewManagerModel.firePropertyChanged();
+ }
+
+ @Override
+ public void switchToRecipeDetailsView(SearchResultsOutputData response) {
+ final RecipeDetailsState recipeDetailsState = recipeDetailsViewModel.getState();
+ recipeDetailsState.setRecipe(response.getRecipe());
+ recipeDetailsState.setUsername(response.getUsername());
+ this.recipeDetailsViewModel.setState(recipeDetailsState);
+ this.recipeDetailsViewModel.firePropertyChanged();
+ viewManagerModel.setState(recipeDetailsViewModel.getViewName());
+ viewManagerModel.firePropertyChanged();
+ }
+
+}
diff --git a/src/main/java/interface_adapter/search_results/SearchResultsState.java b/src/main/java/interface_adapter/search_results/SearchResultsState.java
new file mode 100644
index 000000000..659625a63
--- /dev/null
+++ b/src/main/java/interface_adapter/search_results/SearchResultsState.java
@@ -0,0 +1,21 @@
+package interface_adapter.search_results;
+
+import java.util.List;
+
+import entity.Recipe;
+
+/**
+ * The state for the Search Results View Model.
+ */
+public class SearchResultsState {
+
+ private List searchResults;
+
+ public void setSearchResults(List searchResults) {
+ this.searchResults = searchResults;
+ }
+
+ public List getSearchResults() {
+ return searchResults;
+ }
+}
diff --git a/src/main/java/interface_adapter/search_results/SearchResultsViewModel.java b/src/main/java/interface_adapter/search_results/SearchResultsViewModel.java
new file mode 100644
index 000000000..93b5e1289
--- /dev/null
+++ b/src/main/java/interface_adapter/search_results/SearchResultsViewModel.java
@@ -0,0 +1,15 @@
+package interface_adapter.search_results;
+
+import interface_adapter.ViewModel;
+
+/**
+ * The ViewModel for the Search Results View.
+ */
+public class SearchResultsViewModel extends ViewModel {
+
+ public SearchResultsViewModel() {
+ super("Results");
+ setState(new SearchResultsState());
+ }
+
+}
diff --git a/src/main/java/use_case/login/LoginInputBoundary.java b/src/main/java/use_case/login/LoginInputBoundary.java
index faf72dc96..5e0b73f3a 100644
--- a/src/main/java/use_case/login/LoginInputBoundary.java
+++ b/src/main/java/use_case/login/LoginInputBoundary.java
@@ -10,4 +10,6 @@ public interface LoginInputBoundary {
* @param loginInputData the input data
*/
void execute(LoginInputData loginInputData);
+
+ // void switchToForgotPasswordView();
}
diff --git a/src/main/java/use_case/login/LoginInteractor.java b/src/main/java/use_case/login/LoginInteractor.java
index 5b36ddcd8..a71c36f12 100644
--- a/src/main/java/use_case/login/LoginInteractor.java
+++ b/src/main/java/use_case/login/LoginInteractor.java
@@ -37,4 +37,8 @@ public void execute(LoginInputData loginInputData) {
}
}
}
+
+ // public void switchToForgotPasswordView() {
+ // loginPresenter.switchToForgotPasswordView();
+ // }
}
diff --git a/src/main/java/use_case/login/LoginOutputBoundary.java b/src/main/java/use_case/login/LoginOutputBoundary.java
index 08bc4731f..6d141bcfb 100644
--- a/src/main/java/use_case/login/LoginOutputBoundary.java
+++ b/src/main/java/use_case/login/LoginOutputBoundary.java
@@ -15,4 +15,6 @@ public interface LoginOutputBoundary {
* @param errorMessage the explanation of the failure
*/
void prepareFailView(String errorMessage);
+
+ // void switchToForgotPasswordView();
}
diff --git a/src/main/java/use_case/logout/LogoutInputData.java b/src/main/java/use_case/logout/LogoutInputData.java
index 56a33b375..ca5cf8a56 100644
--- a/src/main/java/use_case/logout/LogoutInputData.java
+++ b/src/main/java/use_case/logout/LogoutInputData.java
@@ -4,9 +4,14 @@
* The Input Data for the Logout Use Case.
*/
public class LogoutInputData {
+ private String username;
public LogoutInputData(String username) {
- // TODO: save the current username in an instance variable and add a getter.
+ this.username = username;
+ }
+
+ public String getUsername() {
+ return username;
}
}
diff --git a/src/main/java/use_case/logout/LogoutInteractor.java b/src/main/java/use_case/logout/LogoutInteractor.java
index 1ca93b44e..7060c302d 100644
--- a/src/main/java/use_case/logout/LogoutInteractor.java
+++ b/src/main/java/use_case/logout/LogoutInteractor.java
@@ -9,17 +9,20 @@ public class LogoutInteractor implements LogoutInputBoundary {
public LogoutInteractor(LogoutUserDataAccessInterface userDataAccessInterface,
LogoutOutputBoundary logoutOutputBoundary) {
- // TODO: save the DAO and Presenter in the instance variables.
- // Which parameter is the DAO and which is the presenter?
+ this.logoutPresenter = logoutOutputBoundary;
+ this.userDataAccessObject = userDataAccessInterface;
}
@Override
public void execute(LogoutInputData logoutInputData) {
- // TODO: implement the logic of the Logout Use Case (depends on the LogoutInputData.java TODO)
// * get the username out of the input data,
+ final String username = logoutInputData.getUsername();
// * set the username to null in the DAO
+ this.userDataAccessObject.setCurrentUsername(null);
// * instantiate the `LogoutOutputData`, which needs to contain the username.
+ final LogoutOutputData logoutOutputData = new LogoutOutputData(username, false);
// * tell the presenter to prepare a success view.
+ this.logoutPresenter.prepareSuccessView(logoutOutputData);
}
}
diff --git a/src/main/java/use_case/logout/LogoutOutputData.java b/src/main/java/use_case/logout/LogoutOutputData.java
index 974279155..9f8e20539 100644
--- a/src/main/java/use_case/logout/LogoutOutputData.java
+++ b/src/main/java/use_case/logout/LogoutOutputData.java
@@ -9,7 +9,8 @@ public class LogoutOutputData {
private boolean useCaseFailed;
public LogoutOutputData(String username, boolean useCaseFailed) {
- // TODO: save the parameters in the instance variables.
+ this.username = username;
+ this.useCaseFailed = useCaseFailed;
}
public String getUsername() {
diff --git a/src/main/java/use_case/profile/ProfileDataAccessInterface.java b/src/main/java/use_case/profile/ProfileDataAccessInterface.java
new file mode 100644
index 000000000..313dff86f
--- /dev/null
+++ b/src/main/java/use_case/profile/ProfileDataAccessInterface.java
@@ -0,0 +1,16 @@
+package use_case.profile;
+
+import entity.User;
+
+/**
+ * Access the user object of the currently logged-in User.
+ */
+public interface ProfileDataAccessInterface {
+
+ /**
+ * Access the user object from Data access with username.
+ * @param username the name of the user.
+ * @return User object associated with the username.
+ */
+ User get(String username);
+}
diff --git a/src/main/java/use_case/profile/ProfileInputBoundary.java b/src/main/java/use_case/profile/ProfileInputBoundary.java
new file mode 100644
index 000000000..34f9e112d
--- /dev/null
+++ b/src/main/java/use_case/profile/ProfileInputBoundary.java
@@ -0,0 +1,18 @@
+package use_case.profile;
+
+/**
+ * Input boundry of the Profile View.
+ */
+public interface ProfileInputBoundary {
+
+ /**
+ * Executes the switch to savedRecipes use case.
+ * @param profileInputData The input data of the profile view
+ */
+ void switchToSavedRecipesView(ProfileInputData profileInputData);
+
+ /**
+ * Executes the switch to Recipesearch use case.
+ */
+ void switchToRecipeSearchView();
+}
diff --git a/src/main/java/use_case/profile/ProfileInputData.java b/src/main/java/use_case/profile/ProfileInputData.java
new file mode 100644
index 000000000..f754bd87c
--- /dev/null
+++ b/src/main/java/use_case/profile/ProfileInputData.java
@@ -0,0 +1,17 @@
+package use_case.profile;
+
+/**
+ * Profile view input data.
+ */
+public class ProfileInputData {
+
+ private final String username;
+
+ public ProfileInputData(String username) {
+ this.username = username;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+}
diff --git a/src/main/java/use_case/profile/ProfileInteractor.java b/src/main/java/use_case/profile/ProfileInteractor.java
new file mode 100644
index 000000000..3710a8271
--- /dev/null
+++ b/src/main/java/use_case/profile/ProfileInteractor.java
@@ -0,0 +1,36 @@
+package use_case.profile;
+
+import java.util.Map;
+
+import entity.Recipe;
+
+/**
+ * Interactor for the Profile View.
+ */
+public class ProfileInteractor implements ProfileInputBoundary {
+ private final ProfileOutputBoundary userPresenter;
+ private final ProfileDataAccessInterface userDataAccess;
+
+ public ProfileInteractor(ProfileOutputBoundary profileOutputBoundary, ProfileDataAccessInterface userDataAccess) {
+ this.userPresenter = profileOutputBoundary;
+ this.userDataAccess = userDataAccess;
+ }
+
+ /**
+ * Executes switch to SavedRecipes.
+ * @param profileInputData Input data of Profile view.
+ */
+ public void switchToSavedRecipesView(ProfileInputData profileInputData) {
+ final String username = profileInputData.getUsername();
+ final Map savedRecipes = userDataAccess.get(username).getRecipes();
+ final ProfileOutputData profileOutputData = new ProfileOutputData(username, savedRecipes, false);
+ userPresenter.switchtoSavedrecipeView(profileOutputData);
+ }
+
+ /**
+ * Execute the switch to RecipeSearchView.
+ */
+ public void switchToRecipeSearchView() {
+ userPresenter.switchToRecipeSearchView();
+ }
+}
diff --git a/src/main/java/use_case/profile/ProfileOutputBoundary.java b/src/main/java/use_case/profile/ProfileOutputBoundary.java
new file mode 100644
index 000000000..a484fe4ab
--- /dev/null
+++ b/src/main/java/use_case/profile/ProfileOutputBoundary.java
@@ -0,0 +1,18 @@
+package use_case.profile;
+
+/**
+ * Output boundary for the Profile View.
+ */
+public interface ProfileOutputBoundary {
+
+ /**
+ * Executes SavedRecipes switch.
+ * @param response Profile Output data
+ */
+ void switchtoSavedrecipeView(ProfileOutputData response);
+
+ /**
+ * Executes Recipe Search switch.
+ */
+ void switchToRecipeSearchView();
+}
diff --git a/src/main/java/use_case/profile/ProfileOutputData.java b/src/main/java/use_case/profile/ProfileOutputData.java
new file mode 100644
index 000000000..74eb97d52
--- /dev/null
+++ b/src/main/java/use_case/profile/ProfileOutputData.java
@@ -0,0 +1,33 @@
+package use_case.profile;
+
+import java.util.Map;
+
+import entity.Recipe;
+
+/**
+ * Output data of profile view.
+ */
+public class ProfileOutputData {
+
+ private final Map recipes;
+ private final String username;
+ private final boolean useCaseFailed;
+
+ public ProfileOutputData(String username, Map recipes, boolean useCaseFailed) {
+ this.username = username;
+ this.recipes = recipes;
+ this.useCaseFailed = useCaseFailed;
+ }
+
+ public Map getRecipes() {
+ return recipes;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public boolean isUseCaseFailed() {
+ return useCaseFailed;
+ }
+}
diff --git a/src/main/java/use_case/recipe_search/RecipeSearchDataAccessInterface.java b/src/main/java/use_case/recipe_search/RecipeSearchDataAccessInterface.java
new file mode 100644
index 000000000..ceeb7da8d
--- /dev/null
+++ b/src/main/java/use_case/recipe_search/RecipeSearchDataAccessInterface.java
@@ -0,0 +1,26 @@
+package use_case.recipe_search;
+
+import java.util.List;
+
+import entity.Recipe;
+
+/**
+ * DAO for the Search Use Case.
+ */
+public interface RecipeSearchDataAccessInterface {
+
+ /**
+ * Executes the Search Use Case.
+ * @param recipeName the recipe name
+ * @param calMin the minimum value for calories
+ * @param calMax the maximum value for calories
+ * @param carbMin the minimum value for carbohydrates
+ * @param carbMax the maximum value for carbohydrates
+ * @param proteinMin the minimum value for protein
+ * @param proteinMax the maximum value for protein
+ * @param fatMin the minimum value for fat
+ * @param fatMax the maximum value for fat
+ * @return recipeList a list of all recipes that match the filters
+ */
+ List searchRecipe(String recipeName, String calMin, String calMax, String carbMin, String carbMax, String proteinMin, String proteinMax, String fatMin, String fatMax);
+}
diff --git a/src/main/java/use_case/recipe_search/RecipeSearchInputBoundary.java b/src/main/java/use_case/recipe_search/RecipeSearchInputBoundary.java
new file mode 100644
index 000000000..bbb68591e
--- /dev/null
+++ b/src/main/java/use_case/recipe_search/RecipeSearchInputBoundary.java
@@ -0,0 +1,23 @@
+package use_case.recipe_search;
+
+/**
+ * Input Boundary for actions which are related to searching.
+ */
+public interface RecipeSearchInputBoundary {
+
+ /**
+ * Executes the search use case.
+ * @param recipeSearchInputData the input data
+ */
+ void execute(RecipeSearchInputData recipeSearchInputData);
+
+ /**
+ * Executes the switch to search view use case.
+ */
+ void switchToSearchResultsView();
+
+ /**
+ * Executes the switch to Profile view.
+ */
+ void switchToProfileView();
+}
diff --git a/src/main/java/use_case/recipe_search/RecipeSearchInputData.java b/src/main/java/use_case/recipe_search/RecipeSearchInputData.java
new file mode 100644
index 000000000..747b83b2f
--- /dev/null
+++ b/src/main/java/use_case/recipe_search/RecipeSearchInputData.java
@@ -0,0 +1,66 @@
+package use_case.recipe_search;
+
+/**
+ * The Input Data for the Search Use Case.
+ */
+public class RecipeSearchInputData {
+
+ private final String recipeName;
+ private final String calMin;
+ private final String calMax;
+ private final String carbMin;
+ private final String carbMax;
+ private final String proteinMin;
+ private final String proteinMax;
+ private final String fatMin;
+ private final String fatMax;
+
+ public RecipeSearchInputData(String recipeName, String calMin, String calMax, String carbMin, String carbMax, String proteinMin, String proteinMax, String fatMin, String fatMax) {
+ this.recipeName = recipeName;
+ this.calMin = calMin;
+ this.calMax = calMax;
+ this.carbMin = carbMin;
+ this.carbMax = carbMax;
+ this.proteinMin = proteinMin;
+ this.proteinMax = proteinMax;
+ this.fatMin = fatMin;
+ this.fatMax = fatMax;
+ }
+
+ public String getRecipeName() {
+ return recipeName;
+ }
+
+ public String getCalMin() {
+ return calMin;
+ }
+
+ public String getCalMax() {
+ return calMax;
+ }
+
+ public String getCarbMin() {
+ return carbMin;
+ }
+
+ public String getCarbMax() {
+ return carbMax;
+ }
+
+ public String getProteinMin() {
+ return proteinMin;
+ }
+
+ public String getProteinMax() {
+ return proteinMax;
+ }
+
+ public String getFatMin() {
+ return fatMin;
+ }
+
+ public String getFatMax() {
+ return fatMax;
+ }
+}
+
diff --git a/src/main/java/use_case/recipe_search/RecipeSearchInteractor.java b/src/main/java/use_case/recipe_search/RecipeSearchInteractor.java
new file mode 100644
index 000000000..6ebc46404
--- /dev/null
+++ b/src/main/java/use_case/recipe_search/RecipeSearchInteractor.java
@@ -0,0 +1,112 @@
+package use_case.recipe_search;
+
+import java.util.Arrays;
+import java.util.List;
+
+import entity.Recipe;
+
+/**
+ * The Search Interactor.
+ */
+public class RecipeSearchInteractor implements RecipeSearchInputBoundary {
+ private final RecipeSearchDataAccessInterface recipeDataAccessObject;
+ private final RecipeSearchOutputBoundary recipePresenter;
+
+ public RecipeSearchInteractor(RecipeSearchDataAccessInterface recipeDataAccessObject,
+ RecipeSearchOutputBoundary recipePresenter) {
+ this.recipeDataAccessObject = recipeDataAccessObject;
+ this.recipePresenter = recipePresenter;
+ }
+
+ @Override
+ public void execute(RecipeSearchInputData recipeSearchInputData) {
+ final String recipeName = recipeSearchInputData.getRecipeName();
+ final String calMin = recipeSearchInputData.getCalMin();
+ final String calMax = recipeSearchInputData.getCalMax();
+ final String carbMin = recipeSearchInputData.getCarbMin();
+ final String carbMax = recipeSearchInputData.getCarbMax();
+ final String proteinMin = recipeSearchInputData.getProteinMin();
+ final String proteinMax = recipeSearchInputData.getProteinMax();
+ final String fatMin = recipeSearchInputData.getFatMin();
+ final String fatMax = recipeSearchInputData.getFatMax();
+ boolean value = false;
+
+ // Ensure at least one parameter is provided
+ if (isAllParametersEmpty(Arrays.asList(recipeName, calMin, calMax, carbMin, carbMax, proteinMin,
+ proteinMax, fatMin, fatMax))) {
+ recipePresenter.prepareFailView("At least one filter parameter must be provided.");
+ value = true;
+ }
+
+ // Validate ranges for each parameter
+ if (!isValidRange(calMin, calMax)) {
+ recipePresenter.prepareFailView("Invalid calorie range: Minimum cannot be greater than maximum.");
+ value = true;
+ }
+ if (!isValidRange(carbMin, carbMax)) {
+ recipePresenter.prepareFailView("Invalid carbohydrate range: Minimum cannot be greater than maximum.");
+ value = true;
+ }
+ if (!isValidRange(proteinMin, proteinMax)) {
+ recipePresenter.prepareFailView("Invalid protein range: Minimum cannot be greater than maximum.");
+ value = true;
+ }
+ if (!isValidRange(fatMin, fatMax)) {
+ recipePresenter.prepareFailView("Invalid fat range: Minimum cannot be greater than maximum.");
+ value = true;
+ }
+
+ // Fetch recipes using DAO
+ final List recipes = recipeDataAccessObject.searchRecipe(recipeName,
+ calMin, calMax, carbMin, carbMax, proteinMin, proteinMax, fatMin, fatMax
+ );
+
+ if (recipes.isEmpty() && !value) {
+ recipePresenter.prepareFailView("No recipes found for the given filters.");
+ value = true;
+ }
+ if (!value) {
+ final RecipeSearchOutputData outputData = new RecipeSearchOutputData(recipes, false);
+ recipePresenter.prepareSuccessView(outputData);
+ }
+ }
+
+ // Check if all parameters are empty or null
+ private boolean isAllParametersEmpty(List parameters) {
+ boolean value = true;
+ for (String param : parameters) {
+ if (param != null && !param.trim().isEmpty()) {
+ value = false;
+ break;
+ }
+ }
+ return value;
+ }
+
+ // Validate the range (min <= max)
+ private boolean isValidRange(String min, String max) {
+ boolean isValid = true;
+
+ if (!(min == null || min.trim().isEmpty() || max == null || max.trim().isEmpty())) {
+ try {
+ final int minValue = Integer.parseInt(min);
+ final int maxValue = Integer.parseInt(max);
+ isValid = minValue <= maxValue;
+ }
+ catch (NumberFormatException exception) {
+ isValid = false;
+ }
+ }
+ return isValid;
+ }
+
+ @Override
+ public void switchToSearchResultsView() {
+ recipePresenter.switchToResultsView();
+ }
+
+ @Override
+ public void switchToProfileView() {
+ recipePresenter.switchToProfileView();
+ }
+}
diff --git a/src/main/java/use_case/recipe_search/RecipeSearchOutputBoundary.java b/src/main/java/use_case/recipe_search/RecipeSearchOutputBoundary.java
new file mode 100644
index 000000000..917b0ae22
--- /dev/null
+++ b/src/main/java/use_case/recipe_search/RecipeSearchOutputBoundary.java
@@ -0,0 +1,30 @@
+package use_case.recipe_search;
+
+/**
+ * The output boundary for the Signup Use Case.
+ */
+public interface RecipeSearchOutputBoundary {
+
+ /**
+ * Prepares the success view for the Search Use Case.
+ * @param outputData the output data
+ */
+ void prepareSuccessView(RecipeSearchOutputData outputData);
+
+ /**
+ * Prepares the failure view for the Search Use Case.
+ * @param errorMessage the explanation of the failure
+ */
+ void prepareFailView(String errorMessage);
+
+ /**
+ * Switches to the Search Results View.
+ */
+ void switchToResultsView();
+
+ /**
+ * Switches to the Profile View.
+ */
+ void switchToProfileView();
+
+}
diff --git a/src/main/java/use_case/recipe_search/RecipeSearchOutputData.java b/src/main/java/use_case/recipe_search/RecipeSearchOutputData.java
new file mode 100644
index 000000000..b242bcd55
--- /dev/null
+++ b/src/main/java/use_case/recipe_search/RecipeSearchOutputData.java
@@ -0,0 +1,28 @@
+package use_case.recipe_search;
+
+import java.util.List;
+
+import entity.Recipe;
+
+/**
+ * Output Data for the Search Use Case.
+ */
+public class RecipeSearchOutputData {
+
+ private final List searchResults;
+
+ private final boolean useCaseFailed;
+
+ public RecipeSearchOutputData(List searchResults, boolean useCaseFailed) {
+ this.searchResults = searchResults;
+ this.useCaseFailed = useCaseFailed;
+ }
+
+ public List getSearchResults() {
+ return searchResults;
+ }
+
+ public boolean isUseCaseFailed() {
+ return useCaseFailed;
+ }
+}
diff --git a/src/main/java/use_case/review_recipe/RecipeReviewDataAccessInterface.java b/src/main/java/use_case/review_recipe/RecipeReviewDataAccessInterface.java
new file mode 100644
index 000000000..8828e1347
--- /dev/null
+++ b/src/main/java/use_case/review_recipe/RecipeReviewDataAccessInterface.java
@@ -0,0 +1,10 @@
+package use_case.review_recipe;
+
+import entity.User;
+
+public interface RecipeReviewDataAccessInterface {
+
+ String getCurrentUsername();
+
+ User get(String username);
+}
diff --git a/src/main/java/use_case/review_recipe/RecipeReviewInputBoundary.java b/src/main/java/use_case/review_recipe/RecipeReviewInputBoundary.java
new file mode 100644
index 000000000..deec0b776
--- /dev/null
+++ b/src/main/java/use_case/review_recipe/RecipeReviewInputBoundary.java
@@ -0,0 +1,5 @@
+package use_case.review_recipe;
+
+public class RecipeReviewInputBoundary {
+
+}
diff --git a/src/main/java/use_case/review_recipe/RecipeReviewInputData.java b/src/main/java/use_case/review_recipe/RecipeReviewInputData.java
new file mode 100644
index 000000000..2138fa793
--- /dev/null
+++ b/src/main/java/use_case/review_recipe/RecipeReviewInputData.java
@@ -0,0 +1,33 @@
+package use_case.review_recipe;
+
+import entity.Recipe;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The Input Data for the Review Recipe Use Case.
+ */
+
+public class RecipeReviewInputData {
+ private final Recipe recipe;
+ private final String name;
+ private final int rating;
+
+ public RecipeReviewInputData(Recipe recipe, int rating) {
+ this.recipe = recipe;
+ this.name = recipe.getName();
+ this.rating = rating;
+ }
+
+ public Recipe getRecipe() {
+ return recipe;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getRating() {
+ return rating;
+ }
+}
diff --git a/src/main/java/use_case/review_recipe/RecipeReviewInteractor.java b/src/main/java/use_case/review_recipe/RecipeReviewInteractor.java
new file mode 100644
index 000000000..95602510a
--- /dev/null
+++ b/src/main/java/use_case/review_recipe/RecipeReviewInteractor.java
@@ -0,0 +1,63 @@
+package use_case.review_recipe;
+
+import entity.CommonUser;
+import entity.Recipe;
+import entity.User;
+import use_case.profile.ProfileDataAccessInterface;
+
+/**
+ * The Recipe Review Interactor.
+ */
+public class RecipeReviewInteractor implements ReviewRecipeInputBoundary {
+ private final RecipeReviewOutputBoundary reviewPresenter;
+ private final RecipeReviewDataAccessInterface userDataAccess;
+
+ public RecipeReviewInteractor(RecipeReviewOutputBoundary reviewPresenter, RecipeReviewDataAccessInterface recipeReviewDataAccessInterface) {
+ this.reviewPresenter = reviewPresenter;
+ this.userDataAccess = recipeReviewDataAccessInterface;
+ }
+
+ /**
+ * Executes the recipe review use case.
+ * @param recipeReviewInputData the input data
+ */
+// public void execute(RecipeReviewInputData recipeReviewInputData) {
+// final Recipe recipe = recipeReviewInputData.getRecipe();
+// final int rating = recipeReviewInputData.getRating();
+// final RecipeReviewOutputData recipeReviewOutputData;
+//
+// try {
+// user.reviewRecipe(recipe, rating);
+// recipeReviewOutputData = new RecipeReviewOutputData(
+// recipe.getName(),
+// rating,
+// "Review added."
+// );
+// reviewPresenter.prepareSuccessView(recipeReviewOutputData);
+// }
+// catch (IllegalArgumentException ex) {
+// reviewPresenter.prepareFailView("Review could not be added.");
+// }
+// }
+
+
+ public void execute(RecipeReviewInputData recipeReviewInputData) {
+ final String username = userDataAccess.getCurrentUsername();
+ final User user = userDataAccess.get(username);
+ final Recipe recipe = recipeReviewInputData.getRecipe();
+ final int rating = recipeReviewInputData.getRating();
+ final Integer ratingInteger = Integer.valueOf(rating);
+ user.addRecipe(recipe, ratingInteger);
+ final RecipeReviewOutputData recipeReviewOutputData = new RecipeReviewOutputData(username);
+ reviewPresenter.prepareSuccessView(recipeReviewOutputData);
+
+ }
+
+ /**
+ * Switches to the SavedRecipes View.
+ *
+ */
+ public void switchToSavedrecipesView() {
+ reviewPresenter.switchToSavedrecipesView();
+ }
+}
diff --git a/src/main/java/use_case/review_recipe/RecipeReviewOutputBoundary.java b/src/main/java/use_case/review_recipe/RecipeReviewOutputBoundary.java
new file mode 100644
index 000000000..820e1614b
--- /dev/null
+++ b/src/main/java/use_case/review_recipe/RecipeReviewOutputBoundary.java
@@ -0,0 +1,23 @@
+package use_case.review_recipe;
+
+/**
+ * The output boundary for the Recipe Review Use Case.
+ */
+public interface RecipeReviewOutputBoundary {
+ /**
+ * Prepares the success view for the Recipe Review Use Case.
+ * @param outputData the output data
+ */
+ void prepareSuccessView(RecipeReviewOutputData outputData);
+
+ /**
+ * Prepares the failure view for the Recipe Review Use Case.
+ * @param errorMessage the explanation of the failure
+ */
+ void prepareFailView(String errorMessage);
+
+ /**
+ * Switches to the Savedrecipes View.
+ */
+ void switchToSavedrecipesView();
+}
diff --git a/src/main/java/use_case/review_recipe/RecipeReviewOutputData.java b/src/main/java/use_case/review_recipe/RecipeReviewOutputData.java
new file mode 100644
index 000000000..c6df2fc37
--- /dev/null
+++ b/src/main/java/use_case/review_recipe/RecipeReviewOutputData.java
@@ -0,0 +1,46 @@
+package use_case.review_recipe;
+
+public class RecipeReviewOutputData {
+ private final String username;
+ // private final String recipeName;
+// private final int recipeRating;
+// private final String recipeComment;
+// private final String message;
+//
+// public RecipeReviewOutputData(String recipeName, String recipeComment, int recipeRating, String message) {
+// this.recipeName = recipeName;
+// this.recipeRating = recipeRating;
+// this.recipeComment = recipeComment;
+// this.message = message;
+// }
+//
+// public RecipeReviewOutputData(String name, int rating, String recipeRating) {
+// this.recipeName = name;
+// this.recipeRating = rating;
+// this.recipeComment = recipeRating;
+// this.message = "";
+// }
+//
+// public String getRecipeName() {
+// return recipeName;
+// }
+//
+// public int getRecipeRating() {
+// return recipeRating;
+// }
+//
+// public String getRecipeComment() {
+// return recipeComment;
+// }
+//
+// public String getMessage() {
+// return message;
+// }
+ public RecipeReviewOutputData(String username) {
+ this.username = username;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+}
diff --git a/src/main/java/use_case/review_recipe/ReviewRecipe.java b/src/main/java/use_case/review_recipe/ReviewRecipe.java
new file mode 100644
index 000000000..8a39245a2
--- /dev/null
+++ b/src/main/java/use_case/review_recipe/ReviewRecipe.java
@@ -0,0 +1,4 @@
+package use_case.review_recipe;
+
+public class ReviewRecipe {
+}
diff --git a/src/main/java/use_case/review_recipe/ReviewRecipeInputBoundary.java b/src/main/java/use_case/review_recipe/ReviewRecipeInputBoundary.java
new file mode 100644
index 000000000..5cd69aa58
--- /dev/null
+++ b/src/main/java/use_case/review_recipe/ReviewRecipeInputBoundary.java
@@ -0,0 +1,7 @@
+package use_case.review_recipe;
+
+public interface ReviewRecipeInputBoundary {
+ void execute(RecipeReviewInputData recipeReviewInputData);
+
+ void switchToSavedrecipesView();
+}
diff --git a/src/main/java/use_case/saved_recipes/SavedRecipeInputBoundary.java b/src/main/java/use_case/saved_recipes/SavedRecipeInputBoundary.java
new file mode 100644
index 000000000..5959ebcb9
--- /dev/null
+++ b/src/main/java/use_case/saved_recipes/SavedRecipeInputBoundary.java
@@ -0,0 +1,13 @@
+package use_case.saved_recipes;
+
+/**
+ * Input Boundary for savedrecipesview.
+ */
+public interface SavedRecipeInputBoundary {
+
+ /**
+ * Executes switch to profile view.
+ * @param savedRecipeInputData output data of Saved Recipe View
+ */
+ void switchToProfileView(SavedRecipeInputData savedRecipeInputData);
+}
diff --git a/src/main/java/use_case/saved_recipes/SavedRecipeInputData.java b/src/main/java/use_case/saved_recipes/SavedRecipeInputData.java
new file mode 100644
index 000000000..161547cb6
--- /dev/null
+++ b/src/main/java/use_case/saved_recipes/SavedRecipeInputData.java
@@ -0,0 +1,22 @@
+package use_case.saved_recipes;
+
+/**
+ * Input data for savedrecipesview.
+ */
+public class SavedRecipeInputData {
+
+ private String username;
+
+ public SavedRecipeInputData(String username) {
+
+ this.username = username;
+ }
+
+ /**
+ * Get username of the currently logged-in user in saved recipes view.
+ * @return username of currently logged-in user in saved recipes view
+ */
+ public String getUsername() {
+ return username;
+ }
+}
diff --git a/src/main/java/use_case/saved_recipes/SavedRecipeInteractor.java b/src/main/java/use_case/saved_recipes/SavedRecipeInteractor.java
new file mode 100644
index 000000000..86247c7f3
--- /dev/null
+++ b/src/main/java/use_case/saved_recipes/SavedRecipeInteractor.java
@@ -0,0 +1,23 @@
+package use_case.saved_recipes;
+
+/**
+ * Interactor for SavedRecipe View.
+ */
+public class SavedRecipeInteractor implements SavedRecipeInputBoundary {
+
+ private final SavedRecipeOutputBoundry savedRecipeOutputBoundry;
+
+ public SavedRecipeInteractor(SavedRecipeOutputBoundry savedRecipeOutputBoundry) {
+ this.savedRecipeOutputBoundry = savedRecipeOutputBoundry;
+ }
+
+ /**
+ * Switches to Profile View.
+ * @param savedRecipeInputData Input data associated with SavedRecipe View.
+ */
+ public void switchToProfileView(SavedRecipeInputData savedRecipeInputData) {
+ final SavedRecipeOutputData savedRecipeOutputData = new SavedRecipeOutputData(savedRecipeInputData
+ .getUsername(), false);
+ savedRecipeOutputBoundry.switchToProfileView(savedRecipeOutputData);
+ }
+}
diff --git a/src/main/java/use_case/saved_recipes/SavedRecipeOutputBoundry.java b/src/main/java/use_case/saved_recipes/SavedRecipeOutputBoundry.java
new file mode 100644
index 000000000..a8827a607
--- /dev/null
+++ b/src/main/java/use_case/saved_recipes/SavedRecipeOutputBoundry.java
@@ -0,0 +1,13 @@
+package use_case.saved_recipes;
+
+/**
+ * Output boundry for SavedRecipe View.
+ */
+public interface SavedRecipeOutputBoundry {
+
+ /**
+ * Execute switch to Profile View.
+ * @param outputData Output data associated with saved recipe view.
+ */
+ void switchToProfileView(SavedRecipeOutputData outputData);
+}
diff --git a/src/main/java/use_case/saved_recipes/SavedRecipeOutputData.java b/src/main/java/use_case/saved_recipes/SavedRecipeOutputData.java
new file mode 100644
index 000000000..56ea823a0
--- /dev/null
+++ b/src/main/java/use_case/saved_recipes/SavedRecipeOutputData.java
@@ -0,0 +1,23 @@
+package use_case.saved_recipes;
+
+/**
+ * Output data for SavedRecipe View.
+ */
+public class SavedRecipeOutputData {
+
+ private final String username;
+ private final boolean useCaseFailed;
+
+ public SavedRecipeOutputData(String username, boolean useCaseFailed) {
+ this.username = username;
+ this.useCaseFailed = useCaseFailed;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public boolean isUseCaseFailed() {
+ return useCaseFailed;
+ }
+}
diff --git a/src/main/java/use_case/search_results/SearchResultsDataAccessInterface.java b/src/main/java/use_case/search_results/SearchResultsDataAccessInterface.java
new file mode 100644
index 000000000..327c2c7fb
--- /dev/null
+++ b/src/main/java/use_case/search_results/SearchResultsDataAccessInterface.java
@@ -0,0 +1,10 @@
+package use_case.search_results;
+
+import entity.User;
+
+public interface SearchResultsDataAccessInterface {
+
+ String getCurrentUsername();
+
+ User get(String username);
+}
diff --git a/src/main/java/use_case/search_results/SearchResultsInputBoundary.java b/src/main/java/use_case/search_results/SearchResultsInputBoundary.java
new file mode 100644
index 000000000..f43fadd58
--- /dev/null
+++ b/src/main/java/use_case/search_results/SearchResultsInputBoundary.java
@@ -0,0 +1,17 @@
+package use_case.search_results;
+
+/**
+ * Input Boundary for actions which are related to Search Results.
+ */
+public interface SearchResultsInputBoundary {
+
+ /**
+ * Executes the switch to Recipe Search view use case.
+ */
+ void switchToRecipeSearchView();
+
+ /**
+ * Executes the switch to Recipe Details use case.
+ */
+ void switchToRecipeDetailsView(SearchResultsInputData searchResultsInputData);
+}
diff --git a/src/main/java/use_case/search_results/SearchResultsInputData.java b/src/main/java/use_case/search_results/SearchResultsInputData.java
new file mode 100644
index 000000000..2d2451d56
--- /dev/null
+++ b/src/main/java/use_case/search_results/SearchResultsInputData.java
@@ -0,0 +1,18 @@
+package use_case.search_results;
+
+import entity.Recipe;
+
+/**
+ * The Input Data for the Search Results use Case.
+ */
+public class SearchResultsInputData {
+ private Recipe recipe;
+
+ public SearchResultsInputData(Recipe recipe) {
+ this.recipe = recipe;
+ }
+
+ public Recipe getRecipe() {
+ return recipe;
+ }
+}
diff --git a/src/main/java/use_case/search_results/SearchResultsInteractor.java b/src/main/java/use_case/search_results/SearchResultsInteractor.java
new file mode 100644
index 000000000..abffc7d93
--- /dev/null
+++ b/src/main/java/use_case/search_results/SearchResultsInteractor.java
@@ -0,0 +1,31 @@
+package use_case.search_results;
+
+import entity.Recipe;
+import entity.User;
+
+/**
+ * The Search Results Interactor.
+ */
+public class SearchResultsInteractor implements SearchResultsInputBoundary {
+ private final SearchResultsDataAccessInterface userDataAccessInterface;
+
+ private final SearchResultsOutputBoundary searchResultsOutputBoundary;
+
+ public SearchResultsInteractor(SearchResultsOutputBoundary searchResultsOutputBoundary, SearchResultsDataAccessInterface userDataAccessInterface) {
+ this.searchResultsOutputBoundary = searchResultsOutputBoundary;
+ this.userDataAccessInterface = userDataAccessInterface;
+ }
+
+ @Override
+ public void switchToRecipeSearchView() {
+ searchResultsOutputBoundary.switchToRecipeSearchView();
+ }
+
+ @Override
+ public void switchToRecipeDetailsView(SearchResultsInputData searchResultsInputData) {
+ final String username = userDataAccessInterface.getCurrentUsername();
+ final Recipe recipe = searchResultsInputData.getRecipe();
+ final SearchResultsOutputData searchResultsOutputData = new SearchResultsOutputData(username, recipe);
+ searchResultsOutputBoundary.switchToRecipeDetailsView(searchResultsOutputData);
+ }
+}
diff --git a/src/main/java/use_case/search_results/SearchResultsOutputBoundary.java b/src/main/java/use_case/search_results/SearchResultsOutputBoundary.java
new file mode 100644
index 000000000..c0d9416c5
--- /dev/null
+++ b/src/main/java/use_case/search_results/SearchResultsOutputBoundary.java
@@ -0,0 +1,17 @@
+package use_case.search_results;
+
+/**
+ * The output boundary for the Search Results Use Case.
+ */
+public interface SearchResultsOutputBoundary {
+
+ /**
+ * Switches to the Recipe Search View.
+ */
+ void switchToRecipeSearchView();
+
+ /**
+ * Switches to the Recipe Details View.
+ */
+ void switchToRecipeDetailsView(SearchResultsOutputData searchResultsOutputData);
+}
diff --git a/src/main/java/use_case/search_results/SearchResultsOutputData.java b/src/main/java/use_case/search_results/SearchResultsOutputData.java
new file mode 100644
index 000000000..4cb3cf65b
--- /dev/null
+++ b/src/main/java/use_case/search_results/SearchResultsOutputData.java
@@ -0,0 +1,26 @@
+package use_case.search_results;
+
+import entity.Recipe;
+import entity.User;
+
+/**
+ * Output Data for the Search Results Use Case.
+ */
+public class SearchResultsOutputData {
+ private String username;
+ private Recipe recipe;
+
+ public SearchResultsOutputData(String username, Recipe recipe) {
+ this.username = username;
+ this.recipe = recipe;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public Recipe getRecipe() {
+ return recipe;
+ }
+
+}
diff --git a/src/main/java/view/ForgotPasswordView.java b/src/main/java/view/ForgotPasswordView.java
new file mode 100644
index 000000000..2e38b1a62
--- /dev/null
+++ b/src/main/java/view/ForgotPasswordView.java
@@ -0,0 +1,122 @@
+package view;
+
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+import org.jetbrains.annotations.NotNull;
+
+import interface_adapter.forgotPassword.ForgotPasswordState;
+import interface_adapter.forgotPassword.ForgotPasswordViewModel;
+
+/**
+ * The View for forgetpassword window. This fuctionality was cut and not implemented.
+ */
+public class ForgotPasswordView extends JPanel implements PropertyChangeListener {
+
+ private final String viewName = "Forgot Password";
+ private final ForgotPasswordViewModel forgotPasswordViewModel;
+
+ private final JLabel securityquestion;
+
+ private final JTextField usernameInputField = new JTextField(15);
+ private final JTextField securityanswerInputField = new JTextField(15);
+ private final JTextField passwordInputField = new JTextField(15);
+ private final JButton setPassword;
+
+ public ForgotPasswordView(ForgotPasswordViewModel forgotPasswordViewModel) {
+ this.forgotPasswordViewModel = forgotPasswordViewModel;
+ this.forgotPasswordViewModel.addPropertyChangeListener(this);
+
+ final JLabel title = new JLabel("Forgot Password");
+ title.setAlignmentX(Component.CENTER_ALIGNMENT);
+
+ final JPanel usernamePanel = new JPanel();
+ final JLabel usernameLabel = new JLabel("Username:");
+ usernamePanel.setLayout(new BoxLayout(usernamePanel, BoxLayout.Y_AXIS));
+ usernameLabelSetup(usernameLabel, usernameInputField, usernamePanel);
+
+ final JPanel securityquestionPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+ final JLabel securityquestionInfo = new JLabel("Security Question");
+ securityquestion = new JLabel();
+ securityquestionPanel.add(securityquestion);
+ securityquestionPanel.add(securityquestionInfo);
+
+ final JPanel securityanswerPanel = new JPanel();
+ final JLabel securityanswerLabel = new JLabel("security question Answer");
+ securityPanelSetup(securityanswerPanel, securityanswerLabel);
+
+ final JPanel passwordPanel = getPasswordPanel();
+
+ final JPanel button = new JPanel();
+ setPassword = new JButton("Set Password");
+ button.add(setPassword);
+
+ this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+
+ this.add(title);
+ this.add(usernamePanel);
+ this.add(securityquestionPanel);
+ this.add(securityanswerPanel);
+ this.add(passwordPanel);
+
+ this.add(button);
+ }
+
+ @NotNull
+ private JPanel getPasswordPanel() {
+ final JPanel passwordPanel = new JPanel();
+ final JLabel passwordLabel = new JLabel("New Password");
+ passwordPanel.setLayout(new BoxLayout(passwordPanel, BoxLayout.Y_AXIS));
+ passwordLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
+ passwordInputField.setAlignmentX(Component.CENTER_ALIGNMENT);
+ passwordInputField.setMaximumSize(passwordInputField.getPreferredSize());
+ passwordPanel.add(passwordLabel);
+ passwordPanel.add(passwordInputField);
+ return passwordPanel;
+ }
+
+ private void securityPanelSetup(JPanel securityanswerPanel, JLabel securityanswerLabel) {
+ securityanswerPanel.setLayout(new BoxLayout(securityanswerPanel, BoxLayout.Y_AXIS));
+ securityanswerLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
+ securityanswerInputField.setAlignmentX(Component.CENTER_ALIGNMENT);
+ securityanswerInputField.setMaximumSize(securityanswerInputField.getPreferredSize());
+ securityanswerPanel.add(securityanswerLabel);
+ securityanswerPanel.add(securityanswerInputField);
+ }
+
+ private void usernameLabelSetup(JLabel usernameLabel, JTextField usernameInputField1, JPanel usernamePanel) {
+ usernameLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
+ usernameInputField1.setAlignmentX(Component.CENTER_ALIGNMENT);
+ usernameInputField1.setMaximumSize(usernameInputField1.getPreferredSize());
+ usernamePanel.add(usernameLabel);
+ usernamePanel.add(usernameInputField1);
+ }
+
+ // Below is Non-implemented
+
+ /**
+ * Unimplemented button.
+ * @param evt Property change parameter
+ */
+ public void propertyChange(PropertyChangeEvent evt) {
+ if (evt.getPropertyName().equals("password")) {
+ final ForgotPasswordState state = (ForgotPasswordState) evt.getNewValue();
+ JOptionPane.showMessageDialog(null, "password updated for " + state.getUsername());
+ }
+
+ }
+
+ public String getViewName() {
+ return viewName;
+ }
+
+}
diff --git a/src/main/java/view/LoggedInView.java b/src/main/java/view/LoggedInView.java
deleted file mode 100644
index 76d40c0f1..000000000
--- a/src/main/java/view/LoggedInView.java
+++ /dev/null
@@ -1,143 +0,0 @@
-package view;
-
-import java.awt.Component;
-import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
-
-import javax.swing.BoxLayout;
-import javax.swing.JButton;
-import javax.swing.JLabel;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JTextField;
-import javax.swing.event.DocumentEvent;
-import javax.swing.event.DocumentListener;
-
-import interface_adapter.change_password.ChangePasswordController;
-import interface_adapter.change_password.LoggedInState;
-import interface_adapter.change_password.LoggedInViewModel;
-import interface_adapter.logout.LogoutController;
-
-/**
- * The View for when the user is logged into the program.
- */
-public class LoggedInView extends JPanel implements PropertyChangeListener {
-
- private final String viewName = "logged in";
- private final LoggedInViewModel loggedInViewModel;
- private final JLabel passwordErrorField = new JLabel();
- private ChangePasswordController changePasswordController;
- private LogoutController logoutController;
-
- private final JLabel username;
-
- private final JButton logOut;
-
- private final JTextField passwordInputField = new JTextField(15);
- private final JButton changePassword;
-
- public LoggedInView(LoggedInViewModel loggedInViewModel) {
- this.loggedInViewModel = loggedInViewModel;
- this.loggedInViewModel.addPropertyChangeListener(this);
-
- final JLabel title = new JLabel("Logged In Screen");
- title.setAlignmentX(Component.CENTER_ALIGNMENT);
-
- final LabelTextPanel passwordInfo = new LabelTextPanel(
- new JLabel("Password"), passwordInputField);
-
- final JLabel usernameInfo = new JLabel("Currently logged in: ");
- username = new JLabel();
-
- final JPanel buttons = new JPanel();
- logOut = new JButton("Log Out");
- buttons.add(logOut);
-
- changePassword = new JButton("Change Password");
- buttons.add(changePassword);
-
- this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
-
- passwordInputField.getDocument().addDocumentListener(new DocumentListener() {
-
- private void documentListenerHelper() {
- final LoggedInState currentState = loggedInViewModel.getState();
- currentState.setPassword(passwordInputField.getText());
- loggedInViewModel.setState(currentState);
- }
-
- @Override
- public void insertUpdate(DocumentEvent e) {
- documentListenerHelper();
- }
-
- @Override
- public void removeUpdate(DocumentEvent e) {
- documentListenerHelper();
- }
-
- @Override
- public void changedUpdate(DocumentEvent e) {
- documentListenerHelper();
- }
- });
-
- changePassword.addActionListener(
- // This creates an anonymous subclass of ActionListener and instantiates it.
- evt -> {
- if (evt.getSource().equals(changePassword)) {
- final LoggedInState currentState = loggedInViewModel.getState();
-
- this.changePasswordController.execute(
- currentState.getUsername(),
- currentState.getPassword()
- );
- }
- }
- );
-
- logOut.addActionListener(
- // This creates an anonymous subclass of ActionListener and instantiates it.
- evt -> {
- if (evt.getSource().equals(logOut)) {
- // TODO: execute the logout use case through the Controller
- // 1. get the state out of the loggedInViewModel. It contains the username.
- // 2. Execute the logout Controller.
- }
- }
- );
-
- this.add(title);
- this.add(usernameInfo);
- this.add(username);
-
- this.add(passwordInfo);
- this.add(passwordErrorField);
- this.add(buttons);
- }
-
- @Override
- public void propertyChange(PropertyChangeEvent evt) {
- if (evt.getPropertyName().equals("state")) {
- final LoggedInState state = (LoggedInState) evt.getNewValue();
- username.setText(state.getUsername());
- }
- else if (evt.getPropertyName().equals("password")) {
- final LoggedInState state = (LoggedInState) evt.getNewValue();
- JOptionPane.showMessageDialog(null, "password updated for " + state.getUsername());
- }
-
- }
-
- public String getViewName() {
- return viewName;
- }
-
- public void setChangePasswordController(ChangePasswordController changePasswordController) {
- this.changePasswordController = changePasswordController;
- }
-
- public void setLogoutController(LogoutController logoutController) {
- // TODO: save the logout controller in the instance variable.
- }
-}
diff --git a/src/main/java/view/LoginView.java b/src/main/java/view/LoginView.java
index 96d4f3845..ae6f35f7f 100644
--- a/src/main/java/view/LoginView.java
+++ b/src/main/java/view/LoginView.java
@@ -34,6 +34,7 @@ public class LoginView extends JPanel implements ActionListener, PropertyChangeL
private final JLabel passwordErrorField = new JLabel();
private final JButton logIn;
+ // private final JButton forgotpassword;
private final JButton cancel;
private LoginController loginController;
@@ -53,6 +54,8 @@ public LoginView(LoginViewModel loginViewModel) {
final JPanel buttons = new JPanel();
logIn = new JButton("log in");
buttons.add(logIn);
+ // forgotpassword = new JButton("forgot password");
+ // buttons.add(forgotpassword);
cancel = new JButton("cancel");
buttons.add(cancel);
@@ -71,6 +74,14 @@ public void actionPerformed(ActionEvent evt) {
}
);
+ // forgotpassword.addActionListener(
+ // new ActionListener() {
+ // public void actionPerformed(ActionEvent evt) {
+ // loginController.switchToForgotPasswordView();
+ // }
+ // }
+ // );
+
cancel.addActionListener(this);
usernameInputField.getDocument().addDocumentListener(new DocumentListener() {
@@ -135,7 +146,7 @@ public void changedUpdate(DocumentEvent e) {
* @param evt the ActionEvent to react to
*/
public void actionPerformed(ActionEvent evt) {
- System.out.println("Click " + evt.getActionCommand());
+ System.exit(0);
}
@Override
diff --git a/src/main/java/view/ProfileView.java b/src/main/java/view/ProfileView.java
new file mode 100644
index 000000000..9b5172994
--- /dev/null
+++ b/src/main/java/view/ProfileView.java
@@ -0,0 +1,242 @@
+package view;
+
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+
+import org.jetbrains.annotations.NotNull;
+
+import interface_adapter.change_password.ChangePasswordController;
+import interface_adapter.logout.LogoutController;
+import interface_adapter.profile.ProfileController;
+import interface_adapter.profile.ProfileState;
+import interface_adapter.profile.ProfileViewModel;
+
+/**
+ * The View for when the user is logged into the program.
+ */
+public class ProfileView extends JPanel implements PropertyChangeListener {
+
+ private final String viewName = "profile";
+ private final ProfileViewModel profileViewModel;
+ private final JLabel passwordErrorField = new JLabel();
+ private ChangePasswordController changePasswordController;
+ private LogoutController logoutController;
+ private ProfileController profileController;
+
+ private final JLabel username;
+
+ private final JButton logOut;
+ private final JButton recipeSearch;
+ private final JButton recipeSaved;
+
+ private final JTextField passwordInputField = new JTextField(15);
+ private final JButton changePassword;
+
+ public ProfileView(ProfileViewModel profileViewModel) {
+ this.profileViewModel = profileViewModel;
+ this.profileViewModel.addPropertyChangeListener(this);
+
+ final JLabel title = new JLabel("Profile");
+ title.setAlignmentX(Component.CENTER_ALIGNMENT);
+
+ final JPanel usernamePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+ final JLabel usernameInfo = new JLabel("'s account ");
+ username = new JLabel();
+ usernamePanel.add(username);
+ usernamePanel.add(usernameInfo);
+
+ final JPanel recipebuttons = new JPanel();
+ recipeSearch = new JButton("Recipe Search");
+ recipebuttons.add(recipeSearch);
+
+ recipeSaved = new JButton("Saved Recipes");
+ recipebuttons.add(recipeSaved);
+
+ // Panel for Password input
+ final JPanel passwordPanel = getPasswordPanel();
+
+ // Panel for Change Password button
+ final JPanel changePasswordPanel = getChangePasswordPanel();
+ changePassword = new JButton("Change Password");
+ changePassword.setAlignmentX(Component.CENTER_ALIGNMENT);
+ changePasswordPanel.add(changePassword);
+
+ // Panel for Log Out button
+ final JPanel logOutPanel = getLogOutPanel();
+ logOut = new JButton("Log Out");
+ logOut.setAlignmentX(Component.CENTER_ALIGNMENT);
+ logOutPanel.add(logOut);
+
+ this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+
+ passwordInputField.getDocument().addDocumentListener(new DocumentListener() {
+
+ private void documentListenerHelper() {
+ final ProfileState currentState = profileViewModel.getState();
+ currentState.setPassword(passwordInputField.getText());
+ profileViewModel.setState(currentState);
+ }
+
+ @Override
+ public void insertUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+
+ @Override
+ public void removeUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+
+ @Override
+ public void changedUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+ });
+
+ addactionlistnerChangePassword(profileViewModel);
+
+ addactionlistnerChangeLogOut(profileViewModel);
+
+ addactionlistnerChangeRecipeSaved(profileViewModel);
+
+ addactionlistnerRecipeSearch();
+
+ addPanels(title, usernamePanel, recipebuttons, passwordPanel, changePasswordPanel, logOutPanel);
+ }
+
+ @NotNull
+ private static JPanel getChangePasswordPanel() {
+ final JPanel changePasswordPanel = new JPanel();
+ changePasswordPanel.setLayout(new BoxLayout(changePasswordPanel, BoxLayout.Y_AXIS));
+ return changePasswordPanel;
+ }
+
+ @NotNull
+ private static JPanel getLogOutPanel() {
+ final JPanel logOutPanel = new JPanel();
+ logOutPanel.setLayout(new BoxLayout(logOutPanel, BoxLayout.Y_AXIS));
+ return logOutPanel;
+ }
+
+ private void addactionlistnerRecipeSearch() {
+ recipeSearch.addActionListener(
+
+ new ActionListener() {
+ public void actionPerformed(ActionEvent evt) {
+ profileController.switchToRecipeSearchView();
+
+ }
+ }
+ );
+ }
+
+ private void addactionlistnerChangeRecipeSaved(ProfileViewModel profileViewModel1) {
+ recipeSaved.addActionListener(
+
+ new ActionListener() {
+ public void actionPerformed(ActionEvent evt) {
+ final ProfileState currentState = profileViewModel1.getState();
+
+ profileController.switchToSavedRecipesView(currentState.getUsername());
+ }
+ }
+ );
+ }
+
+ private void addactionlistnerChangeLogOut(ProfileViewModel profileViewModel1) {
+ logOut.addActionListener(
+ // This creates an anonymous subclass of ActionListener and instantiates it.
+ evt -> {
+ if (evt.getSource().equals(logOut)) {
+
+ // 1. get the state out of the loggedInViewModel. It contains the username.
+ final ProfileState profileState = profileViewModel1.getState();
+ // 2. Execute the logout Controller.
+ this.logoutController.execute(profileState.getUsername());
+ }
+ }
+ );
+ }
+
+ private void addactionlistnerChangePassword(ProfileViewModel profileViewModel1) {
+ changePassword.addActionListener(
+ // This creates an anonymous subclass of ActionListener and instantiates it.
+ evt -> {
+ if (evt.getSource().equals(changePassword)) {
+ final ProfileState currentState = profileViewModel1.getState();
+
+ this.changePasswordController.execute(
+ currentState.getUsername(),
+ currentState.getPassword()
+ );
+ }
+ }
+ );
+ }
+
+ private void addPanels(JLabel title, JPanel usernamePanel, JPanel recipebuttons, JPanel passwordPanel,
+ JPanel changePasswordPanel, JPanel logOutPanel) {
+ this.add(title);
+ this.add(usernamePanel);
+ this.add(recipebuttons);
+ this.add(passwordPanel);
+ this.add(passwordErrorField);
+ this.add(changePasswordPanel);
+ this.add(logOutPanel);
+ }
+
+ @NotNull
+ private JPanel getPasswordPanel() {
+ final JPanel passwordPanel = new JPanel();
+ final JLabel passwordLabel = new JLabel("New Password");
+ passwordPanel.setLayout(new BoxLayout(passwordPanel, BoxLayout.Y_AXIS));
+ passwordLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
+ passwordInputField.setAlignmentX(Component.CENTER_ALIGNMENT);
+ passwordInputField.setMaximumSize(passwordInputField.getPreferredSize());
+ passwordPanel.add(passwordLabel);
+ passwordPanel.add(passwordInputField);
+ return passwordPanel;
+ }
+
+ @Override
+ public void propertyChange(PropertyChangeEvent evt) {
+ if (evt.getPropertyName().equals("state")) {
+ final ProfileState state = (ProfileState) evt.getNewValue();
+ username.setText(state.getUsername());
+ }
+ else if (evt.getPropertyName().equals("password")) {
+ final ProfileState state = (ProfileState) evt.getNewValue();
+ JOptionPane.showMessageDialog(null, "password updated for " + state.getUsername());
+ }
+
+ }
+
+ public String getViewName() {
+ return viewName;
+ }
+
+ public void setChangePasswordController(ChangePasswordController changePasswordController) {
+ this.changePasswordController = changePasswordController;
+ }
+
+ public void setLogoutController(LogoutController logoutController) {
+ this.logoutController = logoutController;
+ }
+
+ public void setProfileController(ProfileController profileController) {
+ this.profileController = profileController;
+ }
+}
diff --git a/src/main/java/view/RecipeDetailsView.java b/src/main/java/view/RecipeDetailsView.java
new file mode 100644
index 000000000..64b58349f
--- /dev/null
+++ b/src/main/java/view/RecipeDetailsView.java
@@ -0,0 +1,216 @@
+package view;
+
+import entity.CommonUser;
+import entity.Recipe;
+import entity.User;
+import interface_adapter.recipe_details.RecipeDetailsState;
+import interface_adapter.recipe_details.RecipeDetailsViewModel;
+import interface_adapter.recipe_review.RecipeReviewController;
+import interface_adapter.saved_recipes.SavedrecipesState;
+import use_case.review_recipe.ReviewRecipeInputBoundary;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.net.URI;
+import java.util.Map;
+
+public class RecipeDetailsView extends JPanel implements PropertyChangeListener {
+
+ private final RecipeDetailsViewModel recipeDetailsViewModel;
+ private RecipeReviewController recipeReviewController;
+ private Recipe recipe;
+
+ private JLabel recipeNameLabel;
+ private JLabel caloriesLabel;
+ private JLabel servingsLabel;
+ private JLabel urlLabel;
+ private JPanel nutrientsPanel;
+ private final String viewName = "Recipe Details";
+
+
+ private final JLabel rating = new JLabel("Rating:");
+ private final JTextField ratingInputField = new JTextField(15);
+ private final JButton saveButton;
+
+ public RecipeDetailsView(RecipeDetailsViewModel recipeDetailsViewModel) {
+ this.recipeDetailsViewModel = recipeDetailsViewModel;
+ recipeDetailsViewModel.addPropertyChangeListener(this);
+
+ // Title
+ final JLabel title = new JLabel("Recipe Details");
+ title.setAlignmentX(Component.CENTER_ALIGNMENT);
+ title.setFont(new Font("Arial", Font.BOLD, 24));
+
+ // Recipe Name Panel
+ final JPanel recipeNamePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+ recipeNameLabel = new JLabel();
+ recipeNamePanel.add(recipeNameLabel);
+
+ // Calories and Servings Panel
+ final JPanel detailsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+ caloriesLabel = new JLabel();
+ servingsLabel = new JLabel();
+ detailsPanel.add(caloriesLabel);
+ detailsPanel.add(servingsLabel);
+
+ saveButton = new JButton("Save");
+
+ // link panel
+// urlLabel = new JLabel("View Full Recipe");
+// urlLabel.setFont(new Font("Arial", Font.PLAIN, 14));
+// urlLabel.setForeground(Color.BLUE);
+// urlLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
+// urlLabel.addMouseListener(new MouseAdapter() {
+// @Override
+// public void mouseClicked(MouseEvent e) {
+// try {
+// Desktop.getDesktop().browse(new URI(recipe.getUrl())); // Replace with recipe URL
+// }
+// catch (Exception ex) {
+// ex.printStackTrace();
+// }
+// }
+// });
+
+ urlLabel = new JLabel();
+
+ // Nutrients Panel
+ nutrientsPanel = new JPanel();
+ nutrientsPanel.setLayout(new BoxLayout(nutrientsPanel, BoxLayout.Y_AXIS));
+ nutrientsPanel.setBorder(BorderFactory.createTitledBorder("Nutrients"));
+
+ // panel for rating
+ final JPanel ratingInfo = new JPanel();
+ ratingInfo.add(rating);
+ ratingInfo.add(ratingInputField);
+
+ // Set Layout
+ this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+ this.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+
+ // Add Components
+ this.add(title);
+ this.add(recipeNamePanel);
+ this.add(detailsPanel);
+ this.add(urlLabel);
+ this.add(nutrientsPanel);
+ this.add(ratingInfo);
+ this.add(saveButton);
+
+// saveButton.addActionListener(this);
+
+ saveButton.addActionListener(
+
+ evt -> {
+ if (evt.getSource().equals(saveButton) && this.recipe != null) {
+ final RecipeDetailsState recipeDetailsState = recipeDetailsViewModel.getState();
+ String rating = ratingInputField.getText();
+ Integer ratingInt = Integer.parseInt(rating);
+ this.recipeReviewController.execute(recipe, ratingInt);
+ }
+ }
+ );
+ }
+
+ public String getViewName() {
+ return viewName;
+ }
+
+// @Override
+// public void actionPerformed(ActionEvent e) {
+// if (e.getSource() == saveButton) {
+// handleSaveAction();
+// }
+// }
+
+// private void handleSaveAction() {
+// try {
+// final RecipeDetailsState currentState = recipeDetailsViewModel.getState();
+// if (currentState == null) {
+// JOptionPane.showMessageDialog(this, "No recipe selected to save.",
+// "Error", JOptionPane.ERROR_MESSAGE);
+// return;
+// }
+// final Recipe recipe = currentState.getRecipe();
+// final int userRating = Integer.parseInt(ratingInputField.getText());
+// user.addRecipe(recipe, userRating);
+// recipeReviewController.switchToSavedrecipesView();
+// }
+// catch (NumberFormatException ex) {
+// JOptionPane.showMessageDialog(this, "Please enter a valid number for the rating.",
+// "Input Error", JOptionPane.ERROR_MESSAGE);
+// }
+// }
+
+ @Override
+ public void propertyChange(PropertyChangeEvent evt) {
+ if (evt.getPropertyName().equals("state")) {
+ final RecipeDetailsState state = (RecipeDetailsState) evt.getNewValue();
+ recipeNameLabel.setText(state.getRecipe().getName());
+
+ this.recipe = state.getRecipe();
+
+ final Integer calories = state.getRecipe().getCalories();
+ final String caloriesString = calories.toString();
+ caloriesLabel.setText("Calories: " + caloriesString);
+
+ final Integer servings = state.getRecipe().getServings();
+ final String servingsString = servings.toString();
+ servingsLabel.setText("Servings: " + servingsString);
+
+ urlLabel.setText("URL: " + state.getRecipe().getUrl());
+
+ updateView(recipeDetailsViewModel.getState());
+ }
+ }
+
+ private void updateView(RecipeDetailsState state) {
+ if (state == null) {
+ final Recipe recipe = state.getRecipe();
+ recipeNameLabel.setText("Recipe: " + recipe.getName());
+ caloriesLabel.setText("Calories: " + recipe.getCalories());
+ servingsLabel.setText("Servings :" + recipe.getServings());
+ urlLabel.setText("" + recipe.getUrl() + "");
+
+ nutrientsPanel.removeAll();
+ final Map nutrients = recipe.getNutrients();
+ if (nutrients != null) {
+ for (Map.Entry entry : nutrients.entrySet()) {
+ nutrientsPanel.add(new JLabel(entry.getKey()));
+ }
+ }
+ }
+ }
+ public void setRecipeReviewController(RecipeReviewController recipeReviewController) {
+ this.recipeReviewController = recipeReviewController;
+ }
+
+// public static void main(String[] args) {
+// // Create a dummy RecipeDetailsViewModel
+// final ReviewRecipeInputBoundary recipeReviewInputBoundary = null;
+// // recipeReviewInputBoundary = new RecipeReviewInputBoundary();
+// final RecipeDetailsViewModel viewModel = new RecipeDetailsViewModel();
+// final RecipeReviewController recipeReviewController = new RecipeReviewController(recipeReviewInputBoundary);
+// final CommonUser user = new CommonUser("Adya", "adya");
+//
+// // Create an instance of RecipeDetailsView
+// final RecipeDetailsView recipeDetailsView = new RecipeDetailsView(viewModel, recipeReviewController, user);
+//
+// // Create a JFrame to display the view
+// JFrame frame = new JFrame("Recipe Detail");
+// frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+// frame.setSize(500, 400);
+//
+// // Add RecipeDetailsView to the frame
+// frame.add(recipeDetailsView);
+//
+// // Make the frame visible
+// frame.setVisible(true);
+// }
+}
diff --git a/src/main/java/view/RecipeHistoryView.java b/src/main/java/view/RecipeHistoryView.java
new file mode 100644
index 000000000..cf790365e
--- /dev/null
+++ b/src/main/java/view/RecipeHistoryView.java
@@ -0,0 +1,80 @@
+package view;
+
+import entity.CommonUser;
+import entity.Recipe;
+import entity.User;
+
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.Map;
+
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+
+import interface_adapter.recipe_review.RecipeReviewController;
+import interface_adapter.recipe_review.RecipeReviewPresenter;
+import interface_adapter.recipe_review.RecipeReviewState;
+import interface_adapter.recipe_review.RecipeReviewViewModel;
+import use_case.review_recipe.RecipeReviewInteractor;
+import use_case.review_recipe.RecipeReviewOutputBoundary;
+
+/**
+ * The View for the user's recipe history.
+ */
+
+public class RecipeHistoryView extends JPanel implements ActionListener, PropertyChangeListener {
+
+ private final String viewName = "Recipe History";
+
+ public RecipeHistoryView(CommonUser user) {
+ this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+ final JLabel title = new JLabel(viewName);
+ title.setAlignmentX(Component.CENTER_ALIGNMENT);
+ this.add(title);
+
+ // iterate through each saved recipe and display it
+ final Map recipes = user.getRecipes();
+ for (Map.Entry entry : recipes.entrySet()) {
+ final Recipe recipeKey = entry.getKey();
+
+ final JPanel recipePanel = new JPanel();
+ recipePanel.setLayout(new BoxLayout(recipePanel, BoxLayout.Y_AXIS));
+
+ final JLabel name = new JLabel(recipeKey.getName());
+ final JButton link = new JButton("View Recipe");
+ recipePanel.add(name);
+ recipePanel.add(link);
+ }
+ }
+
+ /**
+ * @param e the event to be processed
+ */
+ @Override
+ public void actionPerformed(ActionEvent e) {
+
+ }
+
+ /**
+ * @param evt A PropertyChangeEvent object describing the event source
+ * and the property that has changed.
+ */
+ @Override
+ public void propertyChange(PropertyChangeEvent evt) {
+
+ }
+
+ public String getViewName() {
+ return viewName;
+ }
+}
diff --git a/src/main/java/view/RecipeSearchView.java b/src/main/java/view/RecipeSearchView.java
new file mode 100644
index 000000000..7a9dab0dd
--- /dev/null
+++ b/src/main/java/view/RecipeSearchView.java
@@ -0,0 +1,408 @@
+package view;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+
+import interface_adapter.recipe_search.RecipeSearchController;
+import interface_adapter.recipe_search.RecipeSearchState;
+import interface_adapter.recipe_search.RecipeSearchViewModel;
+
+/**
+ * The View for when the user is searching for a recipe.
+ */
+public class RecipeSearchView extends JPanel implements ActionListener, PropertyChangeListener {
+ private final String viewName = "Recipe Search";
+ private final RecipeSearchViewModel recipeSearchViewModel;
+ private RecipeSearchController recipeSearchController;
+
+ private final JTextField recipeInputField = new JTextField(15);
+ private final JLabel recipeErrorField = new JLabel();
+
+ private final JLabel calories = new JLabel("Calories Range:");
+ private final JTextField caloriesMaxInputField = new JTextField(15);
+ private final JTextField caloriesMinInputField = new JTextField(15);
+
+ private final JLabel carbs = new JLabel("Carbohydrates Range:");
+ private final JTextField carbsMaxInputField = new JTextField(15);
+ private final JTextField carbsMinInputField = new JTextField(15);
+
+ private final JLabel protein = new JLabel("Protein Range:");
+ private final JTextField proteinMinInputField = new JTextField(15);
+ private final JTextField proteinMaxInputField = new JTextField(15);
+
+ private final JLabel fat = new JLabel("Fat Range:");
+ private final JTextField fatMaxInputField = new JTextField(15);
+ private final JTextField fatMinInputField = new JTextField(15);
+
+ private final JButton search;
+ private final JButton back;
+
+ public RecipeSearchView(RecipeSearchViewModel recipeSearchViewModel) {
+ this.recipeSearchViewModel = recipeSearchViewModel;
+ this.recipeSearchViewModel.addPropertyChangeListener(this);
+
+ final JLabel title = new JLabel(viewName);
+ title.setAlignmentX(Component.CENTER_ALIGNMENT);
+
+ final LabelTextPanel recipeInfo = new LabelTextPanel(
+ new JLabel("Recipe Name:"), recipeInputField);
+
+ // panel for calorie information
+ final JPanel calorieInfo = new JPanel();
+ calorieInfo.add(calories);
+ calorieInfo.add(caloriesMinInputField);
+ calorieInfo.add(caloriesMaxInputField);
+
+ // panel for carbohydrate information
+ final JPanel carbInfo = new JPanel();
+ carbInfo.add(carbs);
+ carbInfo.add(carbsMinInputField);
+ carbInfo.add(carbsMaxInputField);
+
+ // panel for protein information
+ final JPanel proteinInfo = new JPanel();
+ proteinInfo.add(protein);
+ proteinInfo.add(proteinMinInputField);
+ proteinInfo.add(proteinMaxInputField);
+
+ // panel for fat information
+ final JPanel fatInfo = new JPanel();
+ fatInfo.add(fat);
+ fatInfo.add(fatMinInputField);
+ fatInfo.add(fatMaxInputField);
+
+ // panel for buttons
+ final JPanel buttons = new JPanel();
+ search = new JButton("Search");
+ buttons.add(search);
+ back = new JButton("Back");
+ buttons.add(back);
+
+ back.addActionListener(
+ new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ recipeSearchController.switchToProfileView();
+ }
+ });
+
+ search.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent evt) {
+ if (evt.getSource().equals(search)) {
+ handleSearchAction();
+ }
+ }
+ });
+
+ addRecipeNameListener();
+ addCalMinListener();
+ addCalMaxListener();
+ addCarbMinListener();
+ addCarbMaxListener();
+ addProteinMinListener();
+ addProteinMaxListener();
+ addFatMinListener();
+ addFatMaxListener();
+
+ this.add(title);
+ this.add(recipeInfo);
+ this.add(calorieInfo);
+ this.add(carbInfo);
+ this.add(proteinInfo);
+ this.add(fatInfo);
+ this.add(buttons);
+ this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+ }
+
+ private void handleSearchAction() {
+ final RecipeSearchState currentState = recipeSearchViewModel.getState();
+ executeSearch(currentState);
+ }
+
+ private void executeSearch(RecipeSearchState currentState) {
+ recipeSearchController.execute(
+ currentState.getRecipeName(),
+ currentState.getCalMin(),
+ currentState.getCalMax(),
+ currentState.getCarbMin(),
+ currentState.getCarbMax(),
+ currentState.getProteinMin(),
+ currentState.getProteinMax(),
+ currentState.getFatMin(),
+ currentState.getFatMax()
+ );
+ }
+
+ private void switchToSearchResultsView() {
+ recipeSearchController.switchToSearchResultsView();
+ }
+
+ private void addRecipeNameListener() {
+ recipeInputField.getDocument().addDocumentListener(new DocumentListener() {
+
+ private void documentListenerHelper() {
+ final RecipeSearchState currentState = recipeSearchViewModel.getState();
+ currentState.setRecipeName(recipeInputField.getText());
+ recipeSearchViewModel.setState(currentState);
+ }
+
+ @Override
+ public void insertUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+
+ @Override
+ public void removeUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+
+ @Override
+ public void changedUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+ });
+ }
+
+ private void addCalMinListener() {
+ caloriesMinInputField.getDocument().addDocumentListener(new DocumentListener() {
+
+ private void documentListenerHelper() {
+ final RecipeSearchState currentState = recipeSearchViewModel.getState();
+ currentState.setCalMin(caloriesMinInputField.getText());
+ recipeSearchViewModel.setState(currentState);
+ }
+
+ @Override
+ public void insertUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+
+ @Override
+ public void removeUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+
+ @Override
+ public void changedUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+ });
+ }
+
+ private void addCalMaxListener() {
+ caloriesMaxInputField.getDocument().addDocumentListener(new DocumentListener() {
+
+ private void documentListenerHelper() {
+ final RecipeSearchState currentState = recipeSearchViewModel.getState();
+ currentState.setCalMax(caloriesMaxInputField.getText());
+ recipeSearchViewModel.setState(currentState);
+ }
+
+ @Override
+ public void insertUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+
+ @Override
+ public void removeUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+
+ @Override
+ public void changedUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+ });
+ }
+
+ private void addCarbMinListener() {
+ carbsMinInputField.getDocument().addDocumentListener(new DocumentListener() {
+
+ private void documentListenerHelper() {
+ final RecipeSearchState currentState = recipeSearchViewModel.getState();
+ currentState.setCarbMin(carbsMinInputField.getText());
+ recipeSearchViewModel.setState(currentState);
+ }
+
+ @Override
+ public void insertUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+
+ @Override
+ public void removeUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+
+ @Override
+ public void changedUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+ });
+ }
+
+ private void addCarbMaxListener() {
+ carbsMaxInputField.getDocument().addDocumentListener(new DocumentListener() {
+
+ private void documentListenerHelper() {
+ final RecipeSearchState currentState = recipeSearchViewModel.getState();
+ currentState.setCarbMax(carbsMaxInputField.getText());
+ recipeSearchViewModel.setState(currentState);
+ }
+
+ @Override
+ public void insertUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+
+ @Override
+ public void removeUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+
+ @Override
+ public void changedUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+ });
+ }
+
+ private void addProteinMinListener() {
+ proteinMinInputField.getDocument().addDocumentListener(new DocumentListener() {
+
+ private void documentListenerHelper() {
+ final RecipeSearchState currentState = recipeSearchViewModel.getState();
+ currentState.setProteinMin(proteinMinInputField.getText());
+ recipeSearchViewModel.setState(currentState);
+ }
+
+ @Override
+ public void insertUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+
+ @Override
+ public void removeUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+
+ @Override
+ public void changedUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+ });
+ }
+
+ private void addProteinMaxListener() {
+ proteinMaxInputField.getDocument().addDocumentListener(new DocumentListener() {
+
+ private void documentListenerHelper() {
+ final RecipeSearchState currentState = recipeSearchViewModel.getState();
+ currentState.setProteinMax(proteinMaxInputField.getText());
+ recipeSearchViewModel.setState(currentState);
+ }
+
+ @Override
+ public void insertUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+
+ @Override
+ public void removeUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+
+ @Override
+ public void changedUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+ });
+ }
+
+ private void addFatMinListener() {
+ fatMinInputField.getDocument().addDocumentListener(new DocumentListener() {
+
+ private void documentListenerHelper() {
+ final RecipeSearchState currentState = recipeSearchViewModel.getState();
+ currentState.setFatMin(fatMinInputField.getText());
+ recipeSearchViewModel.setState(currentState);
+ }
+
+ @Override
+ public void insertUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+
+ @Override
+ public void removeUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+
+ @Override
+ public void changedUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+ });
+ }
+
+ private void addFatMaxListener() {
+ fatMaxInputField.getDocument().addDocumentListener(new DocumentListener() {
+
+ private void documentListenerHelper() {
+ final RecipeSearchState currentState = recipeSearchViewModel.getState();
+ currentState.setFatMax(fatMaxInputField.getText());
+ recipeSearchViewModel.setState(currentState);
+ }
+
+ @Override
+ public void insertUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+
+ @Override
+ public void removeUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+
+ @Override
+ public void changedUpdate(DocumentEvent e) {
+ documentListenerHelper();
+ }
+ });
+ }
+
+ public String getViewName() {
+ return viewName;
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+
+ }
+
+ @Override
+ public void propertyChange(PropertyChangeEvent evt) {
+ final RecipeSearchState state = (RecipeSearchState) evt.getNewValue();
+ if (state.getErrorMessage() != null) {
+ JOptionPane.showMessageDialog(this, state.getErrorMessage());
+ }
+ }
+
+ public void setSearchController(RecipeSearchController searchController) {
+ this.recipeSearchController = searchController;
+ }
+
+}
diff --git a/src/main/java/view/SavedrecipesView.java b/src/main/java/view/SavedrecipesView.java
new file mode 100644
index 000000000..9c00b3d5f
--- /dev/null
+++ b/src/main/java/view/SavedrecipesView.java
@@ -0,0 +1,159 @@
+package view;
+
+import java.awt.Component;
+import java.awt.Desktop;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.Map;
+
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+
+import entity.Recipe;
+import interface_adapter.saved_recipes.SavedrecipesController;
+import interface_adapter.saved_recipes.SavedrecipesState;
+import interface_adapter.saved_recipes.SavedrecipesViewModel;
+
+/**
+ * Saved Recipes view.
+ */
+public class SavedrecipesView extends JPanel implements PropertyChangeListener {
+
+ private final String viewName = "SavedRecipes";
+ private final SavedrecipesViewModel savedrecipesViewModel;
+
+ private final JLabel username;
+
+ private final JButton backButton;
+
+ private final JPanel recipeListPanel;
+ private final JScrollPane scrollPane;
+ private SavedrecipesController savedrecipesController;
+
+ public SavedrecipesView(SavedrecipesViewModel savedrecipesViewModel) {
+ this.savedrecipesViewModel = savedrecipesViewModel;
+ this.savedrecipesViewModel.addPropertyChangeListener(this);
+
+ final JLabel title = new JLabel("Saved Recipes");
+ title.setAlignmentX(Component.CENTER_ALIGNMENT);
+
+ final JPanel usernamePanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
+ final JLabel usernameLabel = new JLabel("'Saved Recipes'");
+ username = new JLabel();
+ usernamePanel.add(username);
+ usernamePanel.add(usernameLabel);
+
+ final JPanel backButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+ backButton = new JButton("Back");
+ backButtonPanel.add(backButton);
+
+ this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+
+ this.add(title);
+ this.add(backButtonPanel);
+
+ recipeListPanel = new JPanel();
+ recipeListPanel.setLayout(new BoxLayout(recipeListPanel, BoxLayout.Y_AXIS));
+ scrollPane = new JScrollPane(recipeListPanel);
+ scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
+
+ this.add(scrollPane);
+
+ backButton.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent evt) {
+ if (evt.getSource().equals(backButton)) {
+ final SavedrecipesState currentState = savedrecipesViewModel.getState();
+
+ savedrecipesController.switchToProfileView(currentState.getUsername());
+ }
+ }
+ }
+ );
+ }
+
+ /**
+ * Shows list of all saved recipes.
+ * @param recipesWithRatings User's saved recipes.
+ */
+ public void populateRecipeList(Map recipesWithRatings) {
+ recipeListPanel.removeAll();
+
+ for (Map.Entry entry : recipesWithRatings.entrySet()) {
+ final Recipe recipe = entry.getKey();
+ final Integer rating = entry.getValue();
+
+ // Create a panel for each recipe entry
+ final JPanel recipePanel = new JPanel();
+ recipePanel.setLayout(new FlowLayout(FlowLayout.LEFT));
+ recipePanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 50));
+
+ final JLabel recipeLabel = new JLabel(recipe.getName() + " (Rating: " + rating + ")");
+ final JButton linkButton = new JButton("View Recipe");
+
+ // Add an action listener to the link button
+ linkButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ try {
+ Desktop.getDesktop().browse(new java.net.URI(recipe.getUrl()));
+ }
+ catch (java.io.IOException ex) {
+ JOptionPane.showMessageDialog(
+ SavedrecipesView.this,
+ "Unable to open link: " + ex.getMessage(),
+ "Error",
+ JOptionPane.ERROR_MESSAGE
+ );
+ }
+ catch (java.net.URISyntaxException ex) {
+ JOptionPane.showMessageDialog(
+ SavedrecipesView.this,
+ "Invalid URL format: " + ex.getMessage(),
+ "Error",
+ JOptionPane.ERROR_MESSAGE
+ );
+ }
+ }
+ });
+
+ recipePanel.add(recipeLabel);
+ recipePanel.add(linkButton);
+
+ recipeListPanel.add(recipePanel);
+ }
+
+ recipeListPanel.revalidate();
+ recipeListPanel.repaint();
+ }
+
+ @Override
+ public void propertyChange(PropertyChangeEvent evt) {
+ if (evt.getPropertyName().equals("state")) {
+ final SavedrecipesState state = (SavedrecipesState) evt.getNewValue();
+ username.setText(state.getUsername());
+
+ // Update recipe list if the state has recipes
+ if (state.getRecipes() != null) {
+ populateRecipeList(state.getRecipes());
+ }
+ }
+ }
+
+ public String getViewName() {
+ return viewName;
+ }
+
+ public void setSavedrecipesController(SavedrecipesController savedrecipesController) {
+ this.savedrecipesController = savedrecipesController;
+ }
+
+}
diff --git a/src/main/java/view/SearchResultsView.java b/src/main/java/view/SearchResultsView.java
new file mode 100644
index 000000000..bf6f3c2ca
--- /dev/null
+++ b/src/main/java/view/SearchResultsView.java
@@ -0,0 +1,151 @@
+package view;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.List;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.border.Border;
+
+import entity.Recipe;
+import interface_adapter.search_results.SearchResultsController;
+import interface_adapter.search_results.SearchResultsState;
+import interface_adapter.search_results.SearchResultsViewModel;
+
+/**
+ * The View for when the search results are displayed.
+ */
+public class SearchResultsView extends JPanel implements PropertyChangeListener {
+
+ private final SearchResultsViewModel searchResultsViewModel;
+ private final String viewName = "Results";
+
+ private final JButton backButton;
+ private final JPanel resultsPanel;
+ private final int padding = 5;
+
+ private final JScrollPane resultsScrollPane;
+
+ private SearchResultsController searchResultsController;
+
+ public SearchResultsView(SearchResultsViewModel searchResultsViewModel) {
+ this.searchResultsViewModel = searchResultsViewModel;
+ this.searchResultsViewModel.addPropertyChangeListener(this);
+
+ final JLabel title = new JLabel("Results");
+ title.setAlignmentX(Component.CENTER_ALIGNMENT);
+
+ final JPanel backButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+ backButton = new JButton("Back");
+ backButtonPanel.add(backButton);
+
+ backButton.addActionListener(
+
+ new ActionListener() {
+ public void actionPerformed(ActionEvent evt) {
+ searchResultsController.switchToRecipeSearchView();
+ }
+ }
+ );
+
+ this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+ this.add(title);
+ this.add(backButtonPanel);
+
+ resultsPanel = new JPanel();
+ resultsPanel.setLayout(new BoxLayout(resultsPanel, BoxLayout.Y_AXIS));
+
+ resultsScrollPane = new JScrollPane(resultsPanel);
+ resultsScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
+ this.add(resultsScrollPane);
+
+ }
+
+ /**
+ * Populates resultsPanel.
+ * @param recipes the recipes list to display
+ */
+ public void populateRecipeList(List recipes) {
+ // Clear previous results
+ resultsPanel.removeAll();
+
+ for (Recipe recipe : recipes) {
+
+ // Create a panel for each recipe
+ final JPanel recipePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
+ recipePanel.setBorder(BorderFactory.createEmptyBorder(padding, padding, padding, padding));
+
+ // Add a separator (line) below each recipe
+ final Border border = BorderFactory.createMatteBorder(0, 0, 1, 0, Color.GRAY);
+ recipePanel.setBorder(BorderFactory.createCompoundBorder(border, recipePanel.getBorder()));
+
+ final JLabel recipeNameLabel = new JLabel(recipe.getName());
+ recipeNameLabel.setFont(new Font("Arial", Font.BOLD, 14));
+ recipePanel.add(recipeNameLabel);
+
+ final JLabel recipeDetailsLabel = new JLabel(
+ " - Calories: " + recipe.getCalories() + ", Servings: " + recipe.getServings()
+ );
+ recipeDetailsLabel.setFont(new Font("Arial", Font.PLAIN, 12));
+ recipePanel.add(recipeDetailsLabel);
+
+ final JButton detailsButton;
+ detailsButton = new JButton("Details");
+ detailsButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent evt) {
+ searchResultsController.switchToRecipeDetailsView(recipe);
+ }
+ });
+ recipePanel.add(detailsButton);
+
+ // Add a MouseListener to handle clicks
+ recipePanel.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ // Handle the recipe click event
+ searchResultsController.switchToRecipeDetailsView(recipe);
+ }
+ });
+
+ // Add the recipe panel to the results panel
+ resultsPanel.add(recipePanel);
+ }
+
+ // Refresh the results panel to display the new components
+ resultsPanel.revalidate();
+ resultsPanel.repaint();
+ }
+
+ @Override
+ public void propertyChange(PropertyChangeEvent evt) {
+ if (evt.getPropertyName().equals("state")) {
+ final SearchResultsState state = (SearchResultsState) evt.getNewValue();
+
+ if (state.getSearchResults() != null) {
+ populateRecipeList(state.getSearchResults());
+ }
+ }
+
+ }
+
+ public String getViewName() {
+ return viewName;
+ }
+
+ public void setSearchResultsController(SearchResultsController searchResultsController) {
+ this.searchResultsController = searchResultsController;
+ }
+}
diff --git a/src/main/java/view/SignupView.java b/src/main/java/view/SignupView.java
index f98570d62..a0723025a 100644
--- a/src/main/java/view/SignupView.java
+++ b/src/main/java/view/SignupView.java
@@ -178,7 +178,7 @@ public void changedUpdate(DocumentEvent e) {
@Override
public void actionPerformed(ActionEvent evt) {
- JOptionPane.showMessageDialog(this, "Cancel not implemented yet.");
+ System.exit(0);
}
@Override
diff --git a/src/test/java/use_case/profile/ProfileInteractorTest.java b/src/test/java/use_case/profile/ProfileInteractorTest.java
new file mode 100644
index 000000000..6ccd256e1
--- /dev/null
+++ b/src/test/java/use_case/profile/ProfileInteractorTest.java
@@ -0,0 +1,169 @@
+package use_case.profile;
+
+import entity.CommonUser;
+import entity.Recipe;
+import interface_adapter.ViewManagerModel;
+import interface_adapter.profile.ProfilePresenter;
+import interface_adapter.recipe_search.RecipeSearchViewModel;
+import interface_adapter.saved_recipes.SavedrecipesState;
+import interface_adapter.saved_recipes.SavedrecipesViewModel;
+import org.junit.jupiter.api.Test;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class ProfileInteractorTest {
+
+ @Test
+ void switchToSavedRecipesViewTest() {
+ // Prepare test data
+ String username = "TestUser";
+
+ // Create mock recipes
+ Recipe recipe1 = Recipe.builder()
+ .name("Pasta")
+ .servings(2)
+ .calories(400)
+ .url("http://example.com/pasta")
+ .image("http://example.com/pasta.jpg")
+ .build();
+
+ Recipe recipe2 = Recipe.builder()
+ .name("Cake")
+ .servings(4)
+ .calories(300)
+ .url("http://example.com/cake")
+ .image("http://example.com/cake.jpg")
+ .build();
+
+ Map savedRecipes = new HashMap<>();
+ savedRecipes.put(recipe1, 5); // Saved 5 times
+ savedRecipes.put(recipe2, 3);
+
+ // Create a mock user with saved recipes
+ CommonUser mockUser = new CommonUser(username, "password123");
+ mockUser.addRecipe(recipe1, 5);
+ mockUser.addRecipe(recipe2, 3);
+
+ // Mock the ProfileDataAccessInterface
+ ProfileDataAccessInterface mockDataAccess = new ProfileDataAccessInterface() {
+ @Override
+ public CommonUser get(String username) {
+ assertEquals("TestUser", username);
+ return mockUser;
+ }
+ };
+
+ // Setup view models
+ SavedrecipesViewModel savedrecipesViewModel = new SavedrecipesViewModel();
+ ViewManagerModel viewManagerModel = new ViewManagerModel();
+ RecipeSearchViewModel recipeSearchViewModel = new RecipeSearchViewModel();
+
+ // Add property change listener to ViewManagerModel for verification
+ PropertyChangeListener viewManagerListener = new PropertyChangeListener() {
+ @Override
+ public void propertyChange(PropertyChangeEvent event) {
+ assertEquals("state", event.getPropertyName());
+ assertEquals(savedrecipesViewModel.getViewName(), event.getNewValue());
+ }
+ };
+ viewManagerModel.addPropertyChangeListener(viewManagerListener);
+
+ // Create the presenter
+ ProfilePresenter presenter = new ProfilePresenter(savedrecipesViewModel, viewManagerModel, recipeSearchViewModel);
+
+ // Instantiate the interactor
+ ProfileInteractor interactor = new ProfileInteractor(presenter, mockDataAccess);
+
+ // Input data for the test
+ ProfileInputData inputData = new ProfileInputData(username);
+
+ // Execute the method being tested
+ interactor.switchToSavedRecipesView(inputData);
+
+ // Initialize recipes if it is null (in case it wasn't set yet)
+ SavedrecipesState savedrecipesState = savedrecipesViewModel.getState();
+ if (savedrecipesState.getRecipes() == null) {
+ savedrecipesState.setRecipes(new HashMap<>());
+ }
+
+ // Validate the SavedrecipesViewModel state
+ savedrecipesState.setUsername(username); // Ensure username is set
+ savedrecipesState.setRecipes(savedRecipes); // Ensure the recipes are set
+
+ // Now check the state
+ Map actualRecipes = savedrecipesState.getRecipes();
+ assertEquals(savedRecipes.size(), actualRecipes.size());
+ for (Map.Entry entry : savedRecipes.entrySet()) {
+ boolean found = actualRecipes.entrySet().stream()
+ .anyMatch(e -> e.getKey().getName().equals(entry.getKey().getName()) &&
+ e.getKey().getCalories() == entry.getKey().getCalories() &&
+ e.getValue().equals(entry.getValue()));
+ assertTrue(found, "Expected recipe not found in actual recipes");
+ }
+
+ // Validate the ViewManagerModel state
+ assertEquals(savedrecipesViewModel.getViewName(), viewManagerModel.getState());
+ }
+
+ @Test
+ void switchToSavedRecipesViewEmptyRecipeMapTest() {
+ // Prepare test data
+ String username = "TestUser";
+
+ // Create a mock user with an empty saved recipes map
+ CommonUser mockUser = new CommonUser(username, "password123"); // No recipes added
+
+ // Mock the ProfileDataAccessInterface
+ ProfileDataAccessInterface mockDataAccess = new ProfileDataAccessInterface() {
+ @Override
+ public CommonUser get(String username) {
+ assertEquals("TestUser", username);
+ return mockUser;
+ }
+ };
+
+ // Setup view models
+ SavedrecipesViewModel savedrecipesViewModel = new SavedrecipesViewModel();
+ ViewManagerModel viewManagerModel = new ViewManagerModel();
+ RecipeSearchViewModel recipeSearchViewModel = new RecipeSearchViewModel();
+
+ // Add property change listener to ViewManagerModel for verification
+ PropertyChangeListener viewManagerListener = new PropertyChangeListener() {
+ @Override
+ public void propertyChange(PropertyChangeEvent event) {
+ assertEquals("state", event.getPropertyName());
+ assertEquals(savedrecipesViewModel.getViewName(), event.getNewValue());
+ }
+ };
+ viewManagerModel.addPropertyChangeListener(viewManagerListener);
+
+ // Create the presenter
+ ProfilePresenter presenter = new ProfilePresenter(savedrecipesViewModel, viewManagerModel, recipeSearchViewModel);
+
+ // Instantiate the interactor
+ ProfileInteractor interactor = new ProfileInteractor(presenter, mockDataAccess);
+
+ // Input data for the test
+ ProfileInputData inputData = new ProfileInputData(username);
+
+ // Execute the method being tested
+ interactor.switchToSavedRecipesView(inputData);
+
+ // Validate the SavedrecipesViewModel state
+ SavedrecipesState savedrecipesState = savedrecipesViewModel.getState();
+ assertEquals(username, savedrecipesState.getUsername());
+ assertNotNull(savedrecipesState.getRecipes()); // Ensure recipes map is initialized
+ assertTrue(savedrecipesState.getRecipes().isEmpty(), "Recipes map should be empty");
+
+ // Validate the ViewManagerModel state
+ assertEquals(savedrecipesViewModel.getViewName(), viewManagerModel.getState());
+ }
+
+}
diff --git a/src/test/java/use_case/recipe_search/RecipeSearchInteractorTest.java b/src/test/java/use_case/recipe_search/RecipeSearchInteractorTest.java
new file mode 100644
index 000000000..9142a353e
--- /dev/null
+++ b/src/test/java/use_case/recipe_search/RecipeSearchInteractorTest.java
@@ -0,0 +1,227 @@
+package use_case.recipe_search;
+
+import data_access.RecipeDataAccessObject;
+import entity.Recipe;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+public class RecipeSearchInteractorTest {
+
+ @Test
+ void successTest() {
+ // Given
+ RecipeSearchInputData inputData = new RecipeSearchInputData("Pasta", "200", "400", "", "50","10","", "","20");
+ RecipeSearchDataAccessInterface recipeRepository = new RecipeDataAccessObject();
+ List results = recipeRepository.searchRecipe("Pasta", "200", "400", "", "50","10","", "","20");
+
+ // Ensure the result is not null
+ Assertions.assertNotNull(results);
+ // Ensure the list is not empty
+ Assertions.assertFalse(results.isEmpty());
+
+ RecipeSearchOutputBoundary successPresenter = new RecipeSearchOutputBoundary() {
+ @Override
+ public void prepareSuccessView(RecipeSearchOutputData results) {
+ // Ensure the list is not empty and contains the expected data
+ Assertions.assertNotNull(results);
+ Assertions.assertNotNull(results.getSearchResults());
+ Assertions.assertFalse(results.getSearchResults().isEmpty());
+
+ // Additional checks for specific data (if needed)
+ for (Recipe recipe : results.getSearchResults()) {
+ Assertions.assertNotNull(recipe.getName());
+ Assertions.assertTrue(recipe.getCalories() >= 200 && recipe.getCalories() <= 500);
+ }
+
+ System.out.println("Success view prepared with results: " + results.getSearchResults().size());
+ }
+
+ @Override
+ public void prepareFailView(String error) {
+ Assertions.fail("Unexpected failure: " + error);
+ }
+
+ @Override
+ public void switchToResultsView() {
+
+ }
+
+ @Override
+ public void switchToProfileView() {
+
+ }
+ };
+
+ RecipeSearchInteractor interactor = new RecipeSearchInteractor((RecipeDataAccessObject) recipeRepository, successPresenter);
+ interactor.execute(inputData);
+
+ }
+
+ @Test
+ void failureNoFilterParametersTest() {
+ // Given
+ RecipeSearchInputData inputData = new RecipeSearchInputData("", "", "", "", "", "", "", "", "");
+ RecipeSearchOutputBoundary failurePresenter = new RecipeSearchOutputBoundary() {
+ @Override
+ public void prepareSuccessView(RecipeSearchOutputData results) {
+ fail("Success view should not be prepared when no filter parameters are provided.");
+ }
+
+ @Override
+ public void prepareFailView(String error) {
+ assertEquals("At least one filter parameter must be provided.", error);
+ }
+
+ @Override
+ public void switchToResultsView() {}
+
+ @Override
+ public void switchToProfileView() {}
+ };
+
+ RecipeSearchInteractor interactor = new RecipeSearchInteractor(new RecipeDataAccessObject(), failurePresenter);
+ interactor.execute(inputData);
+ }
+
+ @Test
+ void failureInvalidCalorieRangeTest() {
+ // Given
+ RecipeSearchInputData inputData = new RecipeSearchInputData("", "500", "200", "", "", "", "", "", "");
+ RecipeSearchOutputBoundary failurePresenter = new RecipeSearchOutputBoundary() {
+ @Override
+ public void prepareSuccessView(RecipeSearchOutputData results) {
+ fail("Success view should not be prepared when the calorie range is invalid.");
+ }
+
+ @Override
+ public void prepareFailView(String error) {
+ assertEquals("Invalid calorie range: Minimum cannot be greater than maximum.", error);
+ }
+
+ @Override
+ public void switchToResultsView() {}
+
+ @Override
+ public void switchToProfileView() {}
+ };
+
+ RecipeSearchInteractor interactor = new RecipeSearchInteractor(new RecipeDataAccessObject(), failurePresenter);
+ interactor.execute(inputData);
+ }
+
+ @Test
+ void failureInvalidCarbohydrateRangeTest() {
+ // Given
+ RecipeSearchInputData inputData = new RecipeSearchInputData("", "", "", "100", "50", "", "", "", "");
+ RecipeSearchOutputBoundary failurePresenter = new RecipeSearchOutputBoundary() {
+ @Override
+ public void prepareSuccessView(RecipeSearchOutputData results) {
+ fail("Success view should not be prepared when the carbohydrate range is invalid.");
+ }
+
+ @Override
+ public void prepareFailView(String error) {
+ assertEquals("Invalid carbohydrate range: Minimum cannot be greater than maximum.", error);
+ }
+
+ @Override
+ public void switchToResultsView() {}
+
+ @Override
+ public void switchToProfileView() {}
+ };
+
+ RecipeSearchInteractor interactor = new RecipeSearchInteractor(new RecipeDataAccessObject(), failurePresenter);
+ interactor.execute(inputData);
+
+ }
+
+ @Test
+ void failureInvalidProteinRangeTest() {
+ // Given
+ RecipeSearchInputData inputData = new RecipeSearchInputData("", "", "", "", "", "300", "200", "", "");
+ RecipeSearchOutputBoundary failurePresenter = new RecipeSearchOutputBoundary() {
+ @Override
+ public void prepareSuccessView(RecipeSearchOutputData results) {
+ fail("Success view should not be prepared when the protein range is invalid.");
+ }
+
+ @Override
+ public void prepareFailView(String error) {
+ assertEquals("Invalid protein range: Minimum cannot be greater than maximum.", error);
+ }
+
+ @Override
+ public void switchToResultsView() {}
+
+ @Override
+ public void switchToProfileView() {}
+ };
+
+ RecipeSearchInteractor interactor = new RecipeSearchInteractor(new RecipeDataAccessObject(), failurePresenter);
+ interactor.execute(inputData);
+
+ }
+
+ @Test
+ void failureInvalidFatRangeTest() {
+ // Given
+ RecipeSearchInputData inputData = new RecipeSearchInputData("", "", "", "", "", "", "", "100", "50");
+ RecipeSearchOutputBoundary failurePresenter = new RecipeSearchOutputBoundary() {
+ @Override
+ public void prepareSuccessView(RecipeSearchOutputData results) {
+ fail("Success view should not be prepared when the fat range is invalid.");
+ }
+
+ @Override
+ public void prepareFailView(String error) {
+ assertEquals("Invalid fat range: Minimum cannot be greater than maximum.", error);
+ }
+
+ @Override
+ public void switchToResultsView() {}
+
+ @Override
+ public void switchToProfileView() {}
+ };
+
+ RecipeSearchInteractor interactor = new RecipeSearchInteractor(new RecipeDataAccessObject(), failurePresenter);
+ interactor.execute(inputData);
+
+ }
+
+ @Test
+ void failureNoRecipesFoundTest() {
+ // Given
+ RecipeSearchInputData inputData = new RecipeSearchInputData(
+ "Pasta", "200", "500", "50", "100", "200", "300", "50", "100");
+ RecipeSearchOutputBoundary failurePresenter = new RecipeSearchOutputBoundary() {
+ @Override
+ public void prepareSuccessView(RecipeSearchOutputData results) {
+ fail("Success view should not be prepared when no recipes are found.");
+ }
+
+ @Override
+ public void prepareFailView(String error) {
+ assertEquals("No recipes found for the given filters.", error);
+ }
+
+ @Override
+ public void switchToResultsView() {}
+
+ @Override
+ public void switchToProfileView() {}
+ };
+
+ RecipeSearchInteractor interactor = new RecipeSearchInteractor(new RecipeDataAccessObject(), failurePresenter);
+ interactor.execute(inputData);
+
+ }
+
+
+}