diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..fe4eec9
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/BSK.db b/BSK.db
new file mode 100644
index 0000000..e69de29
diff --git a/pom.xml b/pom.xml
index 0e408ff..3a52d4c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -99,5 +99,10 @@
slf4j-api
2.0.16
+
+ org.xerial
+ sqlite-jdbc
+ 3.47.1.0
+
diff --git a/sample.db b/sample.db
new file mode 100644
index 0000000..1c765ae
Binary files /dev/null and b/sample.db differ
diff --git a/src/main/java/BsK/client/network/handler/ClientHandler.java b/src/main/java/BsK/client/network/handler/ClientHandler.java
index def114b..f8c9f49 100644
--- a/src/main/java/BsK/client/network/handler/ClientHandler.java
+++ b/src/main/java/BsK/client/network/handler/ClientHandler.java
@@ -1,5 +1,6 @@
package BsK.client.network.handler;
+
import BsK.client.ui.handler.UIHandler;
import BsK.common.packet.Packet;
import BsK.common.packet.PacketSerializer;
@@ -14,35 +15,84 @@ import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
import lombok.extern.slf4j.Slf4j;
+import org.w3c.dom.Text;
@Slf4j
public class ClientHandler extends SimpleChannelInboundHandler {
public static ChannelHandlerContext ctx;
+ public static TextWebSocketFrame frame;
public static final ClientHandler INSTANCE = new ClientHandler();
+
+ private static final Map, List>> listeners = new HashMap<>();
+
+ public static void addResponseListener(Class responseType, ResponseListener listener) {
+ listeners.computeIfAbsent(responseType, k -> new ArrayList<>()).add(listener);
+ }
+
+ public static void removeResponseListener(Class responseType, ResponseListener listener) {
+ var listenerList = listeners.get(responseType);
+ if (listenerList != null) {
+ listenerList.remove(listener);
+ }
+ }
+
+// @Override
+// protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame frame) {
+// log.debug("Received message: {}", frame.text());
+// var packet = PacketSerializer.GSON.fromJson(frame.text(), Packet.class);
+// switch (packet) {
+// case HandshakeCompleteResponse handshakeCompleteResponse -> {
+// log.info("Handshake complete");
+// this.ctx = ctx;
+// this.frame = frame;
+// }
+// case ErrorResponse response -> {
+// log.error("Received error response: {}", response.getError());
+// switch (response.getError()) {
+// case USER_ALREADY_EXISTS -> log.error("User already exists");
+// default -> log.error("Received error response: {}", response.getError());
+// }
+// }
+// case null, default -> log.warn("Unknown message: {}", frame.text());
+// }
+// }
+
@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame frame) {
log.debug("Received message: {}", frame.text());
var packet = PacketSerializer.GSON.fromJson(frame.text(), Packet.class);
- switch (packet) {
- case HandshakeCompleteResponse handshakeCompleteResponse -> {
+
+ if (packet != null) {
+ // Handle HandshakeCompleteResponse to initialize ctx and frame
+ if (packet instanceof HandshakeCompleteResponse handshakeResponse) {
log.info("Handshake complete");
- this.ctx = ctx;
-// var login = new LoginRequest("user", "user");
-// NetworkUtil.sendPacket(ctx.channel(), login);
+ ClientHandler.ctx = ctx;
+ ClientHandler.frame = frame;
+ return; // No further processing needed for handshake response
}
- case ErrorResponse response -> {
- log.error("Received error response: {}", response.getError());
- switch (response.getError()) {
- case USER_ALREADY_EXISTS -> log.error("User already exists");
- default -> log.error("Received error response: {}", response.getError());
+
+
+ // Process other packets
+ List> responseListeners = listeners.get(packet.getClass());
+ if (responseListeners != null) {
+ for (ResponseListener> listener : responseListeners) {
+ notifyListener(listener, packet);
}
+ } else {
+ log.warn("No listeners registered for response type: {}", packet.getClass().getName());
}
- case LoginSuccessResponse loginSuccessResponse -> log.info("Received login success response");
- case null, default -> log.warn("Unknown message: {}", frame.text());
+ } else {
+ log.warn("Unknown or null packet received: {}", frame.text());
}
}
+
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
@@ -60,4 +110,14 @@ public class ClientHandler extends SimpleChannelInboundHandler void notifyListener(ResponseListener> listener, T response) {
+ try {
+ ((ResponseListener) listener).onResponse(response);
+ } catch (ClassCastException e) {
+ log.error("Listener type mismatch for response: {}", response.getClass(), e);
+ }
+ }
+
}
diff --git a/src/main/java/BsK/client/network/handler/ResponseListener.java b/src/main/java/BsK/client/network/handler/ResponseListener.java
new file mode 100644
index 0000000..d86ca20
--- /dev/null
+++ b/src/main/java/BsK/client/network/handler/ResponseListener.java
@@ -0,0 +1,5 @@
+package BsK.client.network.handler;
+
+public interface ResponseListener {
+ void onResponse(T response);
+}
diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java b/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java
new file mode 100644
index 0000000..d91c003
--- /dev/null
+++ b/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java
@@ -0,0 +1,218 @@
+package BsK.client.ui.component.CheckUpPage;
+
+import BsK.client.ui.component.MainFrame;
+import BsK.client.ui.component.common.RoundedButtonUI;
+import BsK.client.ui.component.common.RoundedPanel;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class CheckUpPage extends JPanel {
+ private boolean debugMode = false; // Enable debug mode
+
+ public CheckUpPage(MainFrame mainFrame) {
+ setLayout(new BorderLayout());
+
+ // Sidebar panel
+ JPanel sidebar = new JPanel() {
+ @Override
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ Graphics2D g2d = (Graphics2D) g;
+ int width = getWidth();
+ int height = getHeight();
+ Color color1 = new Color(63, 81, 181);
+ Color color2 = new Color(33, 150, 243);
+ GradientPaint gp = new GradientPaint(0, 0, color1, 0, height, color2);
+ g2d.setPaint(gp);
+ g2d.fillRect(0, 0, width, height);
+
+ if (debugMode) {
+ drawBoundingBox(g2d, this);
+ }
+ }
+ };
+ sidebar.setBackground(new Color(63, 81, 181));
+ sidebar.setLayout(new GridLayout(15, 1));
+ sidebar.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
+
+ String[] sidebarItems = {"Thống kê","Thăm khám", "Dữ liệu bệnh nhân", "Kho", "Người dùng", "Thông tin"};
+ String[] destination = {"DashboardPage","CheckUpPage", "PatientDataPage", "InventoryPage", "UserPage", "InfoPage"};
+ for(int i = 0; i < sidebarItems.length; i++) {
+ String item = sidebarItems[i];
+ String dest = destination[i];
+ JLabel label = new JLabel(item);
+ label.setForeground(Color.WHITE);
+ label.setHorizontalAlignment(SwingConstants.CENTER);
+ label.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); // Change cursor to hand
+ label.setFont(new Font("Arial", Font.BOLD, 14));
+
+ // Add a mouse listener to handle click events
+ label.addMouseListener(new java.awt.event.MouseAdapter() {
+ @Override
+ public void mouseClicked(java.awt.event.MouseEvent e) {
+ // Handle the click event
+ mainFrame.showPage(dest);
+ // JOptionPane.showMessageDialog(null, item + " clicked!");
+ }
+
+ @Override
+ public void mouseEntered(java.awt.event.MouseEvent e) {
+ label.setForeground(new Color(200, 230, 255)); // Highlight on hover
+ }
+
+ @Override
+ public void mouseExited(java.awt.event.MouseEvent e) {
+ label.setForeground(Color.WHITE); // Restore original color
+ }
+ });
+
+ sidebar.add(label);
+ }
+
+ // Make the sidebar scrollable
+ JScrollPane sidebarScrollPane = new JScrollPane(sidebar);
+ sidebarScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
+ sidebarScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+ sidebarScrollPane.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
+ sidebarScrollPane.getViewport().setOpaque(false);
+ sidebarScrollPane.setOpaque(false);
+
+ // Topbar panel
+ JPanel topbar = new JPanel() {
+ @Override
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ if (debugMode) {
+ drawBoundingBox((Graphics2D) g, this);
+ }
+ }
+ };
+ topbar.setLayout(new BorderLayout());
+ topbar.setBackground(Color.WHITE);
+ topbar.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
+
+ JLabel title = new JLabel("Check Up");
+ title.setFont(new Font("Arial", Font.BOLD, 18));
+ topbar.add(title, BorderLayout.WEST);
+
+ JLabel userInfo = new JLabel("User Name (Admin)");
+ userInfo.setHorizontalAlignment(SwingConstants.RIGHT);
+ topbar.add(userInfo, BorderLayout.EAST);
+
+ // Main content panel
+ JPanel mainContent = new JPanel() {
+ @Override
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ if (debugMode) {
+ drawBoundingBox((Graphics2D) g, this);
+ }
+ }
+ };
+ mainContent.setLayout(new BorderLayout());
+ mainContent.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
+
+ // Data table inside a RoundedPanel
+ RoundedPanel dataTable1 = new RoundedPanel(20, Color.WHITE, false) {
+ @Override
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ if (debugMode) {
+ drawBoundingBox((Graphics2D) g, this);
+ }
+ }
+ };
+
+ RoundedPanel dataTable2 = new RoundedPanel(20, Color.WHITE, false) {
+ @Override
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ if (debugMode) {
+ drawBoundingBox((Graphics2D) g, this);
+ }
+ }
+ };
+
+ JLabel titleText1 = new JLabel();
+ titleText1.setText("Check Up Queue 1");
+ titleText1.setFont(new Font("Arial", Font.BOLD, 16));
+ titleText1.setBackground(Color.WHITE);
+ titleText1.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); // Padding
+
+ JLabel titleText2 = new JLabel();
+ titleText2.setText("Check Up Queue 2");
+ titleText2.setFont(new Font("Arial", Font.BOLD, 16));
+ titleText2.setBackground(Color.WHITE);
+ titleText2.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); // Padding
+
+ dataTable1.setLayout(new BorderLayout());
+ dataTable1.add(titleText1, BorderLayout.NORTH);
+ dataTable1.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 10)); // Adjust padding for better appearance
+
+ dataTable2.setLayout(new BorderLayout());
+ dataTable2.add(titleText2, BorderLayout.NORTH);
+ dataTable2.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 10)); // Adjust padding for better appearance
+
+ String[] columns = {"Name", "Gender", "Course"};
+ String[][] data = {
+ {"John", "Male", "Java"},
+ {"Dara", "Male", "C++"},
+ {"Bora", "Male", "C#"},
+ };
+ JTable table1 = new JTable(data, columns);
+ JScrollPane tableScroll1 = new JScrollPane(table1);
+ tableScroll1.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); // Inner padding
+ dataTable1.add(tableScroll1, BorderLayout.CENTER);
+
+ JTable table2 = new JTable(data, columns);
+ JScrollPane tableScroll2 = new JScrollPane(table2);
+ tableScroll2.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); // Inner padding
+ dataTable2.add(tableScroll2, BorderLayout.CENTER);
+
+ UIManager.getDefaults().put("SplitPane.border", BorderFactory.createEmptyBorder()); // Remove border
+ JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, dataTable1, dataTable2);
+ splitPane.setResizeWeight(0.5); // Split
+
+ // Add the split pane to the main content
+ mainContent.add(splitPane, BorderLayout.CENTER);
+ // Notice board
+ JPanel noticeBoard = new RoundedPanel(20, Color.WHITE, false) {
+ @Override
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ if (debugMode) {
+ drawBoundingBox((Graphics2D) g, this);
+ }
+ }
+ };
+ noticeBoard.setLayout(new BorderLayout());
+ noticeBoard.setBorder(BorderFactory.createTitledBorder("Notice Board"));
+ noticeBoard.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
+
+ JTextArea noticeText = new JTextArea("04/10/2021\nHidemode Now\n\n03/10/2021\nFurther Reading\n");
+ noticeText.setEditable(false);
+ noticeBoard.add(new JScrollPane(noticeText), BorderLayout.CENTER);
+
+ //mainContent.add(noticeBoard, BorderLayout.EAST);
+
+ // Adding panels to the frame
+ add(sidebarScrollPane, BorderLayout.WEST);
+ add(topbar, BorderLayout.NORTH);
+ add(mainContent, BorderLayout.CENTER);
+ }
+
+ private void drawBoundingBox(Graphics2D g2d, JComponent component) {
+ g2d.setColor(Color.RED);
+ g2d.setStroke(new BasicStroke(2));
+ g2d.drawRect(0, 0, component.getWidth() - 1, component.getHeight() - 1);
+ }
+
+ public static void main(String[] args) {
+// JFrame frame = new JFrame("Dashboard");
+// frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+// frame.setSize(800, 600);
+// frame.add(new CheckUpPage();
+// frame.setVisible(true);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/BsK/client/ui/component/DashboardPage/DashboardPage.java b/src/main/java/BsK/client/ui/component/DashboardPage/DashboardPage.java
new file mode 100644
index 0000000..ed8ccef
--- /dev/null
+++ b/src/main/java/BsK/client/ui/component/DashboardPage/DashboardPage.java
@@ -0,0 +1,179 @@
+package BsK.client.ui.component.DashboardPage;
+
+import BsK.client.ui.component.MainFrame;
+import BsK.client.ui.component.common.RoundedButtonUI;
+import BsK.client.ui.component.common.RoundedPanel;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class DashboardPage extends JPanel {
+ public DashboardPage(MainFrame mainFrame) {
+ setLayout(new BorderLayout());
+
+ // Sidebar panel
+ JPanel sidebar = new JPanel() {
+ @Override
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ Graphics2D g2d = (Graphics2D) g;
+ int width = getWidth();
+ int height = getHeight();
+ Color color1 = new Color(63, 81, 181);
+ Color color2 = new Color(33, 150, 243);
+ GradientPaint gp = new GradientPaint(0, 0, color1, 0, height, color2);
+ g2d.setPaint(gp);
+ g2d.fillRect(0, 0, width, height);
+ }
+ };
+ sidebar.setBackground(new Color(63, 81, 181));
+ sidebar.setLayout(new GridLayout(15, 1));
+ sidebar.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
+
+ String[] sidebarItems = {"Thống kê","Thăm khám", "Dữ liệu bệnh nhân", "Kho", "Người dùng", "Thông tin"};
+ String[] destination = {"DashboardPage","CheckUpPage", "PatientDataPage", "InventoryPage", "UserPage", "InfoPage"};
+ for(int i = 0; i < sidebarItems.length; i++) {
+ String item = sidebarItems[i];
+ String dest = destination[i];
+ JLabel label = new JLabel(item);
+ label.setForeground(Color.WHITE);
+ label.setHorizontalAlignment(SwingConstants.CENTER);
+ label.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); // Change cursor to hand
+ label.setFont(new Font("Arial", Font.BOLD, 14));
+
+ // Add a mouse listener to handle click events
+ label.addMouseListener(new java.awt.event.MouseAdapter() {
+ @Override
+ public void mouseClicked(java.awt.event.MouseEvent e) {
+ // Handle the click event
+ mainFrame.showPage(dest);
+
+ // JOptionPane.showMessageDialog(null, item + " clicked!");
+ }
+
+ @Override
+ public void mouseEntered(java.awt.event.MouseEvent e) {
+ label.setForeground(new Color(200, 230, 255)); // Highlight on hover
+ }
+
+ @Override
+ public void mouseExited(java.awt.event.MouseEvent e) {
+ label.setForeground(Color.WHITE); // Restore original color
+ }
+ });
+
+ sidebar.add(label);
+ }
+
+// Make the sidebar scrollable
+ JScrollPane sidebarScrollPane = new JScrollPane(sidebar);
+ sidebarScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
+ sidebarScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+ sidebarScrollPane.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
+ sidebarScrollPane.getViewport().setOpaque(false);
+ sidebarScrollPane.setOpaque(false);
+
+
+
+ // Topbar panel
+ JPanel topbar = new JPanel();
+ topbar.setLayout(new BorderLayout());
+ topbar.setBackground(Color.WHITE);
+ topbar.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
+
+ JLabel title = new JLabel("Dashboard / Home");
+ title.setFont(new Font("Arial", Font.BOLD, 18));
+ topbar.add(title, BorderLayout.WEST);
+
+ JLabel userInfo = new JLabel("User Name (Admin)");
+ userInfo.setHorizontalAlignment(SwingConstants.RIGHT);
+ topbar.add(userInfo, BorderLayout.EAST);
+
+ // Main content panel
+ JPanel mainContent = new JPanel();
+ mainContent.setLayout(new BorderLayout());
+ mainContent.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
+
+ // Top cards
+ JPanel topCards = new JPanel();
+ topCards.setLayout(new GridLayout(1, 4, 20, 20));
+ String[] cardTitles = {"Add Check", "Income", "Expense", "Other Income"};
+ Color[] cardColors = {Color.PINK, Color.CYAN, Color.ORANGE, Color.GREEN};
+
+ for (int i = 0; i < cardTitles.length; i++) {
+ JPanel card = new RoundedPanel(20, cardColors[i], true);
+ card.setBackground(cardColors[i]);
+ card.setLayout(new BorderLayout());
+ card.setBorder(BorderFactory.createEmptyBorder(30, 20, 30, 20));
+
+ JLabel cardTitle = new JLabel(cardTitles[i]);
+ cardTitle.setForeground(Color.WHITE);
+ cardTitle.setHorizontalAlignment(SwingConstants.CENTER);
+ card.add(cardTitle, BorderLayout.NORTH);
+
+ JLabel cardValue = new JLabel("Value", SwingConstants.CENTER);
+ cardValue.setForeground(Color.WHITE);
+ cardValue.setFont(new Font("Arial", Font.BOLD, 16));
+ card.add(cardValue, BorderLayout.CENTER);
+
+ topCards.add(card);
+ }
+
+ mainContent.add(topCards, BorderLayout.NORTH);
+
+ // Wrapper panel for spacing
+ JPanel wrapperPanel = new JPanel();
+ wrapperPanel.setLayout(new BorderLayout());
+ wrapperPanel.setBorder(BorderFactory.createEmptyBorder(20, 0, 0, 0)); // Add spacing above
+ wrapperPanel.setOpaque(false); // Ensure transparency for background compatibility
+
+// Data table inside a RoundedPanel
+ RoundedPanel dataTable = new RoundedPanel(20, Color.WHITE, false);
+ dataTable.setLayout(new BorderLayout());
+ dataTable.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); // Adjust padding for better appearance
+
+ String[] columns = {"Name", "Gender", "Course", "Fees", "Action"};
+ String[][] data = {
+ {"John", "Male", "Java", "$300", "Edit/Delete"},
+ {"Dara", "Male", "C++", "$300", "Edit/Delete"},
+ {"Bora", "Male", "C#", "$300", "Edit/Delete"},
+ };
+ JTable table = new JTable(data, columns);
+ JScrollPane tableScroll = new JScrollPane(table);
+ tableScroll.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); // Inner padding
+ dataTable.add(tableScroll, BorderLayout.CENTER);
+
+// Add RoundedPanel to the wrapper panel
+ wrapperPanel.add(dataTable, BorderLayout.CENTER);
+
+// Add the wrapper panel to the main content
+ mainContent.add(wrapperPanel, BorderLayout.CENTER);
+
+
+
+ // Notice board
+ JPanel noticeBoard = new RoundedPanel(20, Color.WHITE, false);
+ noticeBoard.setLayout(new BorderLayout());
+ noticeBoard.setBorder(BorderFactory.createTitledBorder("Notice Board"));
+ noticeBoard.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
+
+ JTextArea noticeText = new JTextArea("04/10/2021\nHidemode Now\n\n03/10/2021\nFurther Reading\n");
+ noticeText.setEditable(false);
+ noticeBoard.add(new JScrollPane(noticeText), BorderLayout.CENTER);
+
+ //mainContent.add(noticeBoard, BorderLayout.EAST);
+
+ // Adding panels to the frame
+ add(sidebarScrollPane, BorderLayout.WEST);
+ add(topbar, BorderLayout.NORTH);
+ add(mainContent, BorderLayout.CENTER);
+ }
+
+ public static void main(String[] args) {
+// JFrame frame = new JFrame("Dashboard");
+// frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+// frame.setSize(800, 600);
+// frame.add(new DashboardPage());
+// frame.setVisible(true);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/BsK/client/ui/component/DashboardPage/SchoolUI.java b/src/main/java/BsK/client/ui/component/DashboardPage/SchoolUI.java
new file mode 100644
index 0000000..177b6d4
--- /dev/null
+++ b/src/main/java/BsK/client/ui/component/DashboardPage/SchoolUI.java
@@ -0,0 +1,175 @@
+package BsK.client.ui.component.DashboardPage;
+
+import BsK.client.ui.component.common.RoundedPanel;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class SchoolUI {
+
+ public static void main(String[] args) {
+ // Create the main frame
+ JFrame frame = new JFrame("School UI");
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ frame.setSize(1200, 800);
+
+ // Main layout
+ frame.setLayout(new BorderLayout());
+
+ // Sidebar panel
+ JPanel sidebar = new JPanel() {
+ @Override
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ Graphics2D g2d = (Graphics2D) g;
+ int width = getWidth();
+ int height = getHeight();
+ Color color1 = new Color(63, 81, 181);
+ Color color2 = new Color(33, 150, 243);
+ GradientPaint gp = new GradientPaint(0, 0, color1, 0, height, color2);
+ g2d.setPaint(gp);
+ g2d.fillRect(0, 0, width, height);
+ }
+ };
+ sidebar.setBackground(new Color(63, 81, 181));
+ sidebar.setLayout(new GridLayout(15, 1));
+ sidebar.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
+
+ String[] sidebarItems = {"Dashboard", "Charts", "Report", "Message", "Staff", "Student", "Library", "Holiday", "Calendar", "Chat App", "Contact", "File Manager", "Our Centres", "Gallery"};
+
+ for (String item : sidebarItems) {
+ JLabel label = new JLabel(item);
+ label.setForeground(Color.WHITE);
+ label.setHorizontalAlignment(SwingConstants.CENTER);
+ label.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); // Change cursor to hand
+ label.setFont(new Font("Arial", Font.BOLD, 14));
+
+ // Add a mouse listener to handle click events
+ label.addMouseListener(new java.awt.event.MouseAdapter() {
+ @Override
+ public void mouseClicked(java.awt.event.MouseEvent e) {
+ // Handle the click event
+ System.out.println(item + " clicked!");
+ JOptionPane.showMessageDialog(null, item + " clicked!");
+ }
+
+ @Override
+ public void mouseEntered(java.awt.event.MouseEvent e) {
+ label.setForeground(new Color(200, 230, 255)); // Highlight on hover
+ }
+
+ @Override
+ public void mouseExited(java.awt.event.MouseEvent e) {
+ label.setForeground(Color.WHITE); // Restore original color
+ }
+ });
+
+ sidebar.add(label);
+ }
+
+// Make the sidebar scrollable
+ JScrollPane sidebarScrollPane = new JScrollPane(sidebar);
+ sidebarScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
+ sidebarScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+ sidebarScrollPane.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
+ sidebarScrollPane.getViewport().setOpaque(false);
+ sidebarScrollPane.setOpaque(false);
+
+
+
+ // Topbar panel
+ JPanel topbar = new JPanel();
+ topbar.setLayout(new BorderLayout());
+ topbar.setBackground(Color.WHITE);
+ topbar.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
+
+ JLabel title = new JLabel("Dashboard / Home");
+ title.setFont(new Font("Arial", Font.BOLD, 18));
+ topbar.add(title, BorderLayout.WEST);
+
+ JLabel userInfo = new JLabel("User Name (Admin)");
+ userInfo.setHorizontalAlignment(SwingConstants.RIGHT);
+ topbar.add(userInfo, BorderLayout.EAST);
+
+ // Main content panel
+ JPanel mainContent = new JPanel();
+ mainContent.setLayout(new BorderLayout());
+ mainContent.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
+
+ // Top cards
+ JPanel topCards = new JPanel();
+ topCards.setLayout(new GridLayout(1, 4, 20, 20));
+ String[] cardTitles = {"New Student", "Income", "Expense", "Other Income"};
+ Color[] cardColors = {Color.PINK, Color.CYAN, Color.ORANGE, Color.GREEN};
+
+ for (int i = 0; i < cardTitles.length; i++) {
+ JPanel card = new RoundedPanel(20, cardColors[i], true);
+ card.setBackground(cardColors[i]);
+ card.setLayout(new BorderLayout());
+ card.setBorder(BorderFactory.createEmptyBorder(30, 20, 30, 20));
+
+ JLabel cardTitle = new JLabel(cardTitles[i]);
+ cardTitle.setForeground(Color.WHITE);
+ cardTitle.setHorizontalAlignment(SwingConstants.CENTER);
+ card.add(cardTitle, BorderLayout.NORTH);
+
+ JLabel cardValue = new JLabel("Value", SwingConstants.CENTER);
+ cardValue.setForeground(Color.WHITE);
+ cardValue.setFont(new Font("Arial", Font.BOLD, 16));
+ card.add(cardValue, BorderLayout.CENTER);
+
+ topCards.add(card);
+ }
+
+ mainContent.add(topCards, BorderLayout.NORTH);
+
+ // Wrapper panel for spacing
+ JPanel wrapperPanel = new JPanel();
+ wrapperPanel.setLayout(new BorderLayout());
+ wrapperPanel.setBorder(BorderFactory.createEmptyBorder(20, 0, 0, 0)); // Add spacing above
+ wrapperPanel.setOpaque(false); // Ensure transparency for background compatibility
+
+// Data table inside a RoundedPanel
+ RoundedPanel dataTable = new RoundedPanel(20, Color.WHITE, false);
+ dataTable.setLayout(new BorderLayout());
+ dataTable.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); // Adjust padding for better appearance
+
+ String[] columns = {"Name", "Gender", "Course", "Fees", "Action"};
+ String[][] data = {
+ {"John", "Male", "Java", "$300", "Edit/Delete"},
+ {"Dara", "Male", "C++", "$300", "Edit/Delete"},
+ {"Bora", "Male", "C#", "$300", "Edit/Delete"},
+ };
+ JTable table = new JTable(data, columns);
+ JScrollPane tableScroll = new JScrollPane(table);
+ tableScroll.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); // Inner padding
+ dataTable.add(tableScroll, BorderLayout.CENTER);
+
+// Add RoundedPanel to the wrapper panel
+ wrapperPanel.add(dataTable, BorderLayout.CENTER);
+
+// Add the wrapper panel to the main content
+ mainContent.add(wrapperPanel, BorderLayout.CENTER);
+
+
+
+ // Notice board
+ JPanel noticeBoard = new RoundedPanel(20, Color.WHITE, false);
+ noticeBoard.setLayout(new BorderLayout());
+ noticeBoard.setBorder(BorderFactory.createTitledBorder("Notice Board"));
+ noticeBoard.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
+
+ JTextArea noticeText = new JTextArea("04/10/2021\nHidemode Now\n\n03/10/2021\nFurther Reading\n");
+ noticeText.setEditable(false);
+ noticeBoard.add(new JScrollPane(noticeText), BorderLayout.CENTER);
+
+ //mainContent.add(noticeBoard, BorderLayout.EAST);
+
+ // Adding panels to the frame
+ frame.add(sidebarScrollPane, BorderLayout.WEST);
+ frame.add(topbar, BorderLayout.NORTH);
+ frame.add(mainContent, BorderLayout.CENTER);
+
+ frame.setVisible(true);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/BsK/client/ui/component/LandingPage/LandingPage.java b/src/main/java/BsK/client/ui/component/LandingPage/LandingPage.java
new file mode 100644
index 0000000..908b12b
--- /dev/null
+++ b/src/main/java/BsK/client/ui/component/LandingPage/LandingPage.java
@@ -0,0 +1,134 @@
+package BsK.client.ui.component.LandingPage;
+
+import BsK.client.ui.component.MainFrame;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+public class LandingPage extends JPanel {
+ public LandingPage(MainFrame mainFrame) {
+ setLayout(new BorderLayout());
+
+ // Left and middle panel with background image and tint
+ JPanel leftMiddlePanel = new JPanel() {
+ @Override
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ Graphics2D g2d = (Graphics2D) g;
+ ImageIcon icon = new ImageIcon("/Users/lethanhdat/Documents/GitHub/ldS_BsK/src/main/java/BsK/client/ui/image/landingpagebg.jpeg");
+ Image image = icon.getImage();
+ g2d.drawImage(image, 0, 0, getWidth(), getHeight(), this);
+ Color tint = new Color(0, 0, 0, 100); // Black tint with transparency
+ g2d.setColor(tint);
+ g2d.fillRect(0, 0, getWidth(), getHeight());
+ }
+ };
+ leftMiddlePanel.setLayout(new GridBagLayout());
+ GridBagConstraints gbc = new GridBagConstraints();
+ gbc.insets = new Insets(10, 10, 10, 10);
+ gbc.gridx = 0;
+ gbc.gridy = 0;
+
+ JLabel titleLabel = new JLabel("BSK");
+ titleLabel.setForeground(Color.WHITE);
+ titleLabel.setFont(new Font("Arial", Font.BOLD, 50));
+ leftMiddlePanel.add(titleLabel, gbc);
+
+ gbc.gridy++;
+ JLabel authorLabel = new JLabel("by lds217 and miti99");
+ authorLabel.setForeground(Color.WHITE);
+ authorLabel.setFont(new Font("Arial", Font.PLAIN, 12));
+ leftMiddlePanel.add(authorLabel, gbc);
+
+ // Right panel with blue gradient and buttons
+ JPanel rightPanel = new JPanel() {
+ @Override
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ Graphics2D g2d = (Graphics2D) g;
+ int width = getWidth();
+ int height = getHeight();
+ Color color1 = new Color(0, 0, 255);
+ Color color2 = new Color(173, 216, 230);
+ GradientPaint gp = new GradientPaint(0, 0, color1, width, height, color2);
+ g2d.setPaint(gp);
+ g2d.fillRect(0, 0, width, height);
+ }
+ };
+ rightPanel.setLayout(new GridBagLayout());
+ rightPanel.setPreferredSize(new Dimension(300, getHeight())); // Set preferred size for the right panel
+ gbc = new GridBagConstraints();
+ gbc.insets = new Insets(10, 10, 10, 10);
+ gbc.gridx = 0;
+ gbc.gridy = 0;
+
+ JButton loginButton = new RoundedButton("Login");
+ JButton registerButton = new RoundedButton("Register");
+
+ // Set the same preferred size for both buttons
+ Dimension buttonSize = new Dimension(150, 40);
+ loginButton.setPreferredSize(buttonSize);
+ registerButton.setPreferredSize(buttonSize);
+
+ rightPanel.add(loginButton, gbc);
+ gbc.gridy++;
+ rightPanel.add(registerButton, gbc);
+
+ add(leftMiddlePanel, BorderLayout.CENTER);
+ add(rightPanel, BorderLayout.EAST);
+
+ // Action listeners for buttons
+ loginButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ mainFrame.showPage("LoginPage");
+ }
+ });
+
+ registerButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ mainFrame.showPage("RegisterPage");
+ }
+ });
+ }
+
+ // Custom button class with rounded edges and stroke
+ private static class RoundedButton extends JButton {
+ public RoundedButton(String text) {
+ super(text);
+ setContentAreaFilled(false);
+ setFocusPainted(false);
+ setOpaque(false);
+ setBorderPainted(false); // Ensure the border is not painted
+ }
+
+ @Override
+ protected void paintComponent(Graphics g) {
+ // Do not call super.paintComponent(g) to avoid painting the background
+ Graphics2D g2 = (Graphics2D) g.create();
+ g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ g2.setColor(getBackground());
+ g2.fillRoundRect(0, 0, getWidth(), getHeight(), 30, 30);
+ g2.dispose();
+ super.paintComponent(g); // Call super to paint the text
+ }
+
+ @Override
+ protected void paintBorder(Graphics g) {
+ Graphics2D g2 = (Graphics2D) g.create();
+ g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ g2.setColor(Color.WHITE);
+ g2.setStroke(new BasicStroke(2));
+ g2.drawRoundRect(0, 0, getWidth() - 1, getHeight() - 1, 30, 30);
+ g2.dispose();
+ }
+
+ @Override
+ public Insets getInsets() {
+ return new Insets(10, 20, 10, 20);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/BsK/client/ui/component/LoginPage/LoginPage.java b/src/main/java/BsK/client/ui/component/LoginPage/LoginPage.java
new file mode 100644
index 0000000..4ad8078
--- /dev/null
+++ b/src/main/java/BsK/client/ui/component/LoginPage/LoginPage.java
@@ -0,0 +1,122 @@
+package BsK.client.ui.component.LoginPage;
+
+import BsK.client.network.handler.ClientHandler;
+import BsK.client.ui.component.MainFrame;
+import BsK.common.packet.Packet;
+import BsK.common.packet.PacketSerializer;
+import BsK.common.packet.req.LoginRequest;
+import BsK.common.packet.res.ErrorResponse;
+import BsK.common.packet.res.LoginSuccessResponse;
+import BsK.common.util.network.NetworkUtil;
+import lombok.extern.slf4j.Slf4j;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+@Slf4j
+public class LoginPage extends JPanel {
+ public LoginPage(MainFrame mainFrame) {
+ setLayout(new BorderLayout());
+
+ // Panel with background image and tint
+ JPanel backgroundPanel = new JPanel() {
+ @Override
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ Graphics2D g2d = (Graphics2D) g;
+ ImageIcon icon = new ImageIcon("src/main/java/BsK/client/ui/image/landingpagebg.jpeg");
+ Image image = icon.getImage();
+ g2d.drawImage(image, 0, 0, getWidth(), getHeight(), this);
+ Color tint = new Color(0, 0, 0, 100); // Black tint with transparency
+ g2d.setColor(tint);
+ g2d.fillRect(0, 0, getWidth(), getHeight());
+ }
+ };
+ backgroundPanel.setLayout(new GridBagLayout());
+ GridBagConstraints gbc = new GridBagConstraints();
+ gbc.insets = new Insets(5, 5, 5, 5);
+ gbc.gridx = 0;
+ gbc.gridy = 0;
+ gbc.gridwidth = 2; // Span across two columns
+
+ JLabel promptLabel = new JLabel("Please enter your details:");
+ promptLabel.setForeground(Color.WHITE);
+ promptLabel.setFont(new Font("Arial", Font.BOLD, 20));
+ backgroundPanel.add(promptLabel, gbc);
+
+ gbc.gridwidth = 1; // Reset to default
+ gbc.gridy++;
+ JLabel usernameLabel = new JLabel("Enter Username:");
+ usernameLabel.setForeground(Color.WHITE);
+ backgroundPanel.add(usernameLabel, gbc);
+
+ gbc.gridx = 1;
+ JTextField loginField = new JTextField(20);
+ backgroundPanel.add(loginField, gbc);
+
+ gbc.gridx = 0;
+ gbc.gridy++;
+ JLabel passwordLabel = new JLabel("Enter Password:");
+ passwordLabel.setForeground(Color.WHITE);
+ backgroundPanel.add(passwordLabel, gbc);
+
+ gbc.gridx = 1;
+ JPasswordField passwordField = new JPasswordField(20);
+ backgroundPanel.add(passwordField, gbc);
+
+ gbc.gridy++;
+ JButton loginButton = new JButton("Login");
+
+ loginButton.addActionListener(e -> {
+ char[] passwordChars = passwordField.getPassword();
+ String password = new String(passwordChars);
+ String username = loginField.getText();
+ var loginRequest = new LoginRequest(username, password);
+ NetworkUtil.sendPacket(ClientHandler.ctx.channel(), loginRequest);
+ });
+
+ // Register listeners for login-related responses
+ ClientHandler.addResponseListener(LoginSuccessResponse.class, response -> {
+ log.info("Login successful, UserId: {}, Role: {}", response.getUserId(), response.getRole());
+ SwingUtilities.invokeLater(() -> mainFrame.showPage("DashboardPage"));
+ });
+
+ ClientHandler.addResponseListener(ErrorResponse.class, response -> {
+ log.error("Login error: {}", response.getError());
+ SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(this,
+ "Login failed: " + response.getError(), "Error", JOptionPane.ERROR_MESSAGE));
+ });
+
+
+ backgroundPanel.add(loginButton, gbc);
+ add(backgroundPanel, BorderLayout.CENTER);
+
+
+ JButton backButton = new JButton("Back");
+ backButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ mainFrame.showPage("LandingPage");
+ }
+ });
+ add(backButton, BorderLayout.SOUTH);
+ }
+
+ @Override
+ public void removeNotify() {
+ super.removeNotify();
+ // Remove listeners to avoid memory leaks
+ ClientHandler.removeResponseListener(LoginSuccessResponse.class, this::handleLoginSuccess);
+ ClientHandler.removeResponseListener(ErrorResponse.class, this::handleErrorResponse);
+ }
+
+ private void handleLoginSuccess(LoginSuccessResponse response) {
+ log.info("Login successful, UserId: {}, Role: {}", response.getUserId(), response.getRole());
+ }
+
+ private void handleErrorResponse(ErrorResponse response) {
+ log.error("Error response: {}", response.getError());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/BsK/client/ui/component/MainFrame.java b/src/main/java/BsK/client/ui/component/MainFrame.java
new file mode 100644
index 0000000..d66d5f6
--- /dev/null
+++ b/src/main/java/BsK/client/ui/component/MainFrame.java
@@ -0,0 +1,44 @@
+package BsK.client.ui.component;
+
+import BsK.client.ui.component.CheckUpPage.CheckUpPage;
+import BsK.client.ui.component.DashboardPage.DashboardPage;
+import BsK.client.ui.component.LandingPage.LandingPage;
+import BsK.client.ui.component.LoginPage.LoginPage;
+import BsK.client.ui.component.RegisterPage.RegisterPage;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class MainFrame extends JFrame {
+ private CardLayout cardLayout;
+ private JPanel mainPanel;
+
+ public MainFrame() {
+ setTitle("BSK");
+ setSize(800, 600);
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ setLocationRelativeTo(null);
+
+ cardLayout = new CardLayout();
+ mainPanel = new JPanel(cardLayout);
+
+ // Add pages to the main panel
+ mainPanel.add(new LandingPage(this), "LandingPage");
+ mainPanel.add(new LoginPage(this), "LoginPage");
+ mainPanel.add(new RegisterPage(this), "RegisterPage");
+ mainPanel.add(new DashboardPage(this), "DashboardPage");
+ mainPanel.add(new CheckUpPage(this), "CheckUpPage");
+
+ add(mainPanel);
+ }
+
+ public void showPage(String pageName) {
+ cardLayout.show(mainPanel, pageName);
+ }
+
+ public static void main(String[] args) {
+ SwingUtilities.invokeLater(() -> {
+ new MainFrame().setVisible(true);
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/BsK/client/ui/component/RegisterPage/RegisterPage.java b/src/main/java/BsK/client/ui/component/RegisterPage/RegisterPage.java
new file mode 100644
index 0000000..d56e0e4
--- /dev/null
+++ b/src/main/java/BsK/client/ui/component/RegisterPage/RegisterPage.java
@@ -0,0 +1,116 @@
+package BsK.client.ui.component.RegisterPage;
+
+import BsK.client.ui.component.MainFrame;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+public class RegisterPage extends JPanel {
+ public RegisterPage(MainFrame mainFrame) {
+ setLayout(new BorderLayout());
+
+ // Panel with background image and tint
+ JPanel backgroundPanel = new JPanel() {
+ @Override
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ Graphics2D g2d = (Graphics2D) g;
+ ImageIcon icon = new ImageIcon("src/main/java/BsK/client/ui/image/landingpagebg.jpeg");
+ Image image = icon.getImage();
+ g2d.drawImage(image, 0, 0, getWidth(), getHeight(), this);
+ Color tint = new Color(0, 0, 0, 100); // Black tint with transparency
+ g2d.setColor(tint);
+ g2d.fillRect(0, 0, getWidth(), getHeight());
+ }
+ };
+ backgroundPanel.setLayout(new GridBagLayout());
+ GridBagConstraints gbc = new GridBagConstraints();
+ gbc.insets = new Insets(10, 10, 10, 10);
+ gbc.gridx = 0;
+ gbc.gridy = 0;
+ gbc.anchor = GridBagConstraints.WEST;
+
+ JLabel promptLabel = new JLabel("Please enter your details:");
+ promptLabel.setForeground(Color.WHITE);
+ promptLabel.setFont(new Font("Arial", Font.BOLD, 20));
+ gbc.gridwidth = 2;
+ backgroundPanel.add(promptLabel, gbc);
+
+ gbc.gridwidth = 1;
+ gbc.gridy++;
+ JLabel usernameLabel = new JLabel("Username:");
+ usernameLabel.setForeground(Color.WHITE);
+ backgroundPanel.add(usernameLabel, gbc);
+
+ gbc.gridx = 1;
+ JTextField usernameField = new JTextField(20);
+ backgroundPanel.add(usernameField, gbc);
+
+ gbc.gridx = 0;
+ gbc.gridy++;
+ JLabel nameLabel = new JLabel("Name:");
+ nameLabel.setForeground(Color.WHITE);
+ backgroundPanel.add(nameLabel, gbc);
+
+ gbc.gridx = 1;
+ JTextField nameField = new JTextField(20);
+ backgroundPanel.add(nameField, gbc);
+
+ gbc.gridx = 0;
+ gbc.gridy++;
+ JLabel passwordLabel = new JLabel("Password:");
+ passwordLabel.setForeground(Color.WHITE);
+ backgroundPanel.add(passwordLabel, gbc);
+
+ gbc.gridx = 1;
+ JPasswordField passwordField = new JPasswordField(20);
+ backgroundPanel.add(passwordField, gbc);
+
+ gbc.gridx = 0;
+ gbc.gridy++;
+ JLabel rePasswordLabel = new JLabel("Re-enter your password:");
+ rePasswordLabel.setForeground(Color.WHITE);
+ backgroundPanel.add(rePasswordLabel, gbc);
+
+ gbc.gridx = 1;
+ JPasswordField rePasswordField = new JPasswordField(20);
+ backgroundPanel.add(rePasswordField, gbc);
+
+ gbc.gridx = 0;
+ gbc.gridy++;
+ JLabel roleLabel = new JLabel("Role:");
+ roleLabel.setForeground(Color.WHITE);
+ backgroundPanel.add(roleLabel, gbc);
+
+ gbc.gridx = 1;
+ String[] roles = {"user", "admin", "doctor", "nurse"};
+ JComboBox roleComboBox = new JComboBox<>(roles);
+ backgroundPanel.add(roleComboBox, gbc);
+
+ gbc.gridy++;
+ JButton loginButton = new JButton("Register");
+ loginButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ mainFrame.showPage("LandingPage");
+ }
+ });
+ backgroundPanel.add(loginButton, gbc);
+ add(backgroundPanel, BorderLayout.CENTER);
+
+ // Back button to return to the landing page
+ JButton backButton = new JButton("Back");
+ backButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ mainFrame.showPage("LandingPage");
+ }
+ });
+ add(backButton, BorderLayout.SOUTH);
+ }
+ public static void main(String[] args) {
+ System.out.println("Working Directory = " + System.getProperty("user.dir"));
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/BsK/client/ui/component/common/RoundedButtonUI.java b/src/main/java/BsK/client/ui/component/common/RoundedButtonUI.java
new file mode 100644
index 0000000..56c3dfc
--- /dev/null
+++ b/src/main/java/BsK/client/ui/component/common/RoundedButtonUI.java
@@ -0,0 +1,39 @@
+package BsK.client.ui.component.common;
+
+import javax.swing.*;
+import javax.swing.plaf.basic.BasicButtonUI;
+import java.awt.*;
+
+public class RoundedButtonUI extends BasicButtonUI {
+ private final int cornerRadius;
+
+ public RoundedButtonUI(int cornerRadius) {
+ this.cornerRadius = cornerRadius;
+ }
+
+ @Override
+ public void installUI(JComponent c) {
+ super.installUI(c);
+ JButton button = (JButton) c;
+ button.setOpaque(false);
+ button.setBorder(BorderFactory.createEmptyBorder(5, 15, 5, 15));
+ }
+
+ @Override
+ public void paint(Graphics g, JComponent c) {
+ JButton button = (JButton) c;
+ Graphics2D g2 = (Graphics2D) g.create();
+ g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ int width = button.getWidth();
+ int height = button.getHeight();
+ g2.setColor(button.getBackground());
+ g2.fillRoundRect(0, 0, width, height, cornerRadius, cornerRadius);
+ g2.setColor(button.getForeground());
+ g2.setFont(button.getFont());
+ FontMetrics fm = g2.getFontMetrics();
+ int stringWidth = fm.stringWidth(button.getText());
+ int stringHeight = fm.getAscent();
+ g2.drawString(button.getText(), (width - stringWidth) / 2, (height + stringHeight) / 2 - 2);
+ g2.dispose();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/BsK/client/ui/component/common/RoundedPanel.java b/src/main/java/BsK/client/ui/component/common/RoundedPanel.java
new file mode 100644
index 0000000..9cca1cc
--- /dev/null
+++ b/src/main/java/BsK/client/ui/component/common/RoundedPanel.java
@@ -0,0 +1,43 @@
+package BsK.client.ui.component.common;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class RoundedPanel extends JPanel {
+ private int cornerRadius;
+ private Color fillColor;
+ private boolean gradient;
+
+ public RoundedPanel(int radius, Color fillColor, boolean gradient) {
+ super();
+ this.cornerRadius = radius;
+ this.fillColor = fillColor;
+ this.gradient = gradient;
+ setOpaque(false); // Ensures background is transparent outside the rounded area
+ }
+
+ @Override
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ int width = getWidth();
+ int height = getHeight();
+ Graphics2D graphics = (Graphics2D) g;
+ graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+
+ // Create a gradient paint from white to the fillColor
+ GradientPaint gradientColor = new GradientPaint(
+ -100, -100, Color.WHITE, width, height, fillColor
+ );
+
+ // Apply the gradient as the fill paint
+ if(gradient)
+ graphics.setPaint(gradientColor);
+ else
+ graphics.setColor(fillColor);
+ graphics.fillRoundRect(0, 0, width, height, cornerRadius, cornerRadius);
+
+ // Draw the rounded border
+ // graphics.setColor(getForeground());
+ // graphics.drawRoundRect(0, 0, width - 1, height - 1, cornerRadius, cornerRadius);
+ }
+}
diff --git a/src/main/java/BsK/client/ui/handler/UIHandler.java b/src/main/java/BsK/client/ui/handler/UIHandler.java
index f9693f1..e62441c 100644
--- a/src/main/java/BsK/client/ui/handler/UIHandler.java
+++ b/src/main/java/BsK/client/ui/handler/UIHandler.java
@@ -1,6 +1,7 @@
package BsK.client.ui.handler;
import BsK.client.network.handler.ClientHandler;
+import BsK.client.ui.component.MainFrame;
import BsK.common.packet.Packet;
import BsK.common.packet.req.LoginRequest;
import BsK.common.packet.res.HandshakeCompleteResponse;
@@ -31,35 +32,9 @@ public class UIHandler {
}
public void showUI() {
- JTextField usernameField = new JTextField();
- JPasswordField passwordField = new JPasswordField();
- Object[] message = {
- "Username:", usernameField,
- "Password:", passwordField
- };
-
- int option = JOptionPane.showConfirmDialog(null, message, "Login", JOptionPane.OK_CANCEL_OPTION);
- if (option == JOptionPane.OK_OPTION) {
- String username = usernameField.getText();
- String password = new String(passwordField.getPassword());
- System.out.println("Username: " + username);
- System.out.println("Password: " + password);
- var login = new LoginRequest(username, password);
- NetworkUtil.sendPacket(ClientHandler.ctx.channel(), login);
- } else {
- System.out.println("Login canceled");
- }
+ SwingUtilities.invokeLater(() -> {
+ new MainFrame().setVisible(true);
+ });
}
-// public void onClickButtonLogin() {
-// if (!readyToLogin) {
-// // TODO: hiển thị lỗi "Chưa kết nối tới server"
-// return;
-// }
-// var username = "username"; // Đọc từ đâu đó
-// var password = "password"; // Đọc từ đâu đó
-//
-// NetworkUtil.sendPacket(ctx.channel(), login);
-// NetworkUtil.sendPacket(loginRequest);
-// }
}
\ No newline at end of file
diff --git a/src/main/java/BsK/client/ui/icon/cont.png b/src/main/java/BsK/client/ui/icon/cont.png
new file mode 100644
index 0000000..dcd8650
Binary files /dev/null and b/src/main/java/BsK/client/ui/icon/cont.png differ
diff --git a/src/main/java/BsK/client/ui/icon/data.png b/src/main/java/BsK/client/ui/icon/data.png
new file mode 100644
index 0000000..1411c8f
Binary files /dev/null and b/src/main/java/BsK/client/ui/icon/data.png differ
diff --git a/src/main/java/BsK/client/ui/icon/exit.png b/src/main/java/BsK/client/ui/icon/exit.png
new file mode 100644
index 0000000..29ebdab
Binary files /dev/null and b/src/main/java/BsK/client/ui/icon/exit.png differ
diff --git a/src/main/java/BsK/client/ui/icon/expense.png b/src/main/java/BsK/client/ui/icon/expense.png
new file mode 100644
index 0000000..94f8901
Binary files /dev/null and b/src/main/java/BsK/client/ui/icon/expense.png differ
diff --git a/src/main/java/BsK/client/ui/icon/income.png b/src/main/java/BsK/client/ui/icon/income.png
new file mode 100644
index 0000000..7876b1b
Binary files /dev/null and b/src/main/java/BsK/client/ui/icon/income.png differ
diff --git a/src/main/java/BsK/client/ui/icon/logo.jpg b/src/main/java/BsK/client/ui/icon/logo.jpg
new file mode 100644
index 0000000..c98dcc4
Binary files /dev/null and b/src/main/java/BsK/client/ui/icon/logo.jpg differ
diff --git a/src/main/java/BsK/client/ui/icon/report.png b/src/main/java/BsK/client/ui/icon/report.png
new file mode 100644
index 0000000..cf74a58
Binary files /dev/null and b/src/main/java/BsK/client/ui/icon/report.png differ
diff --git a/src/main/java/BsK/client/ui/icon/user.png b/src/main/java/BsK/client/ui/icon/user.png
new file mode 100644
index 0000000..f829b46
Binary files /dev/null and b/src/main/java/BsK/client/ui/icon/user.png differ
diff --git a/src/main/java/BsK/client/ui/image/landingpagebg.jpeg b/src/main/java/BsK/client/ui/image/landingpagebg.jpeg
new file mode 100644
index 0000000..038dc80
Binary files /dev/null and b/src/main/java/BsK/client/ui/image/landingpagebg.jpeg differ
diff --git a/src/main/java/BsK/common/packet/req/LoginRequest.java b/src/main/java/BsK/common/packet/req/LoginRequest.java
index d07c64d..58c35a2 100644
--- a/src/main/java/BsK/common/packet/req/LoginRequest.java
+++ b/src/main/java/BsK/common/packet/req/LoginRequest.java
@@ -8,6 +8,7 @@ import java.time.LocalDateTime;
@AllArgsConstructor
@Data
+
public class LoginRequest implements Packet {
String username;
String password;
diff --git a/src/main/java/BsK/common/packet/res/ErrorResponse.java b/src/main/java/BsK/common/packet/res/ErrorResponse.java
index d1249cc..db58104 100644
--- a/src/main/java/BsK/common/packet/res/ErrorResponse.java
+++ b/src/main/java/BsK/common/packet/res/ErrorResponse.java
@@ -10,7 +10,11 @@ public class ErrorResponse implements Packet {
Error error;
- public enum Error {
+ public String getError() {
+ return error.toString();
+ }
+
+ public enum Error {
SUCCESS,
UNKNOWN,
USERNAME_NOT_FOUND,
diff --git a/src/main/java/BsK/server/Server.java b/src/main/java/BsK/server/Server.java
index d42260d..53cb4c8 100644
--- a/src/main/java/BsK/server/Server.java
+++ b/src/main/java/BsK/server/Server.java
@@ -17,48 +17,74 @@ import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import java.net.InetSocketAddress;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Statement;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Server {
private static final int PORT = 1999;
+ static Connection connection;
- public static void main(String[] args) throws InterruptedException {
- EventLoopGroup parentGroup =
- Epoll.isAvailable() ? new EpollEventLoopGroup() : new NioEventLoopGroup();
- EventLoopGroup childGroup =
- Epoll.isAvailable() ? new EpollEventLoopGroup() : new NioEventLoopGroup();
- try {
- ServerBootstrap bootstrap =
- new ServerBootstrap()
- .group(parentGroup, childGroup)
- .channel(
- Epoll.isAvailable()
- ? EpollServerSocketChannel.class
- : NioServerSocketChannel.class)
- .localAddress(new InetSocketAddress(PORT))
- .childHandler(
- new ChannelInitializer() {
- @Override
- protected void initChannel(SocketChannel ch) throws Exception {
- ch.pipeline()
- .addLast(new IdleStateHandler(60 * 30, 0, 0, TimeUnit.SECONDS))
- .addLast(new HttpServerCodec())
- .addLast(new ChunkedWriteHandler())
- .addLast(new HttpObjectAggregator(8192))
- .addLast("ws", new WebSocketServerProtocolHandler("/"))
- .addLast(new ServerHandler());
- }
- });
+ static {
+ try {
+ connection = DriverManager.getConnection("jdbc:sqlite:src/main/resources/database/BSK.db");
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
- ChannelFuture f = bootstrap.bind().sync();
+ public static Statement statement;
- log.info("Server started on port {}", PORT);
- f.channel().closeFuture().sync();
- } finally {
- parentGroup.shutdownGracefully();
- childGroup.shutdownGracefully();
+ static {
+ try {
+ statement = connection.createStatement();
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public Server() throws SQLException {
+ }
+
+ public static void main(String[] args) throws InterruptedException, SQLException {
+ EventLoopGroup parentGroup =
+ Epoll.isAvailable() ? new EpollEventLoopGroup() : new NioEventLoopGroup();
+ EventLoopGroup childGroup =
+ Epoll.isAvailable() ? new EpollEventLoopGroup() : new NioEventLoopGroup();
+ try {
+ ServerBootstrap bootstrap =
+ new ServerBootstrap()
+ .group(parentGroup, childGroup)
+ .channel(
+ Epoll.isAvailable()
+ ? EpollServerSocketChannel.class
+ : NioServerSocketChannel.class)
+ .localAddress(new InetSocketAddress(PORT))
+ .childHandler(
+ new ChannelInitializer() {
+ @Override
+ protected void initChannel(SocketChannel ch) throws Exception {
+ ch.pipeline()
+ .addLast(new IdleStateHandler(60 * 30, 0, 0, TimeUnit.SECONDS))
+ .addLast(new HttpServerCodec())
+ .addLast(new ChunkedWriteHandler())
+ .addLast(new HttpObjectAggregator(8192))
+ .addLast("ws", new WebSocketServerProtocolHandler("/"))
+ .addLast(new ServerHandler());
+ }
+ });
+
+ ChannelFuture f = bootstrap.bind().sync();
+
+ log.info("Server started on port {}", PORT);
+ f.channel().closeFuture().sync();
+ } finally {
+ parentGroup.shutdownGracefully();
+ childGroup.shutdownGracefully();
+ }
+ }
}
- }
-}
diff --git a/src/main/java/BsK/server/network/entity/User.java b/src/main/java/BsK/server/network/entity/User.java
index 696204c..ab64037 100644
--- a/src/main/java/BsK/server/network/entity/User.java
+++ b/src/main/java/BsK/server/network/entity/User.java
@@ -1,26 +1,50 @@
package BsK.server.network.entity;
+import BsK.server.network.manager.SessionManager;
import io.netty.channel.Channel;
import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import static BsK.server.Server.statement;
+
+@Slf4j
@Data
public class User {
- int id;
+ int userId;
Channel channel;
- Role role = Role.GUEST;
+ Role role;
- public User(int id, Channel channel) {
- this.id = id;
+ int sessionId;
+
+ public User(Channel channel, int sessionId) {
this.channel = channel;
+ this.sessionId = sessionId;
+ Role role = Role.GUEST;
}
public void authenticate(String username, String password) {
- if (username.equals("admin") && password.equals("admin")) {
- role = Role.ADMIN;
- } else if (username.equals("user") && password.equals("user")) {
- role = Role.USER;
- }
+ try {
+ ResultSet rs = statement.executeQuery("select * from User where user_name = '" + username + "' and password = '" + password + "'");
+ if (!rs.isBeforeFirst()) {
+ System.out.println("No data found in the User table.");
+ } else {
+ while(rs.next())
+ {
+ log.info(rs.getString("name") + " logged in");
+ role = Role.valueOf(rs.getString("role_name"));
+ userId = rs.getInt("user_id");
+ }
+
+ }
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+
+
}
public boolean isAuthenticated() {
diff --git a/src/main/java/BsK/server/network/handler/ServerHandler.java b/src/main/java/BsK/server/network/handler/ServerHandler.java
index 2365472..43a6810 100644
--- a/src/main/java/BsK/server/network/handler/ServerHandler.java
+++ b/src/main/java/BsK/server/network/handler/ServerHandler.java
@@ -13,7 +13,7 @@ import BsK.common.packet.res.LoginSuccessResponse;
import BsK.common.util.network.NetworkUtil;
import BsK.server.database.entity.Patient;
import BsK.server.database.entity.Patient.Sex;
-import BsK.server.network.manager.UserManager;
+import BsK.server.network.manager.SessionManager;
import BsK.server.network.util.UserUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
@@ -32,17 +32,19 @@ public class ServerHandler extends SimpleChannelInboundHandler {
var patientInfo =
new Patient(getPatientInfoReq.getPatientId(), new Date(1999, 01, 01), "test",
Sex.MALE);
var response = new GetPatientInfoRes(patientInfo);
- UserUtil.sendPacket(user.getId(), response);
+ UserUtil.sendPacket(user.getUserId(), response);
}
case null, default -> log.warn("Received unknown packet: {}", packet);
}
@@ -79,7 +82,7 @@ public class ServerHandler extends SimpleChannelInboundHandler SESSION_ID_MAP = new ConcurrentSkipListMap<>();
+
+ private static final Map CHANNEL_SESSION_ID_MAP = new ConcurrentHashMap<>();
+
+ private static final Map CHANNEL_TO_USER_MAP = new ConcurrentHashMap<>();
+
+ private static final AtomicInteger SESSION_ID = new AtomicInteger(0);
+
+
+ public static User getUserByChannel(String channel) {
+ return CHANNEL_TO_USER_MAP.get(channel);
+ }
+
+ public static int getSessionByChannelId(String channelId) {
+ return CHANNEL_SESSION_ID_MAP.get(channelId);
+ }
+
+ public static String getChannelBySessionId(int sessionId) {
+ return SESSION_ID_MAP.get(sessionId);
+ }
+
+ public static int onUserLogin(Channel channel) {
+ int sessionId = SESSION_ID.incrementAndGet();
+ var user = new User(channel, sessionId);
+ SESSION_ID_MAP.put(sessionId, channel.id().asLongText());
+ CHANNEL_SESSION_ID_MAP.put(channel.id().asLongText(), sessionId);
+ CHANNEL_TO_USER_MAP.put(channel.id().asLongText(), user);
+ return sessionId;
+ }
+
+ public static void onUserDisconnect(Channel channel) {
+ int sessionId = CHANNEL_SESSION_ID_MAP.remove(channel.id().asLongText());
+ SESSION_ID_MAP.remove(sessionId);
+ }
+}
diff --git a/src/main/java/BsK/server/network/manager/UserManager.java b/src/main/java/BsK/server/network/manager/UserManager.java
deleted file mode 100644
index 0ac28ea..0000000
--- a/src/main/java/BsK/server/network/manager/UserManager.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package BsK.server.network.manager;
-
-import BsK.server.network.entity.User;
-import io.netty.channel.Channel;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentSkipListMap;
-import java.util.concurrent.atomic.AtomicInteger;
-import lombok.AccessLevel;
-import lombok.NoArgsConstructor;
-
-@NoArgsConstructor(access = AccessLevel.PRIVATE)
-public class UserManager {
- private static final Map USER_MAP = new ConcurrentSkipListMap<>();
-
- private static final Map CHANNEL_ID_MAP = new ConcurrentHashMap<>();
-
- private static final AtomicInteger USER_ID = new AtomicInteger(0);
-
- public static User getUserById(int userId) {
- return USER_MAP.get(userId);
- }
-
- public static User getUserByChannelId(String channelId) {
- return USER_MAP.get(CHANNEL_ID_MAP.get(channelId));
- }
-
- public static User getUserByChannel(Channel channel) {
- return getUserByChannelId(channel.id().asLongText());
- }
-
- public static int onUserLogin(Channel channel) {
- int userId = USER_ID.incrementAndGet();
- var user = new User(userId, channel);
- USER_MAP.put(userId, user);
- CHANNEL_ID_MAP.put(channel.id().asLongText(), userId);
- return userId;
- }
-
- public static void onUserDisconnect(Channel channel) {
- USER_MAP.remove(CHANNEL_ID_MAP.remove(channel.id().asLongText()));
- }
-}
diff --git a/src/main/java/BsK/server/network/util/UserUtil.java b/src/main/java/BsK/server/network/util/UserUtil.java
index ba80a84..3e0a9df 100644
--- a/src/main/java/BsK/server/network/util/UserUtil.java
+++ b/src/main/java/BsK/server/network/util/UserUtil.java
@@ -2,14 +2,16 @@ package BsK.server.network.util;
import BsK.common.packet.Packet;
import BsK.common.util.network.NetworkUtil;
-import BsK.server.network.manager.UserManager;
+import BsK.server.network.manager.SessionManager;
+import io.netty.channel.Channel;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class UserUtil {
- public static void sendPacket(int userId, Packet packet) {
- var user = UserManager.getUserById(userId);
+ public static void sendPacket(int sessionId, Packet packet) {
+ String channel = SessionManager.getChannelBySessionId(sessionId);
+ var user = SessionManager.getUserByChannel(channel);
if (user == null) return;
NetworkUtil.sendPacket(user.getChannel(), packet);
diff --git a/src/main/java/BsK/testing/Sample.java b/src/main/java/BsK/testing/Sample.java
new file mode 100644
index 0000000..093a04d
--- /dev/null
+++ b/src/main/java/BsK/testing/Sample.java
@@ -0,0 +1,45 @@
+package BsK.testing;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+public class Sample
+{
+ public static void main(String[] args)
+ {
+ // NOTE: Connection and Statement are AutoCloseable.
+ // Don't forget to close them both in order to avoid leaks.
+ try
+ (
+ // create a database connection
+ Connection connection = DriverManager.getConnection("jdbc:sqlite:src/main/resources/database/BSK.db");
+ Statement statement = connection.createStatement();
+ )
+ {
+ System.out.println("Connection established successfully.");
+
+ statement.setQueryTimeout(30); // set timeout to 30 sec.
+
+ ResultSet rs = statement.executeQuery("select * from User");
+ if (!rs.isBeforeFirst()) {
+ System.out.println("No data found in the User table.");
+ } else {
+ while(rs.next())
+ {
+ // read the result set
+ System.out.println("name = " + rs.getString("name"));
+ System.out.println("id = " + rs.getInt("user_id"));
+ }
+ }
+ }
+ catch(SQLException e)
+ {
+ // if the error message is "out of memory",
+ // it probably means no database file is found
+ e.printStackTrace(System.err);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/database/BSK.db b/src/main/resources/database/BSK.db
new file mode 100644
index 0000000..b1e2f19
Binary files /dev/null and b/src/main/resources/database/BSK.db differ
diff --git "a/src\\database\\BSK.db" "b/src\\database\\BSK.db"
new file mode 100644
index 0000000..e69de29