Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/main/java/frontend/HelloFX.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class HelloFX extends Application {
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloFX.class.getResource("main-view.fxml"));
Parent root = fxmlLoader.load();
Scene scene = new Scene(root, 640, 480);
Scene scene = new Scene(root, 680, 500);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
stage.setTitle("Restaurangbokningar");
stage.setScene(scene);
Expand Down
15 changes: 6 additions & 9 deletions src/main/java/frontend/controller/BookingController.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import backend.entities.Booking;
import backend.entities.Restaurant;
import backend.services.BookingService;
import frontend.model.RestaurantHandler;
import javafx.fxml.FXML;
import javafx.scene.control.*;

Expand All @@ -12,9 +13,7 @@
public class BookingController {

private final BookingService bookingService = new BookingService();
private Restaurant restaurant;

@FXML private Label restaurantNameLabel;
private Restaurant restaurant = RestaurantHandler.getCurrentRestaurant();

@FXML private TextField firstNameField;
@FXML private TextField lastNameField;
Expand All @@ -28,12 +27,6 @@ public class BookingController {

@FXML private Label statusLabel;

public void setRestaurant(Restaurant restaurant) {
this.restaurant = restaurant;
restaurantNameLabel.setText(restaurant.getName());
}


@FXML
private void initialize() {

Expand Down Expand Up @@ -82,4 +75,8 @@ private void BookTable() {
statusLabel.setText(e.getMessage());
}
}

// public void setRestaurant(Restaurant restaurant) {
// this.restaurant = restaurant;
// }
}
4 changes: 3 additions & 1 deletion src/main/java/frontend/controller/MainController.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public class MainController {
private TextField searchRestaurantField;
@FXML
private FlowPane restaurantContainer;
@FXML
private VBox mainArea;

@FXML
private void initialize() {
Expand All @@ -43,7 +45,7 @@ public void handleRestaurantSearch(ActionEvent event){
private void displayRestaurants(List<Restaurant> restaurants){
restaurantContainer.getChildren().clear();
for(Restaurant r : restaurants){
RestaurantCard restaurantCard = new RestaurantCard(r, restaurantContainer);
RestaurantCard restaurantCard = new RestaurantCard(r, mainArea);
restaurantContainer.getChildren().add(restaurantCard);
}
}
Expand Down
43 changes: 38 additions & 5 deletions src/main/java/frontend/controller/RestaurantController.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,36 @@
package frontend.controller;


import backend.entities.Restaurant;
import frontend.model.ImageHandler;
import frontend.model.RestaurantHandler;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;

import javafx.scene.control.Label;

public class RestaurantController {
@FXML
private Pane restaurantRoot;

private Pane restaurantPageRoot;
@FXML
private Label restaurantName;
@FXML
private Label restaurantAdress;
@FXML
private Label adressIcon;
@FXML
private Label categoryIcon;
@FXML
private Label restaurantCategory;
@FXML
private Label meanPriceIcon;
@FXML
private Label restaurantMeanPrice;
@FXML
private Label ratingIcon;
@FXML
private Label restaurantRating;
@FXML
private ImageView restaurantImage;

private Restaurant restaurant;

Expand All @@ -24,6 +41,22 @@ private void initialize() {
if (restaurant != null) {
restaurantName.setText(restaurant.getName());
}

setRestaurantData(restaurant);
}

private void setRestaurantData(Restaurant restaurant){
restaurantName.setText(restaurant.getName());
adressIcon.setText("📍");
restaurantAdress.setText(restaurant.getAddress());
categoryIcon.setText("🍴");
restaurantCategory.setText(restaurant.getCategory());
meanPriceIcon.setText("$");
restaurantMeanPrice.setText(restaurant.getMeanPrice().toString() + " kr");
ratingIcon.setText("⭐ ");
restaurantRating.setText(String.valueOf(restaurant.getRating()));
restaurantImage.setImage(ImageHandler.getRestaurantImage(restaurant));
ImageHandler.scaleAndCropImage(restaurantImage, 350, 250);
}
Comment on lines +48 to 60

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Potential NullPointerException if image loading fails.

Same issue as in RestaurantCard: ImageHandler.getRestaurantImage() can return null, which will cause an NPE in scaleAndCropImage().

🛠️ Proposed fix
-        restaurantImage.setImage(ImageHandler.getRestaurantImage(restaurant));
-        ImageHandler.scaleAndCropImage(restaurantImage, 250, 350);
+        Image image = ImageHandler.getRestaurantImage(restaurant);
+        if (image != null) {
+            restaurantImage.setImage(image);
+            ImageHandler.scaleAndCropImage(restaurantImage, 250, 350);
+        }

Note: You'll need to add import javafx.scene.image.Image; if not already imported via the wildcard.

🤖 Prompt for AI Agents
In `@src/main/java/frontend/controller/RestaurantController.java` around lines 48
- 60, setRestaurantData may pass a null Image from
ImageHandler.getRestaurantImage into ImageHandler.scaleAndCropImage causing an
NPE; update setRestaurantData to capture the result of
ImageHandler.getRestaurantImage(restaurant) into a local Image variable (or
check restaurantImage.getImage()), check for null and if null either set a
fallback placeholder Image or skip calling ImageHandler.scaleAndCropImage, then
call restaurantImage.setImage(...) only with a non-null Image; ensure
javafx.scene.image.Image is imported if not already.


}
22 changes: 21 additions & 1 deletion src/main/java/frontend/model/ImageHandler.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
package frontend.model;

import backend.entities.Restaurant;
import frontend.view.RestaurantCard;
import javafx.geometry.Rectangle2D;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.shape.Rectangle;

import java.util.Objects;

public class ImageHandler {
public static void scaleAndCropImage(ImageView imageView, double targetHeight, double targetWidth){
public static void scaleAndCropImage(ImageView imageView, double targetWidth, double targetHeight){
Image image = imageView.getImage();

double width = image.getWidth();
double height = image.getHeight();

//Calculate ratio
double ratio = Math.min(width / targetWidth, height / targetHeight);
double viewWidth = targetWidth * ratio;
double viewHeight = targetHeight * ratio;
Expand All @@ -23,5 +29,19 @@ public static void scaleAndCropImage(ImageView imageView, double targetHeight, d
imageView.setFitWidth(targetWidth);
imageView.setFitHeight(targetHeight);
imageView.setPreserveRatio(false);
imageView.setSmooth(true);
}

public static Image getRestaurantImage(Restaurant restaurant) {
String imagePath = "/images/" + restaurant.getImagePath();
try {
return new Image(Objects.requireNonNull(ImageHandler.class.getResourceAsStream(imagePath)));
} catch (Exception e) {
System.out.println("Could not find: " + imagePath);
e.printStackTrace();
return null;
}
}
Comment on lines +35 to 44

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Returning null will cause NPE in downstream callers.

When image loading fails, this method returns null. However, RestaurantCard (line 25-26 in the relevant snippet) calls scaleAndCropImage() immediately after, which accesses image.getWidth() on line 16 of this file — causing a NullPointerException.

Consider either:

  1. Return a placeholder/fallback image instead of null
  2. Throw a custom exception to force callers to handle the error explicitly
  3. Document the null return and update callers to handle it
Option 1: Return a placeholder image (preferred)
 public static Image getRestaurantImage(Restaurant restaurant) {
     String imagePath = "/images/" + restaurant.getImagePath();
     try {
         return new Image(Objects.requireNonNull(ImageHandler.class.getResourceAsStream(imagePath)));
     } catch (Exception e) {
         System.out.println("Could not find: " + imagePath);
         e.printStackTrace();
-        return null;
+        // Return a placeholder image or handle gracefully
+        return new Image(Objects.requireNonNull(ImageHandler.class.getResourceAsStream("/images/placeholder.png")));
     }
 }
Option 2: Add null guard in RestaurantCard

In RestaurantCard.java:

Image image = ImageHandler.getRestaurantImage(restaurant);
if (image != null) {
    view.setImage(image);
    ImageHandler.scaleAndCropImage(view, 100, 150);
}
🤖 Prompt for AI Agents
In `@src/main/java/frontend/model/ImageHandler.java` around lines 35 - 44,
getRestaurantImage currently returns null on failure which causes NPEs when
callers like RestaurantCard call scaleAndCropImage; change getRestaurantImage to
return a non-null fallback Image instead of null (e.g., load a bundled
placeholder resource such as "/images/placeholder.png" and return that when
Objects.requireNonNull or resource loading fails), ensure the method still logs
the error, and leave scaleAndCropImage and RestaurantCard unchanged;
alternatively, if you prefer explicit handling, throw a custom unchecked
ImageLoadException from getRestaurantImage and update RestaurantCard to catch it
and handle the fallback there (use the symbols getRestaurantImage,
scaleAndCropImage, RestaurantCard).


}

39 changes: 13 additions & 26 deletions src/main/java/frontend/view/RestaurantCard.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,35 +22,22 @@ public RestaurantCard(Restaurant restaurant, Pane pane){
SceneHandler.switchScene(pane, "restaurant-view.fxml");
});

setUpImage(restaurant);
ImageView view = new ImageView();
view.setImage(ImageHandler.getRestaurantImage(restaurant));
ImageHandler.scaleAndCropImage(view, 150, 100);

//Rounds upper two corners of image, so it matches corners of VBox (container)
Rectangle clip = new Rectangle();
clip.widthProperty().bind(view.fitWidthProperty());
clip.heightProperty().bind(view.fitHeightProperty().add(50));
clip.setArcWidth(30);
clip.setArcHeight(30);

view.setClip(clip);
this.getChildren().add(view);
Comment on lines +25 to +37

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Potential NullPointerException if image loading fails.

ImageHandler.getRestaurantImage() returns null when the image cannot be found (see ImageHandler.java). If this happens, scaleAndCropImage() will throw an NPE when accessing image.getWidth() on the null image.

🛠️ Proposed fix
         ImageView view = new ImageView();
-        view.setImage(ImageHandler.getRestaurantImage(restaurant));
-        ImageHandler.scaleAndCropImage(view, 100, 150);
+        Image image = ImageHandler.getRestaurantImage(restaurant);
+        if (image != null) {
+            view.setImage(image);
+            ImageHandler.scaleAndCropImage(view, 100, 150);
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ImageView view = new ImageView();
view.setImage(ImageHandler.getRestaurantImage(restaurant));
ImageHandler.scaleAndCropImage(view, 100, 150);
//Rounds upper two corners of image, so it matches corners of VBox (container)
Rectangle clip = new Rectangle();
clip.widthProperty().bind(view.fitWidthProperty());
clip.heightProperty().bind(view.fitHeightProperty().add(50));
clip.setArcWidth(30);
clip.setArcHeight(30);
view.setClip(clip);
this.getChildren().add(view);
ImageView view = new ImageView();
Image image = ImageHandler.getRestaurantImage(restaurant);
if (image != null) {
view.setImage(image);
ImageHandler.scaleAndCropImage(view, 100, 150);
}
//Rounds upper two corners of image, so it matches corners of VBox (container)
Rectangle clip = new Rectangle();
clip.widthProperty().bind(view.fitWidthProperty());
clip.heightProperty().bind(view.fitHeightProperty().add(50));
clip.setArcWidth(30);
clip.setArcHeight(30);
view.setClip(clip);
this.getChildren().add(view);
🤖 Prompt for AI Agents
In `@src/main/java/frontend/view/RestaurantCard.java` around lines 25 - 37,
ImageHandler.getRestaurantImage(restaurant) can return null causing
ImageHandler.scaleAndCropImage(view,...) to NPE; update RestaurantCard to check
the returned Image before calling scaleAndCropImage: call
ImageHandler.getRestaurantImage(restaurant) into a local variable, if it's null
set view to a default/placeholder Image (or skip scaling) and only call
ImageHandler.scaleAndCropImage(view, 100, 150) and apply the Rectangle clip when
the Image is non-null; ensure the local variable and ImageView "view" are used
so you don't call scaleAndCropImage or access image properties on a null image.

setUpNameAndRating(restaurant);
}

private void setUpImage(Restaurant restaurant){
ImageView imageView = new ImageView();
String imagePath = "/images/" + restaurant.getImagePath();

try{
Image image = new Image(Objects.requireNonNull(RestaurantCard.class.getResourceAsStream(imagePath)));
imageView.setImage(image);
ImageHandler.scaleAndCropImage(imageView, 100, 150);

//Rounds upper two corners of image, so it matches corners of VBox (container)
Rectangle clip = new Rectangle();
clip.widthProperty().bind(imageView.fitWidthProperty());
clip.heightProperty().bind(imageView.fitHeightProperty().add(50));
clip.setArcWidth(30);
clip.setArcHeight(30);

imageView.setClip(clip);
this.getChildren().add(imageView);

} catch (Exception e) {
imageView.setImage(null);
System.out.println("Could not find: " + imagePath);
}
}

private void setUpNameAndRating(Restaurant restaurant){
HBox container = new HBox();

Expand Down
54 changes: 52 additions & 2 deletions src/main/resources/frontend/application.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
-fx-font-size: 15;
}

/*Restaurant Card*/
.restaurantBox{
-fx-background-color: white;
-fx-background-radius: 15;
Expand All @@ -11,16 +12,65 @@
/* Gives a small shadow behind the cards */
-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.1), 10, 0, 0, 4);
}

.restaurantBox:hover{
-fx-scale-x: 1.15;
-fx-scale-y: 1.15;
-fx-cursor: hand;
}

.restaurantBox .label{
-fx-padding: 10;
-fx-font-family: "Agency FB";
-fx-font-size: 16;
}

/*Restaurant Page*/
.restaurantPage-Header{
-fx-font-family: "Agency FB";
-fx-font-size: 32px;
-fx-text-fill: #1a1a1a;
-fx-padding: 0 20 10 0;
}
.restaurantPage-Info{
-fx-font-family: "Verdana";
-fx-font-size: 12px;
-fx-text-fill: #1a1a1a;
-fx-padding: 8 10 7 5;
}
.restaurantPage-Icon{
-fx-font-family: "Agency FB";
-fx-font-size: 12px;
-fx-text-fill: #333333;
-fx-padding: 8 0 10 0;
}

/*Booking Form*/
.bookingForm-Header{
-fx-font-family: "Agency FB";
-fx-font-size: 20px;
-fx-padding: 0 0 10 0;
}
.bookingForm-Textfield{
-fx-font-family: "Verdana";
-fx-spacing: 10;
}
.bookingForm-Labels .label{
-fx-font-family: "Verdana";
-fx-padding: 15 0 10 0;
}
.bookingForm-Choice {
-fx-alignment: TOP_RIGHT;
-fx-padding: 10 0 10 0;
-fx-spacing: 15;
}
.bookingForm-ChoiceBox {
-fx-pref-width: 100px;
-fx-max-width: 100px;
}
.bookingForm-Button{
-fx-alignment: center;
-fx-background-color: #4cbb17;
-fx-text-fill: #1a1a1a;
}



41 changes: 41 additions & 0 deletions src/main/resources/frontend/booking-form.fxml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<?import javafx.geometry.Insets?>
<VBox xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" fx:controller="frontend.controller.BookingController">
<padding>
<Insets top="15" bottom="10" left="20" right="40"/>
</padding>

<Label fx:id="statusLabel"/>
<Label text="Book a table:" styleClass="bookingForm-Header"/>

<VBox styleClass="bookingForm-Textfield">
<TextField fx:id="firstNameField" promptText="First name"/>
<TextField fx:id="lastNameField" promptText="Last name"/>
<TextField fx:id="phoneField" promptText="Phone number"/>
<TextField fx:id="emailField" promptText="Email"/>
</VBox>


<HBox>
<VBox styleClass="bookingForm-Labels">
<Label text="Guests"/>
<Label text="Date"/>
<Label text="Start time"/>
</VBox>

<VBox styleClass="bookingForm-Choice">
<Spinner fx:id="guestsSpinner" prefWidth="50" styleClass="bookingForm-ChoiceBox"/>
<DatePicker fx:id="datePicker" styleClass="bookingForm-ChoiceBox"/>
<ComboBox fx:id="startTimeBox" prefWidth="100" styleClass="bookingForm-ChoiceBox"/>
</VBox>
</HBox>

<VBox alignment="CENTER">
<Button text="Book" onAction="#BookTable" prefWidth="100" styleClass="bookingForm-Button"/>
</VBox>

</VBox>
48 changes: 0 additions & 48 deletions src/main/resources/frontend/booking-view.fxml

This file was deleted.

Loading
Loading