From fa80e657af2de78018aa939809d73de0c9c57ca6 Mon Sep 17 00:00:00 2001 From: Eric Thilen Date: Tue, 6 Jan 2026 14:16:05 +0100 Subject: [PATCH 1/9] commit on branch --- src/main/java/org/example/App.java | 3 - src/main/java/org/example/Car.java | 4 ++ .../java/org/example/config/JPAConfig.java | 21 +++++++ .../example/controller/BookingController.java | 4 ++ .../org/example/controller/CarController.java | 11 ++++ .../controller/CustomerController.java | 5 ++ .../controller/MainViewController.java | 4 ++ src/main/java/org/example/model/Booking.java | 58 +++++++++++++++++++ .../java/org/example/model/BookingStatus.java | 8 +++ src/main/java/org/example/model/Car.java | 41 +++++++++++++ src/main/java/org/example/model/Customer.java | 21 +++++++ src/main/java/org/example/model/Payment.java | 7 +++ .../example/repository/BookingRepository.java | 4 ++ .../org/example/repository/CarRepository.java | 4 ++ .../repository/CustomerRepository.java | 4 ++ .../org/example/service/BookingService.java | 4 ++ .../java/org/example/service/CarService.java | 4 ++ .../org/example/service/CustomerService.java | 4 ++ src/main/resources/init.sql | 0 src/main/resources/persistence.xml | 23 ++++++++ 20 files changed, 231 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/example/Car.java create mode 100644 src/main/java/org/example/config/JPAConfig.java create mode 100644 src/main/java/org/example/controller/BookingController.java create mode 100644 src/main/java/org/example/controller/CarController.java create mode 100644 src/main/java/org/example/controller/CustomerController.java create mode 100644 src/main/java/org/example/controller/MainViewController.java create mode 100644 src/main/java/org/example/model/Booking.java create mode 100644 src/main/java/org/example/model/BookingStatus.java create mode 100644 src/main/java/org/example/model/Car.java create mode 100644 src/main/java/org/example/model/Customer.java create mode 100644 src/main/java/org/example/model/Payment.java create mode 100644 src/main/java/org/example/repository/BookingRepository.java create mode 100644 src/main/java/org/example/repository/CarRepository.java create mode 100644 src/main/java/org/example/repository/CustomerRepository.java create mode 100644 src/main/java/org/example/service/BookingService.java create mode 100644 src/main/java/org/example/service/CarService.java create mode 100644 src/main/java/org/example/service/CustomerService.java create mode 100644 src/main/resources/init.sql create mode 100644 src/main/resources/persistence.xml diff --git a/src/main/java/org/example/App.java b/src/main/java/org/example/App.java index 165e5cd5..666a561f 100644 --- a/src/main/java/org/example/App.java +++ b/src/main/java/org/example/App.java @@ -1,7 +1,4 @@ package org.example; public class App { - public static void main(String[] args) { - System.out.println("Hello There!"); - } } diff --git a/src/main/java/org/example/Car.java b/src/main/java/org/example/Car.java new file mode 100644 index 00000000..82972e73 --- /dev/null +++ b/src/main/java/org/example/Car.java @@ -0,0 +1,4 @@ +package org.example; + +public class Car { +} diff --git a/src/main/java/org/example/config/JPAConfig.java b/src/main/java/org/example/config/JPAConfig.java new file mode 100644 index 00000000..44b524ba --- /dev/null +++ b/src/main/java/org/example/config/JPAConfig.java @@ -0,0 +1,21 @@ +package org.example.config; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.Persistence; + +public class JPAConfig { + + private static final EntityManagerFactory emf = + Persistence.createEntityManagerFactory("carRentalPU"); + + public static EntityManager getEntityManager() { + return emf.createEntityManager(); + } + + public static void close() { + if (emf.isOpen()) { + emf.close(); + } + } +} diff --git a/src/main/java/org/example/controller/BookingController.java b/src/main/java/org/example/controller/BookingController.java new file mode 100644 index 00000000..7c7a57e5 --- /dev/null +++ b/src/main/java/org/example/controller/BookingController.java @@ -0,0 +1,4 @@ +package org.example.controller; + +public class BookingController { +} diff --git a/src/main/java/org/example/controller/CarController.java b/src/main/java/org/example/controller/CarController.java new file mode 100644 index 00000000..49de64a5 --- /dev/null +++ b/src/main/java/org/example/controller/CarController.java @@ -0,0 +1,11 @@ +package org.example.controller; + +public class CarController { + boolean CarDamage=false; + + boolean CarRented=false; + boolean CarAvailable=true; + boolean CarBooked=false; + boolean CarBroke=false; + boolean CarReturned=false; +} diff --git a/src/main/java/org/example/controller/CustomerController.java b/src/main/java/org/example/controller/CustomerController.java new file mode 100644 index 00000000..88432c18 --- /dev/null +++ b/src/main/java/org/example/controller/CustomerController.java @@ -0,0 +1,5 @@ +package org.example.controller; + +public interface CustomerController { + int addCustomer(); +} diff --git a/src/main/java/org/example/controller/MainViewController.java b/src/main/java/org/example/controller/MainViewController.java new file mode 100644 index 00000000..aa1a0cdc --- /dev/null +++ b/src/main/java/org/example/controller/MainViewController.java @@ -0,0 +1,4 @@ +package org.example.controller; + +public class MainViewController { +} diff --git a/src/main/java/org/example/model/Booking.java b/src/main/java/org/example/model/Booking.java new file mode 100644 index 00000000..52fd6bb4 --- /dev/null +++ b/src/main/java/org/example/model/Booking.java @@ -0,0 +1,58 @@ +package org.example.model; + +import jakarta.persistence.*; + + +import javax.tools.DocumentationTool; +import javax.xml.stream.Location; +import java.time.LocalDate; +import java.time.LocalDateTime; + +@Entity +public class Booking { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(optional = false) + @JoinColumn(name = "customer_id") + Customer customer; + + @ManyToOne(optional = false) + @JoinColumn(name = "car_id") + private Car car; + + @ManyToOne + @JoinColumn(name = "pickup_location_id") + private Location pickupLocation; + + @ManyToOne + @JoinColumn(name = "dropoff_location_id") + private Location dropoffLocation; + + @OneToMany + @JoinColumn(name = "booking_id") + private List extras; + + @OneToOne + @JoinColumn(name = "payment_id") + private Payment payment; + + private LocalDate startDate; + private LocalDate endDate; + + protected Booking() { + // JPA requires a no-arg constructor + } + + public Booking(Customer customer, Car car, Location pickupLocation, Location dropoffLocation, LocalDate startDate, LocalDate endDate) { + this.customer = customer; + this.car = car; + this.startDate = startDate; + this.endDate = endDate; + this.status = BookingStatus.RESERVED; + this.createdAt = LocalDateTime.now(); + + } + +} diff --git a/src/main/java/org/example/model/BookingStatus.java b/src/main/java/org/example/model/BookingStatus.java new file mode 100644 index 00000000..7960aa78 --- /dev/null +++ b/src/main/java/org/example/model/BookingStatus.java @@ -0,0 +1,8 @@ +package org.example.model; + +public enum BookingStatus { + RESERVED, + ACTIVE, + COMPLETED, + CANCELLED +} diff --git a/src/main/java/org/example/model/Car.java b/src/main/java/org/example/model/Car.java new file mode 100644 index 00000000..b5ec40f0 --- /dev/null +++ b/src/main/java/org/example/model/Car.java @@ -0,0 +1,41 @@ +package org.example; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; + +@Entity +public class Car { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String brand; + private String model; + private int year; + private String type; + private double price; + private double dailyPrice; // ny egenskap + + // Getters och setters + public Long getId() { return id; } + + public String getBrand() { return brand; } + public void setBrand(String brand) { this.brand = brand; } + + public String getModel() { return model; } + public void setModel(String model) { this.model = model; } + + public int getYear() { return year; } + public void setYear(int year) { this.year = year; } + + public String getType() { return type; } + public void setType(String type) { this.type = type; } + + public double getPrice() { return price; } + public void setPrice(double price) { this.price = price; } + + public double getDailyPrice() { return dailyPrice; } + public void setDailyPrice(double dailyPrice) { this.dailyPrice = dailyPrice; } +} diff --git a/src/main/java/org/example/model/Customer.java b/src/main/java/org/example/model/Customer.java new file mode 100644 index 00000000..1a66e22d --- /dev/null +++ b/src/main/java/org/example/model/Customer.java @@ -0,0 +1,21 @@ +package org.example.model; + +import jakarta.persistence.*; +import java.util.ArrayList; +import java.util.List; + +@Entity +public class Customer { + + @Id + @GeneratedValue + private Long id; + + private String name; + private String email; + + @OneToMany(mappedBy = "customer") + private List bookings = new ArrayList<>(); + + // getters & setters +} diff --git a/src/main/java/org/example/model/Payment.java b/src/main/java/org/example/model/Payment.java new file mode 100644 index 00000000..0e4a6dd0 --- /dev/null +++ b/src/main/java/org/example/model/Payment.java @@ -0,0 +1,7 @@ +package org.example.model; + +import jakarta.persistence.Entity; + +@Entity +public class Payment { +} diff --git a/src/main/java/org/example/repository/BookingRepository.java b/src/main/java/org/example/repository/BookingRepository.java new file mode 100644 index 00000000..a63b50d4 --- /dev/null +++ b/src/main/java/org/example/repository/BookingRepository.java @@ -0,0 +1,4 @@ +package org.example.repository; + +public class BookingRepository { +} diff --git a/src/main/java/org/example/repository/CarRepository.java b/src/main/java/org/example/repository/CarRepository.java new file mode 100644 index 00000000..69ce8060 --- /dev/null +++ b/src/main/java/org/example/repository/CarRepository.java @@ -0,0 +1,4 @@ +package org.example.repository; + +public class CarRepository { +} diff --git a/src/main/java/org/example/repository/CustomerRepository.java b/src/main/java/org/example/repository/CustomerRepository.java new file mode 100644 index 00000000..38b788c2 --- /dev/null +++ b/src/main/java/org/example/repository/CustomerRepository.java @@ -0,0 +1,4 @@ +package org.example.repository; + +public class CustomerRepository { +} diff --git a/src/main/java/org/example/service/BookingService.java b/src/main/java/org/example/service/BookingService.java new file mode 100644 index 00000000..aea99093 --- /dev/null +++ b/src/main/java/org/example/service/BookingService.java @@ -0,0 +1,4 @@ +package org.example.service; + +public class BookingService { +} diff --git a/src/main/java/org/example/service/CarService.java b/src/main/java/org/example/service/CarService.java new file mode 100644 index 00000000..e81e1ea5 --- /dev/null +++ b/src/main/java/org/example/service/CarService.java @@ -0,0 +1,4 @@ +package org.example.service; + +public class CarService { +} diff --git a/src/main/java/org/example/service/CustomerService.java b/src/main/java/org/example/service/CustomerService.java new file mode 100644 index 00000000..4c00baea --- /dev/null +++ b/src/main/java/org/example/service/CustomerService.java @@ -0,0 +1,4 @@ +package org.example.service; + +public class CustomerService { +} diff --git a/src/main/resources/init.sql b/src/main/resources/init.sql new file mode 100644 index 00000000..e69de29b diff --git a/src/main/resources/persistence.xml b/src/main/resources/persistence.xml new file mode 100644 index 00000000..0377b867 --- /dev/null +++ b/src/main/resources/persistence.xml @@ -0,0 +1,23 @@ + + + + org.example.Car + + + + + + + + + + + + + + From b5b8c5678aee44de5e10a34363189e11f0bc6500 Mon Sep 17 00:00:00 2001 From: Eric Thilen Date: Fri, 9 Jan 2026 14:37:35 +0100 Subject: [PATCH 2/9] commit on branch --- docker-compose.yml | 15 ++ README.md | 53 +++++++ pom.xml | 90 ++++++----- src/main/java/org/example/App.java | 4 - src/main/java/org/example/Car.java | 4 - src/main/java/org/example/Main.java | 23 +++ .../config/{JPAConfig.java => JpaUtil.java} | 10 +- .../example/controller/BookingController.java | 140 ++++++++++++++++++ .../org/example/controller/CarController.java | 11 -- .../controller/CentralHubController.java | 64 ++++++++ .../controller/CustomerController.java | 5 - .../example/controller/MainController.java | 36 +++++ .../controller/MainViewController.java | 4 - src/main/java/org/example/entity/Addon.java | 36 +++++ src/main/java/org/example/entity/Booking.java | 52 +++++++ .../java/org/example/entity/BookingType.java | 6 + src/main/java/org/example/entity/Car.java | 49 ++++++ src/main/java/org/example/model/Booking.java | 58 -------- .../java/org/example/model/BookingStatus.java | 8 - src/main/java/org/example/model/Car.java | 41 ----- src/main/java/org/example/model/Customer.java | 21 --- .../example/repository/AddonRepository.java | 14 ++ .../example/repository/BookingRepository.java | 10 ++ .../org/example/repository/CarRepository.java | 10 ++ .../repository/CustomerRepository.java | 4 - .../org/example/service/AddonService.java | 14 ++ .../org/example/service/BookingService.java | 84 +++++++++++ .../java/org/example/service/CarService.java | 10 ++ .../org/example/service/CustomerService.java | 4 - src/main/resources/META-INF/persistence.xml | 28 ++++ src/main/resources/fxml/booking-view.fxml | 42 ++++++ src/main/resources/fxml/central-hub.fxml | 23 +++ src/main/resources/fxml/main-view.fxml | 21 +++ src/main/resources/init.sql | 0 src/main/resources/persistence.xml | 23 --- 35 files changed, 787 insertions(+), 230 deletions(-) create mode 100644 docker-compose.yml delete mode 100644 src/main/java/org/example/App.java delete mode 100644 src/main/java/org/example/Car.java create mode 100644 src/main/java/org/example/Main.java rename src/main/java/org/example/config/{JPAConfig.java => JpaUtil.java} (62%) delete mode 100644 src/main/java/org/example/controller/CarController.java create mode 100644 src/main/java/org/example/controller/CentralHubController.java delete mode 100644 src/main/java/org/example/controller/CustomerController.java create mode 100644 src/main/java/org/example/controller/MainController.java delete mode 100644 src/main/java/org/example/controller/MainViewController.java create mode 100644 src/main/java/org/example/entity/Addon.java create mode 100644 src/main/java/org/example/entity/Booking.java create mode 100644 src/main/java/org/example/entity/BookingType.java create mode 100644 src/main/java/org/example/entity/Car.java delete mode 100644 src/main/java/org/example/model/Booking.java delete mode 100644 src/main/java/org/example/model/BookingStatus.java delete mode 100644 src/main/java/org/example/model/Car.java delete mode 100644 src/main/java/org/example/model/Customer.java create mode 100644 src/main/java/org/example/repository/AddonRepository.java delete mode 100644 src/main/java/org/example/repository/CustomerRepository.java create mode 100644 src/main/java/org/example/service/AddonService.java delete mode 100644 src/main/java/org/example/service/CustomerService.java create mode 100644 src/main/resources/META-INF/persistence.xml create mode 100644 src/main/resources/fxml/booking-view.fxml create mode 100644 src/main/resources/fxml/central-hub.fxml create mode 100644 src/main/resources/fxml/main-view.fxml delete mode 100644 src/main/resources/init.sql delete mode 100644 src/main/resources/persistence.xml diff --git a/ docker-compose.yml b/ docker-compose.yml new file mode 100644 index 00000000..e39b1170 --- /dev/null +++ b/ docker-compose.yml @@ -0,0 +1,15 @@ +version: "3.8" + +services: + mysql: + image: mysql:8 + container_name: car_rental_mysql + restart: always + environment: + MYSQL_ROOT_PASSWORD: root + MYSQL_DATABASE: car_rental + MYSQL_USER: car_user + MYSQL_PASSWORD: strong_password_here + ports: + - "3306:3306" + command: --default-authentication-plugin=mysql_native_password diff --git a/README.md b/README.md index 05def9a9..89e2874a 100644 --- a/README.md +++ b/README.md @@ -60,3 +60,56 @@ Many-to-Many: Uppgifter ↔ Användare One-to-Many: En restaurang → flera bord Many-to-Many: Gäster ↔ Bokningar + + +--- + +## MySQL i Docker för detta projekt + +Följ dessa steg för att köra MySQL lokalt i Docker och koppla upp applikationen. + +### 1) Starta MySQL 8 med Docker Compose + +Kräver Docker Desktop (eller docker + docker compose plugin). + +Kör i projektroten: +``` +docker compose up -d +``` +Det startar en MySQL 8-container med: +- Databas: `car_rental` +- Användare: `car_user` +- Lösenord: `strong_password_here` +- Port: `3306` (exponerad på localhost) + +### 2) JPA/Hibernate är konfigurerat för MySQL +`src/main/resources/META-INF/persistence.xml` pekar nu på: +``` +jdbc:mysql://localhost:3306/car_rental +user=car_user, password=strong_password_here +``` +Hibernate skapar/uppdaterar tabellerna vid start (`hibernate.hbm2ddl.auto=update`). + +### 3) Verifiera och fyll på data +- Verifiera att containern är igång: +``` +docker ps +``` +- Logga in (om du har MySQL-klient): +``` +mysql -h 127.0.0.1 -P 3306 -u car_user -p +USE car_rental; +SHOW TABLES; +``` +- Lägg in seed-data (efter att applikationen skapat tabellerna) med filen `sql/seeds.sql`: +``` +mysql -h 127.0.0.1 -P 3306 -u car_user -p car_rental < sql/seeds.sql +``` + +### 4) Starta applikationen +- Bygg och kör som vanligt (t.ex. via IntelliJ eller Maven). Vid start kommer Hibernate att skapa tabellerna i MySQL. + +### 5) Felsökning +- Port 3306 upptagen → stoppa annan MySQL eller ändra port i `docker-compose.yml` och i `persistence.xml`. +- Access denied → kontrollera user/lösen i både compose och `persistence.xml`. +- No suitable driver → kör `mvn clean package` och säkerställ att `mysql-connector-j` finns i `pom.xml`. diff --git a/pom.xml b/pom.xml index 909503d0..78c8d451 100644 --- a/pom.xml +++ b/pom.xml @@ -1,54 +1,74 @@ - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 + http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 org.example - JavaJPA + car-rental 1.0-SNAPSHOT - 25 + 25 + 25 UTF-8 - 6.0.1 - 3.27.6 - 5.21.0 + 22.0.2 + 6.4.4.Final + 3.1.0 + + + + org.openjfx + javafx-controls + ${javafx.version} + + + org.openjfx + javafx-fxml + ${javafx.version} + + + - org.junit.jupiter - junit-jupiter - ${junit.jupiter.version} - test + jakarta.persistence + jakarta.persistence-api + ${jakarta.persistence.version} + + + + org.hibernate.orm + hibernate-core + ${hibernate.version} + + + - org.assertj - assertj-core - ${assertj.core.version} - test + com.mysql + mysql-connector-j + 8.3.0 + + - org.mockito - mockito-junit-jupiter - ${mockito.version} - test + org.slf4j + slf4j-simple + 2.0.12 - - org.hibernate.orm - hibernate-core - 7.2.0.Final - - - com.mysql - mysql-connector-j - 9.5.0 - runtime - - - io.github.classgraph - classgraph - 4.8.184 - + + + + + org.openjfx + javafx-maven-plugin + 0.0.8 + + org.example.Main + + + + diff --git a/src/main/java/org/example/App.java b/src/main/java/org/example/App.java deleted file mode 100644 index 666a561f..00000000 --- a/src/main/java/org/example/App.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.example; - -public class App { -} diff --git a/src/main/java/org/example/Car.java b/src/main/java/org/example/Car.java deleted file mode 100644 index 82972e73..00000000 --- a/src/main/java/org/example/Car.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.example; - -public class Car { -} diff --git a/src/main/java/org/example/Main.java b/src/main/java/org/example/Main.java new file mode 100644 index 00000000..77b10c2b --- /dev/null +++ b/src/main/java/org/example/Main.java @@ -0,0 +1,23 @@ +package org.example; + +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.stage.Stage; + +public class Main extends Application { + + @Override + public void start(Stage stage) throws Exception { + FXMLLoader loader = new FXMLLoader( + getClass().getResource("/fxml/main-view.fxml")); + Scene scene = new Scene(loader.load()); + stage.setTitle("Car Rental"); + stage.setScene(scene); + stage.show(); + } + + public static void main(String[] args) { + launch(); + } +} diff --git a/src/main/java/org/example/config/JPAConfig.java b/src/main/java/org/example/config/JpaUtil.java similarity index 62% rename from src/main/java/org/example/config/JPAConfig.java rename to src/main/java/org/example/config/JpaUtil.java index 44b524ba..b25ca174 100644 --- a/src/main/java/org/example/config/JPAConfig.java +++ b/src/main/java/org/example/config/JpaUtil.java @@ -4,18 +4,12 @@ import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.Persistence; -public class JPAConfig { +public class JpaUtil { private static final EntityManagerFactory emf = - Persistence.createEntityManagerFactory("carRentalPU"); + Persistence.createEntityManagerFactory("car_rental_pu"); public static EntityManager getEntityManager() { return emf.createEntityManager(); } - - public static void close() { - if (emf.isOpen()) { - emf.close(); - } - } } diff --git a/src/main/java/org/example/controller/BookingController.java b/src/main/java/org/example/controller/BookingController.java index 7c7a57e5..39bdb5fb 100644 --- a/src/main/java/org/example/controller/BookingController.java +++ b/src/main/java/org/example/controller/BookingController.java @@ -1,4 +1,144 @@ package org.example.controller; +import javafx.fxml.FXML; +import javafx.scene.control.*; +import javafx.scene.layout.VBox; +import org.example.entity.Addon; +import org.example.entity.Booking; +import org.example.entity.BookingType; +import org.example.entity.Car; +import org.example.service.AddonService; +import org.example.service.BookingService; +import org.example.service.CarService; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.List; + public class BookingController { + + @FXML + private ComboBox carComboBox; + + @FXML + private RadioButton hourlyRadio; + + @FXML + private RadioButton dailyRadio; + + @FXML + private DatePicker startDatePicker; + + @FXML + private DatePicker endDatePicker; + + @FXML + private VBox addonsVBox; + + @FXML + private Label priceLabel; + + @FXML + private Label statusLabel; + + private final CarService carService = new CarService(); + private final BookingService bookingService = new BookingService(); + private final AddonService addonService = new AddonService(); + + private final ToggleGroup bookingTypeGroup = new ToggleGroup(); + private final List addonCheckBoxes = new ArrayList<>(); + + @FXML + public void initialize() { + carComboBox.getItems().setAll(carService.getAllCars()); + + hourlyRadio.setToggleGroup(bookingTypeGroup); + dailyRadio.setToggleGroup(bookingTypeGroup); + dailyRadio.setSelected(true); + + List addons = addonService.getAllAddons(); + for (Addon addon : addons) { + CheckBox cb = new CheckBox(addon.toString()); + cb.setUserData(addon); + cb.setOnAction(e -> updatePrice()); + addonCheckBoxes.add(cb); + addonsVBox.getChildren().add(cb); + } + + carComboBox.setOnAction(e -> updatePrice()); + hourlyRadio.setOnAction(e -> updatePrice()); + dailyRadio.setOnAction(e -> updatePrice()); + startDatePicker.setOnAction(e -> updatePrice()); + endDatePicker.setOnAction(e -> updatePrice()); + } + + private void updatePrice() { + if (canCalculatePrice()) { + double price = bookingService.calculatePrice( + carComboBox.getValue(), + startDatePicker.getValue().atStartOfDay(), + endDatePicker.getValue().atTime(LocalTime.MAX), + getSelectedType(), + getSelectedAddons() + ); + priceLabel.setText(String.format("%.2f kr", price)); + } + } + + private boolean canCalculatePrice() { + return carComboBox.getValue() != null && + startDatePicker.getValue() != null && + endDatePicker.getValue() != null && + !endDatePicker.getValue().isBefore(startDatePicker.getValue()); + } + + private BookingType getSelectedType() { + return hourlyRadio.isSelected() ? BookingType.HOURLY : BookingType.DAILY; + } + + private List getSelectedAddons() { + List selected = new ArrayList<>(); + for (CheckBox cb : addonCheckBoxes) { + if (cb.isSelected()) { + selected.add((Addon) cb.getUserData()); + } + } + return selected; + } + + @FXML + private void handleBooking() { + if (!canCalculatePrice()) { + statusLabel.setText("Vänligen fyll i alla fält korrekt."); + statusLabel.setStyle("-fx-text-fill: red;"); + return; + } + + Car car = carComboBox.getValue(); + LocalDateTime start = startDatePicker.getValue().atStartOfDay(); + LocalDateTime end = endDatePicker.getValue().atTime(LocalTime.MAX); + BookingType type = getSelectedType(); + List addons = getSelectedAddons(); + + if (!bookingService.isCarAvailable(car, start, end)) { + statusLabel.setText("Bilen är tyvärr inte tillgänglig under denna period."); + statusLabel.setStyle("-fx-text-fill: red;"); + return; + } + + double price = bookingService.calculatePrice(car, start, end, type, addons); + + Booking booking = new Booking(start, end, type, car); + booking.setAddons(addons); + booking.setTotalPrice(price); + + try { + bookingService.saveBooking(booking); + statusLabel.setText("Bokning bekräftad!"); + statusLabel.setStyle("-fx-text-fill: green;"); + } catch (Exception e) { + statusLabel.setText("Ett fel uppstod vid bokning."); + statusLabel.setStyle("-fx-text-fill: red;"); + } + } } diff --git a/src/main/java/org/example/controller/CarController.java b/src/main/java/org/example/controller/CarController.java deleted file mode 100644 index 49de64a5..00000000 --- a/src/main/java/org/example/controller/CarController.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.example.controller; - -public class CarController { - boolean CarDamage=false; - - boolean CarRented=false; - boolean CarAvailable=true; - boolean CarBooked=false; - boolean CarBroke=false; - boolean CarReturned=false; -} diff --git a/src/main/java/org/example/controller/CentralHubController.java b/src/main/java/org/example/controller/CentralHubController.java new file mode 100644 index 00000000..a42f195b --- /dev/null +++ b/src/main/java/org/example/controller/CentralHubController.java @@ -0,0 +1,64 @@ +package org.example.controller; + +import javafx.beans.property.SimpleStringProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.TableCell; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.cell.PropertyValueFactory; +import org.example.entity.Car; +import org.example.service.BookingService; +import org.example.service.CarService; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class CentralHubController { + + @FXML + private TableView carTable; + + @FXML + private TableColumn brandColumn; + + @FXML + private TableColumn modelColumn; + + @FXML + private TableColumn regColumn; + + @FXML + private TableColumn availabilityColumn; + + @FXML + private TableColumn nextAvailableColumn; + + private final CarService carService = new CarService(); + private final BookingService bookingService = new BookingService(); + private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); + + @FXML + public void initialize() { + brandColumn.setCellValueFactory(new PropertyValueFactory<>("brand")); + modelColumn.setCellValueFactory(new PropertyValueFactory<>("model")); + regColumn.setCellValueFactory(new PropertyValueFactory<>("registrationNumber")); + + availabilityColumn.setCellValueFactory(cellData -> { + Car car = cellData.getValue(); + boolean available = bookingService.isCarAvailable(car, LocalDateTime.now(), LocalDateTime.now().plusMinutes(1)); + return new SimpleStringProperty(available ? "Tillgänglig" : "Bokad"); + }); + + nextAvailableColumn.setCellValueFactory(cellData -> { + Car car = cellData.getValue(); + LocalDateTime nextTime = bookingService.getNextAvailableTime(car); + if (nextTime.isBefore(LocalDateTime.now()) || nextTime.isEqual(LocalDateTime.now())) { + return new SimpleStringProperty("Nu"); + } + return new SimpleStringProperty(nextTime.format(formatter)); + }); + + carTable.getItems().setAll(carService.getAllCars()); + } +} diff --git a/src/main/java/org/example/controller/CustomerController.java b/src/main/java/org/example/controller/CustomerController.java deleted file mode 100644 index 88432c18..00000000 --- a/src/main/java/org/example/controller/CustomerController.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.example.controller; - -public interface CustomerController { - int addCustomer(); -} diff --git a/src/main/java/org/example/controller/MainController.java b/src/main/java/org/example/controller/MainController.java new file mode 100644 index 00000000..d7e00b7f --- /dev/null +++ b/src/main/java/org/example/controller/MainController.java @@ -0,0 +1,36 @@ +package org.example.controller; + +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.Node; +import javafx.scene.layout.StackPane; + +public class MainController { + + @FXML + private StackPane contentArea; + + @FXML + public void initialize() { + showCentralHub(); + } + + @FXML + private void showCentralHub() { + load("/fxml/central-hub.fxml"); + } + + @FXML + private void showBookingView() { + load("/fxml/booking-view.fxml"); + } + + private void load(String path) { + try { + Node node = FXMLLoader.load(getClass().getResource(path)); + contentArea.getChildren().setAll(node); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/org/example/controller/MainViewController.java b/src/main/java/org/example/controller/MainViewController.java deleted file mode 100644 index aa1a0cdc..00000000 --- a/src/main/java/org/example/controller/MainViewController.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.example.controller; - -public class MainViewController { -} diff --git a/src/main/java/org/example/entity/Addon.java b/src/main/java/org/example/entity/Addon.java new file mode 100644 index 00000000..e27cd73b --- /dev/null +++ b/src/main/java/org/example/entity/Addon.java @@ -0,0 +1,36 @@ +package org.example.entity; + +import jakarta.persistence.*; +import java.util.ArrayList; +import java.util.List; + +@Entity +@Table(name = "addons") +public class Addon { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + private double price; + + @ManyToMany(mappedBy = "addons") + private List bookings = new ArrayList<>(); + + public Addon() {} + + public Addon(String name, double price) { + this.name = name; + this.price = price; + } + + public Long getId() { return id; } + public String getName() { return name; } + public double getPrice() { return price; } + + @Override + public String toString() { + return name + " (" + price + " kr)"; + } +} diff --git a/src/main/java/org/example/entity/Booking.java b/src/main/java/org/example/entity/Booking.java new file mode 100644 index 00000000..b01078e5 --- /dev/null +++ b/src/main/java/org/example/entity/Booking.java @@ -0,0 +1,52 @@ +package org.example.entity; + +import jakarta.persistence.*; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@Entity +@Table(name = "bookings") +public class Booking { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private LocalDateTime startTime; + private LocalDateTime endTime; + + @Enumerated(EnumType.STRING) + private BookingType bookingType; + + @ManyToOne + private Car car; + + @ManyToMany + private List addons = new ArrayList<>(); + + private double totalPrice; + + public Booking() {} + + public Booking(LocalDateTime startTime, LocalDateTime endTime, BookingType bookingType, Car car) { + this.startTime = startTime; + this.endTime = endTime; + this.bookingType = bookingType; + this.car = car; + } + + public Long getId() { return id; } + public LocalDateTime getStartTime() { return startTime; } + public void setStartTime(LocalDateTime startTime) { this.startTime = startTime; } + public LocalDateTime getEndTime() { return endTime; } + public void setEndTime(LocalDateTime endTime) { this.endTime = endTime; } + public BookingType getBookingType() { return bookingType; } + public void setBookingType(BookingType bookingType) { this.bookingType = bookingType; } + public Car getCar() { return car; } + public void setCar(Car car) { this.car = car; } + public List getAddons() { return addons; } + public void setAddons(List addons) { this.addons = addons; } + public double getTotalPrice() { return totalPrice; } + public void setTotalPrice(double totalPrice) { this.totalPrice = totalPrice; } +} diff --git a/src/main/java/org/example/entity/BookingType.java b/src/main/java/org/example/entity/BookingType.java new file mode 100644 index 00000000..9c228246 --- /dev/null +++ b/src/main/java/org/example/entity/BookingType.java @@ -0,0 +1,6 @@ +package org.example.entity; + +public enum BookingType { + HOURLY, + DAILY +} diff --git a/src/main/java/org/example/entity/Car.java b/src/main/java/org/example/entity/Car.java new file mode 100644 index 00000000..9ddeeef3 --- /dev/null +++ b/src/main/java/org/example/entity/Car.java @@ -0,0 +1,49 @@ +package org.example.entity; + +import jakarta.persistence.*; +import java.util.ArrayList; +import java.util.List; + +@Entity +@Table(name = "cars") +public class Car { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String brand; + private String model; + + @Column(unique = true) + private String registrationNumber; + + private double pricePerHour; + private double pricePerDay; + + @OneToMany(mappedBy = "car", cascade = CascadeType.ALL) + private List bookings = new ArrayList<>(); + + public Car() {} + + public Car(String brand, String model, String registrationNumber, + double pricePerHour, double pricePerDay) { + this.brand = brand; + this.model = model; + this.registrationNumber = registrationNumber; + this.pricePerHour = pricePerHour; + this.pricePerDay = pricePerDay; + } + + public Long getId() { return id; } + public String getBrand() { return brand; } + public String getModel() { return model; } + public String getRegistrationNumber() { return registrationNumber; } + public double getPricePerHour() { return pricePerHour; } + public double getPricePerDay() { return pricePerDay; } + + @Override + public String toString() { + return brand + " " + model + " (" + registrationNumber + ")"; + } +} diff --git a/src/main/java/org/example/model/Booking.java b/src/main/java/org/example/model/Booking.java deleted file mode 100644 index 52fd6bb4..00000000 --- a/src/main/java/org/example/model/Booking.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.example.model; - -import jakarta.persistence.*; - - -import javax.tools.DocumentationTool; -import javax.xml.stream.Location; -import java.time.LocalDate; -import java.time.LocalDateTime; - -@Entity -public class Booking { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @ManyToOne(optional = false) - @JoinColumn(name = "customer_id") - Customer customer; - - @ManyToOne(optional = false) - @JoinColumn(name = "car_id") - private Car car; - - @ManyToOne - @JoinColumn(name = "pickup_location_id") - private Location pickupLocation; - - @ManyToOne - @JoinColumn(name = "dropoff_location_id") - private Location dropoffLocation; - - @OneToMany - @JoinColumn(name = "booking_id") - private List extras; - - @OneToOne - @JoinColumn(name = "payment_id") - private Payment payment; - - private LocalDate startDate; - private LocalDate endDate; - - protected Booking() { - // JPA requires a no-arg constructor - } - - public Booking(Customer customer, Car car, Location pickupLocation, Location dropoffLocation, LocalDate startDate, LocalDate endDate) { - this.customer = customer; - this.car = car; - this.startDate = startDate; - this.endDate = endDate; - this.status = BookingStatus.RESERVED; - this.createdAt = LocalDateTime.now(); - - } - -} diff --git a/src/main/java/org/example/model/BookingStatus.java b/src/main/java/org/example/model/BookingStatus.java deleted file mode 100644 index 7960aa78..00000000 --- a/src/main/java/org/example/model/BookingStatus.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.example.model; - -public enum BookingStatus { - RESERVED, - ACTIVE, - COMPLETED, - CANCELLED -} diff --git a/src/main/java/org/example/model/Car.java b/src/main/java/org/example/model/Car.java deleted file mode 100644 index b5ec40f0..00000000 --- a/src/main/java/org/example/model/Car.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.example; - -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; - -@Entity -public class Car { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - private String brand; - private String model; - private int year; - private String type; - private double price; - private double dailyPrice; // ny egenskap - - // Getters och setters - public Long getId() { return id; } - - public String getBrand() { return brand; } - public void setBrand(String brand) { this.brand = brand; } - - public String getModel() { return model; } - public void setModel(String model) { this.model = model; } - - public int getYear() { return year; } - public void setYear(int year) { this.year = year; } - - public String getType() { return type; } - public void setType(String type) { this.type = type; } - - public double getPrice() { return price; } - public void setPrice(double price) { this.price = price; } - - public double getDailyPrice() { return dailyPrice; } - public void setDailyPrice(double dailyPrice) { this.dailyPrice = dailyPrice; } -} diff --git a/src/main/java/org/example/model/Customer.java b/src/main/java/org/example/model/Customer.java deleted file mode 100644 index 1a66e22d..00000000 --- a/src/main/java/org/example/model/Customer.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.example.model; - -import jakarta.persistence.*; -import java.util.ArrayList; -import java.util.List; - -@Entity -public class Customer { - - @Id - @GeneratedValue - private Long id; - - private String name; - private String email; - - @OneToMany(mappedBy = "customer") - private List bookings = new ArrayList<>(); - - // getters & setters -} diff --git a/src/main/java/org/example/repository/AddonRepository.java b/src/main/java/org/example/repository/AddonRepository.java new file mode 100644 index 00000000..3d8cdf68 --- /dev/null +++ b/src/main/java/org/example/repository/AddonRepository.java @@ -0,0 +1,14 @@ +package org.example.repository; + +import org.example.config.JpaUtil; +import org.example.entity.Addon; +import jakarta.persistence.EntityManager; +import java.util.List; + +public class AddonRepository { + + public List findAll() { + EntityManager em = JpaUtil.getEntityManager(); + return em.createQuery("SELECT a FROM Addon a", Addon.class).getResultList(); + } +} diff --git a/src/main/java/org/example/repository/BookingRepository.java b/src/main/java/org/example/repository/BookingRepository.java index a63b50d4..2091c93e 100644 --- a/src/main/java/org/example/repository/BookingRepository.java +++ b/src/main/java/org/example/repository/BookingRepository.java @@ -1,4 +1,14 @@ package org.example.repository; +import org.example.config.JpaUtil; +import org.example.entity.Booking; +import jakarta.persistence.EntityManager; +import java.util.List; + public class BookingRepository { + + public List findAll() { + EntityManager em = JpaUtil.getEntityManager(); + return em.createQuery("SELECT b FROM Booking b", Booking.class).getResultList(); + } } diff --git a/src/main/java/org/example/repository/CarRepository.java b/src/main/java/org/example/repository/CarRepository.java index 69ce8060..dc626a36 100644 --- a/src/main/java/org/example/repository/CarRepository.java +++ b/src/main/java/org/example/repository/CarRepository.java @@ -1,4 +1,14 @@ package org.example.repository; +import org.example.config.JpaUtil; +import org.example.entity.Car; +import jakarta.persistence.EntityManager; +import java.util.List; + public class CarRepository { + + public List findAll() { + EntityManager em = JpaUtil.getEntityManager(); + return em.createQuery("SELECT c FROM Car c", Car.class).getResultList(); + } } diff --git a/src/main/java/org/example/repository/CustomerRepository.java b/src/main/java/org/example/repository/CustomerRepository.java deleted file mode 100644 index 38b788c2..00000000 --- a/src/main/java/org/example/repository/CustomerRepository.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.example.repository; - -public class CustomerRepository { -} diff --git a/src/main/java/org/example/service/AddonService.java b/src/main/java/org/example/service/AddonService.java new file mode 100644 index 00000000..7773eb83 --- /dev/null +++ b/src/main/java/org/example/service/AddonService.java @@ -0,0 +1,14 @@ +package org.example.service; + +import org.example.entity.Addon; +import org.example.repository.AddonRepository; +import java.util.List; + +public class AddonService { + + private final AddonRepository repo = new AddonRepository(); + + public List getAllAddons() { + return repo.findAll(); + } +} diff --git a/src/main/java/org/example/service/BookingService.java b/src/main/java/org/example/service/BookingService.java index aea99093..ae6fd953 100644 --- a/src/main/java/org/example/service/BookingService.java +++ b/src/main/java/org/example/service/BookingService.java @@ -1,4 +1,88 @@ package org.example.service; +import org.example.entity.Booking; +import org.example.repository.BookingRepository; +import org.example.config.JpaUtil; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityTransaction; +import org.example.entity.Addon; +import org.example.entity.BookingType; +import org.example.entity.Car; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.List; + public class BookingService { + + private final BookingRepository repo = new BookingRepository(); + + public List getAllBookings() { + return repo.findAll(); + } + + public List getBookingsForCar(Car car) { + EntityManager em = JpaUtil.getEntityManager(); + return em.createQuery("SELECT b FROM Booking b WHERE b.car = :car ORDER BY b.startTime", Booking.class) + .setParameter("car", car) + .getResultList(); + } + + public boolean isCarAvailable(Car car, LocalDateTime start, LocalDateTime end) { + List bookings = getBookingsForCar(car); + for (Booking b : bookings) { + if (start.isBefore(b.getEndTime()) && end.isAfter(b.getStartTime())) { + return false; + } + } + return true; + } + + public LocalDateTime getNextAvailableTime(Car car) { + List bookings = getBookingsForCar(car); + LocalDateTime now = LocalDateTime.now(); + LocalDateTime latestEnd = now; + + for (Booking b : bookings) { + if (b.getEndTime().isAfter(latestEnd)) { + if (b.getStartTime().isAfter(latestEnd)) { + // There is a gap + return latestEnd; + } + latestEnd = b.getEndTime(); + } + } + return latestEnd; + } + + public double calculatePrice(Car car, LocalDateTime start, LocalDateTime end, BookingType type, List addons) { + Duration duration = Duration.between(start, end); + double basePrice = 0; + if (type == BookingType.HOURLY) { + basePrice = Math.ceil(duration.toMinutes() / 60.0) * car.getPricePerHour(); + } else { + basePrice = Math.ceil(duration.toHours() / 24.0) * car.getPricePerDay(); + } + + double addonPrice = addons.stream().mapToDouble(Addon::getPrice).sum(); + return basePrice + addonPrice; + } + + public void saveBooking(Booking booking) { + EntityManager em = JpaUtil.getEntityManager(); + EntityTransaction tx = em.getTransaction(); + try { + tx.begin(); + if (booking.getId() == null) { + em.persist(booking); + } else { + em.merge(booking); + } + tx.commit(); + } catch (Exception e) { + if (tx.isActive()) tx.rollback(); + throw e; + } finally { + em.close(); + } + } } diff --git a/src/main/java/org/example/service/CarService.java b/src/main/java/org/example/service/CarService.java index e81e1ea5..f3e98bd7 100644 --- a/src/main/java/org/example/service/CarService.java +++ b/src/main/java/org/example/service/CarService.java @@ -1,4 +1,14 @@ package org.example.service; +import org.example.entity.Car; +import org.example.repository.CarRepository; +import java.util.List; + public class CarService { + + private final CarRepository repo = new CarRepository(); + + public List getAllCars() { + return repo.findAll(); + } } diff --git a/src/main/java/org/example/service/CustomerService.java b/src/main/java/org/example/service/CustomerService.java deleted file mode 100644 index 4c00baea..00000000 --- a/src/main/java/org/example/service/CustomerService.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.example.service; - -public class CustomerService { -} diff --git a/src/main/resources/META-INF/persistence.xml b/src/main/resources/META-INF/persistence.xml new file mode 100644 index 00000000..aaa9a11c --- /dev/null +++ b/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,28 @@ + + + + + + org.hibernate.jpa.HibernatePersistenceProvider + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/fxml/booking-view.fxml b/src/main/resources/fxml/booking-view.fxml new file mode 100644 index 00000000..fd22f679 --- /dev/null +++ b/src/main/resources/fxml/booking-view.fxml @@ -0,0 +1,42 @@ + + + + + + + +