Skip to content
Open
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
1 change: 1 addition & 0 deletions .idea/.name

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 15 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,26 @@ application {
mainClass = 'ap.restaurant.restaurant.HelloApplication'
}

javafx {
version = '17.0.6'
modules = ['javafx.controls', 'javafx.fxml']
}

dependencies {

testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}")

implementation 'org.postgresql:postgresql:42.7.2'
implementation 'org.openjfx:javafx-controls:21.0.2'
implementation 'org.openjfx:javafx-fxml:21.0.2'
}

java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(23))
}
}

javafx {
version = "21.0.2"
modules = ['javafx.controls', 'javafx.fxml']
}

test {
Expand Down
134 changes: 134 additions & 0 deletions src/main/java/ap/restaurant/DB/DatabaseManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package ap.restaurant.DB;

import java.sql.*;

import static ap.restaurant.utils.PasswordUtil.hash;

public class DatabaseManager {
private static final String DB_URL = "jdbc:postgresql://localhost:5432/restaurant_db";
private static final String DB_USER = "postgres";
private static final String DB_PASSWORD = System.getenv("DB_PASSWORD");

public static Connection getConnection() throws SQLException {
if (DB_PASSWORD == null || DB_PASSWORD.isEmpty()) {
throw new SQLException("Database password is not provided. Please set the DB_PASSWORD environment variable.");
}
return DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
}

public static void createTables() throws SQLException {
String[] sqls = {
"CREATE TABLE IF NOT EXISTS \"User\" (" +
"id SERIAL PRIMARY KEY, " +
"username VARCHAR(100) UNIQUE NOT NULL, " +
"email VARCHAR(100) UNIQUE NOT NULL, " +
"password VARCHAR(255) NOT NULL" +
")",
"CREATE TABLE IF NOT EXISTS MenuItem (" +
"id SERIAL PRIMARY KEY, " +
"name VARCHAR(100) NOT NULL UNIQUE, " +
"description TEXT, " +
"price DECIMAL(10,2) NOT NULL, " +
"category VARCHAR(50)" +
")",
"CREATE TABLE IF NOT EXISTS \"Order\" (" +
"id SERIAL PRIMARY KEY, " +
"user_id INTEGER REFERENCES \"User\"(id) ON DELETE SET NULL, " +
"total_price DECIMAL(10,2) NOT NULL, " +
"order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP" +
")",
"CREATE TABLE IF NOT EXISTS OrderDetail (" +
"id SERIAL PRIMARY KEY, " +
"order_id INTEGER REFERENCES \"Order\"(id) ON DELETE CASCADE, " +
"menu_item_id INTEGER REFERENCES MenuItem(id) ON DELETE SET NULL, " +
"quantity INTEGER NOT NULL, " +
"subtotal DECIMAL(10,2) NOT NULL" +
")"
};

try (Connection conn = getConnection(); Statement stmt = conn.createStatement()) {
for (String sql : sqls) {
stmt.executeUpdate(sql);
}
stmt.executeUpdate("INSERT INTO MenuItem (name, description, price, category) VALUES " +
"('Kebab', 'Grilled lamb kebab with spices', 12.50, 'Main Course'), " +
"('Fesenjan', 'Chicken stew with pomegranate and walnuts', 15.00, 'Traditional'), " +
"('Ghormeh Sabzi', 'Herb stew with beef', 13.75, 'Traditional') " +
"ON CONFLICT (name) DO NOTHING");
}
}

public static void saveUser(String username, String email, String password) throws SQLException {
String sql = "INSERT INTO \"User\" (username, email, password) VALUES (?, ?, ?)";
try (Connection conn = getConnection(); PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, username);
stmt.setString(2, email);
stmt.setString(3, hash(password));
stmt.executeUpdate();
}
}

public static boolean authenticateUser(String email, String password) throws SQLException {
String sql = "SELECT COUNT(*) FROM \"User\" WHERE email = ? AND password = ?";
try (Connection conn = getConnection(); PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, email);
stmt.setString(2, hash(password));
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
return rs.getInt(1) > 0;
}
}
}
return false;
}

public static ResultSet getMenuItems() throws SQLException {
String sql = "SELECT id, name, price FROM MenuItem";
Connection conn = getConnection();
try {
PreparedStatement stmt = conn.prepareStatement(sql);
return stmt.executeQuery();
} catch (SQLException e) {
conn.close();
throw e;
}
}

public static int saveOrder(int userId, double totalPrice) throws SQLException {
String sql = "INSERT INTO \"Order\" (user_id, total_price, order_date) VALUES (?, ?, ?) RETURNING id";
try (Connection conn = getConnection(); PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, userId);
stmt.setDouble(2, totalPrice);
stmt.setTimestamp(3, new java.sql.Timestamp(System.currentTimeMillis()));
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
return rs.getInt("id");
}
throw new SQLException("Failed to retrieve order ID");
}
}

public static void saveOrderDetail(int orderId, int menuItemId, int quantity, double subtotal) throws SQLException {
String sql = "INSERT INTO OrderDetail (order_id, menu_item_id, quantity, subtotal) VALUES (?, ?, ?, ?)";
try (Connection conn = getConnection(); PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, orderId);
stmt.setInt(2, menuItemId);
stmt.setInt(3, quantity);
stmt.setDouble(4, subtotal);
stmt.executeUpdate();
}
}

public static int getUserIdFromEmail(String email) throws SQLException {
String sql = "SELECT id FROM \"User\" WHERE email = ?";
try (Connection conn = getConnection(); PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, email);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
return rs.getInt("id");
}
}
}
return 0;
}
}
58 changes: 58 additions & 0 deletions src/main/java/ap/restaurant/app/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package ap.restaurant.app;

import ap.restaurant.DB.DatabaseManager;
import ap.restaurant.controllers.auth.SignInController;
import ap.restaurant.utils.AlertUtil;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.io.IOException;
import java.sql.SQLException;
import java.util.Objects;

public class Main extends Application {

@Override
public void start(Stage primaryStage) {
try {
try {
DatabaseManager.createTables();
} catch (SQLException e) {
AlertUtil.showError("Failed to initialize database: " + e.getMessage() +
"\nPlease ensure PostgreSQL is running and DB_PASSWORD is set.");
return;
}

FXMLLoader loader = new FXMLLoader(getClass().getResource("/ap/restaurant/fxml/SignIn.fxml"));
Scene scene = new Scene(loader.load());

SignInController controller = loader.getController();
if (controller == null) {
throw new RuntimeException("SignInController is null. Check fx:controller attribute in SignIn.fxml.");
}

primaryStage.setTitle("SBU RESTAURANT");
primaryStage.setScene(scene);
primaryStage.setResizable(true);
primaryStage.getIcons().add(
new javafx.scene.image.Image(Objects.requireNonNull(getClass().getResourceAsStream("/pics/Sbu-logo.svg.png")))
);
primaryStage.show();

} catch (IOException e) {
System.err.println("Error loading SignIn.fxml: " + e.getMessage());
e.printStackTrace();
AlertUtil.showError("Failed to load the Sign In page. Please check the application resources and try again.");
} catch (Exception e) {
System.err.println("Unexpected error during application startup: " + e.getMessage());
e.printStackTrace();
AlertUtil.showError("An unexpected error occurred during startup: " + e.getMessage());
}
}

public static void main(String[] args) {
launch(args);
}
}
118 changes: 118 additions & 0 deletions src/main/java/ap/restaurant/controllers/CheckoutController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package ap.restaurant.controllers;

import ap.restaurant.utils.AlertUtil;
import ap.restaurant.utils.SceneUtil;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;

import java.sql.SQLException;

public class CheckoutController {
@FXML
private TableView<MenuController.CartItem> receiptTable;
@FXML
private TableColumn<MenuController.CartItem, String> nameCol;
@FXML
private TableColumn<MenuController.CartItem, Integer> qtyCol;
@FXML
private TableColumn<MenuController.CartItem, Double> priceCol;
@FXML
private TableColumn<MenuController.CartItem, Double> subtotalCol;
@FXML
private Label totalPriceLabel;
@FXML
private Label confirmationLabel;
@FXML
private Button placeOrderButton;
@FXML
private Button orderAgainButton;
@FXML
private Button logoutButton;

private ObservableList<MenuController.CartItem> cartItems;
private int userId;

public void setCartItems(ObservableList<MenuController.CartItem> cartItems) {
this.cartItems = cartItems;
initializeTable();
}

public void setUserId(int userId) {
this.userId = userId;
}

@FXML
private void initialize() {
nameCol.setCellValueFactory(cell ->
new javafx.beans.value.ObservableValueBase<String>() {
@Override public String getValue() {
return cell.getValue().getMenuItem().getName();
}
});
qtyCol.setCellValueFactory(cell ->
new javafx.beans.value.ObservableValueBase<Integer>() {
@Override public Integer getValue() {
return cell.getValue().getQuantity();
}
});
priceCol.setCellValueFactory(cell ->
new javafx.beans.value.ObservableValueBase<Double>() {
@Override public Double getValue() {
return cell.getValue().getMenuItem().getPrice();
}
});
subtotalCol.setCellValueFactory(cell ->
new javafx.beans.value.ObservableValueBase<Double>() {
@Override public Double getValue() {
return cell.getValue().getSubtotal();
}
});
if (cartItems != null) {
initializeTable();
}
}

private void initializeTable() {
receiptTable.setItems(cartItems);
double total = 0;
for (MenuController.CartItem item : cartItems) {
total += item.getSubtotal();
}
totalPriceLabel.setText("Total: " + String.format("%.2f", total) + " Toman");
}

@FXML
private void onPlaceOrderClick() {
if (userId == 0) {
AlertUtil.showError("User not logged in.");
return;
}
try {
double total = 0;
for (MenuController.CartItem item : cartItems) {
total += item.getSubtotal();
}
int orderId = ap.restaurant.DB.DatabaseManager.saveOrder(userId, total);
for (MenuController.CartItem item : cartItems) {
ap.restaurant.DB.DatabaseManager.saveOrderDetail(orderId, item.getMenuItem().getId(), item.getQuantity(), item.getSubtotal());
}
confirmationLabel.setText("Purchase done!");
} catch (SQLException e) {
AlertUtil.showError("Order failed.");
}
}

@FXML
private void onOrderAgainClick() {
SceneUtil.changeSceneWithSameSize(orderAgainButton, "/ap/restaurant/fxml/Menu.fxml");
}

@FXML
private void onLogoutClick() {
SceneUtil.changeSceneWithSameSize(logoutButton, "/ap/restaurant/fxml/SignIn.fxml");
}
}
Loading