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